mirror of
https://github.com/LanZhan-Harmony/WindowsMusicPlayer-TheUntamedMusicPlayer.git
synced 2026-05-06 19:20:18 +08:00
更新ShellPage
This commit is contained in:
@@ -25,7 +25,7 @@ public sealed partial class MainWindow : WindowEx, IRecipient<LogMessage>
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
AppWindow.SetIcon(Path.Combine(AppContext.BaseDirectory, "Assets/AppIcon/WindowIcon.ico"));
|
||||
AppWindow.SetIcon(Path.Combine(AppContext.BaseDirectory, "Assets/AppIcon/Icon.png"));
|
||||
Title = "AppDisplayName".GetLocalized();
|
||||
ExtendsContentIntoTitleBar = true;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Diagnostics;
|
||||
using MemoryPack;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using The_Untamed_Music_Player.Contracts.Models;
|
||||
using The_Untamed_Music_Player.Helpers;
|
||||
@@ -136,33 +137,51 @@ public partial class LocalAlbumInfo : IAlbumInfoBase
|
||||
return string.Join(" • ", parts);
|
||||
}
|
||||
|
||||
public void LoadCover()
|
||||
public void InitializeCover()
|
||||
{
|
||||
if (string.IsNullOrEmpty(CoverPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
Cover = new BitmapImage { DecodePixelWidth = 160 };
|
||||
}
|
||||
catch
|
||||
{
|
||||
Debug.WriteLine($"专辑封面初始化失败:{Name}");
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadCover()
|
||||
{
|
||||
if (Cover is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
using var musicFile = TagLib.File.Create(CoverPath);
|
||||
var coverBuffer = musicFile.Tag.Pictures[0].Data.Data;
|
||||
var stream = new MemoryStream(coverBuffer);
|
||||
App.MainWindow?.DispatcherQueue.TryEnqueue(async () =>
|
||||
{
|
||||
try
|
||||
App.MainWindow?.DispatcherQueue.TryEnqueue(
|
||||
DispatcherQueuePriority.Low,
|
||||
async () =>
|
||||
{
|
||||
Cover = new BitmapImage { DecodePixelWidth = 160 };
|
||||
await Cover.SetSourceAsync(stream.AsRandomAccessStream());
|
||||
try
|
||||
{
|
||||
await Cover.SetSourceAsync(stream.AsRandomAccessStream());
|
||||
}
|
||||
catch
|
||||
{
|
||||
Debug.WriteLine($"专辑封面加载失败:{Name}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
stream.Dispose();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
Debug.WriteLine($"专辑封面加载失败:{Name}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
stream.Dispose();
|
||||
}
|
||||
});
|
||||
);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -123,7 +123,7 @@ public partial class MusicLibrary : ObservableRecipient
|
||||
Artists = libraryData.Artists;
|
||||
Genres = libraryData.Genres;
|
||||
_musicFolders = libraryData.MusicFolders;
|
||||
LoadCoverAsync();
|
||||
await InitializeCovers();
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
Messenger.Send(new HaveMusicMessage(!Songs.IsEmpty))
|
||||
);
|
||||
@@ -146,7 +146,7 @@ public partial class MusicLibrary : ObservableRecipient
|
||||
.Keys.Concat(["SongInfo_AllGenres".GetLocalized()])
|
||||
.OrderBy(x => x, new GenreComparer()),
|
||||
];
|
||||
LoadCoverAsync();
|
||||
await InitializeCovers();
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
Messenger.Send(new HaveMusicMessage(!Songs.IsEmpty))
|
||||
);
|
||||
@@ -162,6 +162,7 @@ public partial class MusicLibrary : ObservableRecipient
|
||||
}
|
||||
finally
|
||||
{
|
||||
_ = Task.Run(LoadCovers);
|
||||
_ = Task.Run(AddFolderWatcher);
|
||||
_librarySemaphore.Release();
|
||||
GC.Collect();
|
||||
@@ -191,7 +192,7 @@ public partial class MusicLibrary : ObservableRecipient
|
||||
}
|
||||
}
|
||||
await Task.WhenAll(loadMusicTasks);
|
||||
LoadCoverAsync();
|
||||
await InitializeCovers();
|
||||
Genres =
|
||||
[
|
||||
.. _musicGenres
|
||||
@@ -204,6 +205,7 @@ public partial class MusicLibrary : ObservableRecipient
|
||||
_musicGenres.Clear();
|
||||
FolderWatchers.Clear();
|
||||
var data = new MusicLibraryData(Songs, Albums, Artists, Genres, _musicFolders);
|
||||
_ = Task.Run(LoadCovers);
|
||||
_ = Task.Run(AddFolderWatcher);
|
||||
FileManager.SaveLibraryDataAsync(Folders, data);
|
||||
}
|
||||
@@ -220,11 +222,25 @@ public partial class MusicLibrary : ObservableRecipient
|
||||
});
|
||||
}
|
||||
|
||||
public void LoadCoverAsync()
|
||||
public Task<bool> InitializeCovers()
|
||||
{
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
foreach (var album in Albums.Values)
|
||||
{
|
||||
album.InitializeCover();
|
||||
}
|
||||
tcs.SetResult(true);
|
||||
});
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
public void LoadCovers()
|
||||
{
|
||||
foreach (var album in Albums.Values)
|
||||
{
|
||||
album.LoadCover(); // 使用同步的懒加载方法
|
||||
album.LoadCover();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using MemoryPack;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using The_Untamed_Music_Player.Contracts.Models;
|
||||
using The_Untamed_Music_Player.Helpers;
|
||||
@@ -55,6 +56,7 @@ public partial class PlaylistInfo
|
||||
|
||||
if (coverPathIndex >= 0)
|
||||
{
|
||||
InitializeCover();
|
||||
GetCover();
|
||||
}
|
||||
}
|
||||
@@ -80,6 +82,7 @@ public partial class PlaylistInfo
|
||||
ModifiedDate = new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds();
|
||||
if (coverUpdated)
|
||||
{
|
||||
InitializeCover();
|
||||
GetCover();
|
||||
}
|
||||
}
|
||||
@@ -120,6 +123,7 @@ public partial class PlaylistInfo
|
||||
{
|
||||
await RefillCoverPaths();
|
||||
}
|
||||
InitializeCover();
|
||||
GetCover();
|
||||
}
|
||||
}
|
||||
@@ -225,36 +229,35 @@ public partial class PlaylistInfo
|
||||
}
|
||||
}
|
||||
|
||||
public void GetCover()
|
||||
public void InitializeCover()
|
||||
{
|
||||
if (CoverPaths.Count == 0)
|
||||
{
|
||||
Cover = null;
|
||||
return;
|
||||
}
|
||||
else if (Cover is null)
|
||||
{
|
||||
const int canvasSize = 256;
|
||||
Cover = new WriteableBitmap(canvasSize, canvasSize);
|
||||
}
|
||||
}
|
||||
|
||||
App.MainWindow?.DispatcherQueue.TryEnqueue(async () =>
|
||||
public async void GetCover()
|
||||
{
|
||||
if (Cover is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// 创建一个256x256的WriteableBitmap作为画布
|
||||
const int canvasSize = 256;
|
||||
const int halfSize = canvasSize / 2;
|
||||
|
||||
Cover = new WriteableBitmap(canvasSize, canvasSize);
|
||||
var buffer = Cover.PixelBuffer;
|
||||
var pixels = new byte[buffer.Length];
|
||||
|
||||
// 初始化为透明
|
||||
Array.Fill(pixels, (byte)0);
|
||||
|
||||
// 定义四个区域的位置 (x, y, width, height)
|
||||
var regions = new[]
|
||||
{
|
||||
new PictureRegion(0, 0, halfSize, halfSize), // 左上
|
||||
new PictureRegion(halfSize, 0, halfSize, halfSize), // 右上
|
||||
new PictureRegion(0, halfSize, halfSize, halfSize), // 左下
|
||||
new PictureRegion(halfSize, halfSize, halfSize, halfSize), // 右下
|
||||
};
|
||||
// 在后台线程中处理所有图像数据
|
||||
var processedRegions = new List<(byte[] pixels, int regionIndex)>();
|
||||
|
||||
for (var i = 0; i < CoverPaths.Count; i++)
|
||||
{
|
||||
@@ -263,13 +266,13 @@ public partial class PlaylistInfo
|
||||
{
|
||||
byte[]? imageBytes = null;
|
||||
|
||||
// 获取图片字节数据
|
||||
if (coverPath.StartsWith("http", StringComparison.OrdinalIgnoreCase)) // 网络图片
|
||||
if (coverPath.StartsWith("http", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
using var httpClient = new HttpClient();
|
||||
httpClient.Timeout = TimeSpan.FromSeconds(5); // 设置超时
|
||||
imageBytes = await httpClient.GetByteArrayAsync(coverPath);
|
||||
}
|
||||
else if (File.Exists(coverPath)) // 本地音乐文件
|
||||
else if (File.Exists(coverPath))
|
||||
{
|
||||
using var musicFile = TagLib.File.Create(coverPath);
|
||||
if (musicFile.Tag.Pictures.Length > 0)
|
||||
@@ -283,19 +286,20 @@ public partial class PlaylistInfo
|
||||
continue;
|
||||
}
|
||||
|
||||
// 加载并调整图片大小
|
||||
// 处理图像并立即释放原始数据
|
||||
var resizedImageBytes = await ResizeImageToFitRegionAsync(
|
||||
imageBytes,
|
||||
halfSize,
|
||||
halfSize
|
||||
);
|
||||
if (resizedImageBytes is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 将调整后的图片绘制到对应区域
|
||||
DrawImageToRegion(pixels, resizedImageBytes, regions[i], canvasSize);
|
||||
// 释放原始图像数据
|
||||
imageBytes = null;
|
||||
|
||||
if (resizedImageBytes is not null)
|
||||
{
|
||||
processedRegions.Add((resizedImageBytes, i));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -303,14 +307,57 @@ public partial class PlaylistInfo
|
||||
}
|
||||
}
|
||||
|
||||
// 将像素数据写入WriteableBitmap
|
||||
using (var pixelStream = buffer.AsStream())
|
||||
{
|
||||
pixelStream.Write(pixels, 0, pixels.Length);
|
||||
}
|
||||
// 切换回UI线程进行最终的绘制
|
||||
App.MainWindow?.DispatcherQueue.TryEnqueue(
|
||||
DispatcherQueuePriority.Low,
|
||||
async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var buffer = Cover.PixelBuffer;
|
||||
var pixels = new byte[buffer.Length];
|
||||
|
||||
// 使WriteableBitmap失效以更新显示
|
||||
Cover.Invalidate();
|
||||
// 初始化为透明
|
||||
Array.Fill(pixels, (byte)0);
|
||||
|
||||
// 定义四个区域的位置
|
||||
var regions = new[]
|
||||
{
|
||||
new PictureRegion(0, 0, halfSize, halfSize), // 左上
|
||||
new PictureRegion(halfSize, 0, halfSize, halfSize), // 右上
|
||||
new PictureRegion(0, halfSize, halfSize, halfSize), // 左下
|
||||
new PictureRegion(halfSize, halfSize, halfSize, halfSize), // 右下
|
||||
};
|
||||
|
||||
// 绘制所有处理好的区域
|
||||
foreach (var (regionPixels, regionIndex) in processedRegions)
|
||||
{
|
||||
if (regionIndex < regions.Length)
|
||||
{
|
||||
DrawImageToRegion(
|
||||
pixels,
|
||||
regionPixels,
|
||||
regions[regionIndex],
|
||||
canvasSize
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 将像素数据写入WriteableBitmap
|
||||
using (var pixelStream = buffer.AsStream())
|
||||
{
|
||||
await pixelStream.WriteAsync(pixels);
|
||||
}
|
||||
|
||||
// 使WriteableBitmap失效以更新显示
|
||||
Cover.Invalidate();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"UI线程绘制失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using The_Untamed_Music_Player.Contracts.Models;
|
||||
@@ -7,11 +9,12 @@ using ZLinq;
|
||||
|
||||
namespace The_Untamed_Music_Player.Models;
|
||||
|
||||
public class PlaylistLibrary
|
||||
public partial class PlaylistLibrary : ObservableRecipient
|
||||
{
|
||||
public List<PlaylistInfo> Playlists { get; set; } = [];
|
||||
|
||||
public PlaylistLibrary()
|
||||
: base(StrongReferenceMessenger.Default)
|
||||
{
|
||||
_ = LoadLibraryAsync();
|
||||
}
|
||||
@@ -19,11 +22,17 @@ public class PlaylistLibrary
|
||||
public async Task LoadLibraryAsync()
|
||||
{
|
||||
Playlists = await FileManager.LoadPlaylistDataAsync();
|
||||
foreach (var playlist in Playlists)
|
||||
{
|
||||
playlist.InitializeCover();
|
||||
}
|
||||
Messenger.Send(new HavePlaylistMessage(Playlists.Count > 0));
|
||||
|
||||
foreach (var playlist in Playlists)
|
||||
{
|
||||
playlist.GetCover();
|
||||
}
|
||||
StrongReferenceMessenger.Default.Send(new HavePlaylistMessage(Playlists.Count > 0));
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
public PlaylistInfo? NewPlaylist(string? name)
|
||||
@@ -35,8 +44,9 @@ public class PlaylistLibrary
|
||||
var uniqueName = GetUniquePlaylistName(name);
|
||||
var info = new PlaylistInfo(uniqueName);
|
||||
Playlists.Add(info);
|
||||
StrongReferenceMessenger.Default.Send(new HavePlaylistMessage(true));
|
||||
StrongReferenceMessenger.Default.Send(
|
||||
Playlists = [.. Playlists.OrderBy(p => p.Name, new TitleComparer())];
|
||||
Messenger.Send(new HavePlaylistMessage(true));
|
||||
Messenger.Send(
|
||||
new LogMessage(
|
||||
LogLevel.None,
|
||||
"PlaylistInfo_Create".GetLocalizedWithReplace("{title}", uniqueName)
|
||||
@@ -54,16 +64,16 @@ public class PlaylistLibrary
|
||||
}
|
||||
var uniqueName = GetUniquePlaylistName(newName);
|
||||
info.Name = uniqueName;
|
||||
StrongReferenceMessenger.Default.Send(new HavePlaylistMessage(Playlists.Count > 0));
|
||||
StrongReferenceMessenger.Default.Send(new PlaylistRenameMessage(oldName, info));
|
||||
Messenger.Send(new HavePlaylistMessage(Playlists.Count > 0));
|
||||
Messenger.Send(new PlaylistRenameMessage(oldName, info));
|
||||
return true;
|
||||
}
|
||||
|
||||
public void DeletePlaylist(PlaylistInfo info)
|
||||
{
|
||||
Playlists.Remove(info);
|
||||
StrongReferenceMessenger.Default.Send(new HavePlaylistMessage(Playlists.Count > 0));
|
||||
StrongReferenceMessenger.Default.Send(
|
||||
Messenger.Send(new HavePlaylistMessage(Playlists.Count > 0));
|
||||
Messenger.Send(
|
||||
new LogMessage(
|
||||
LogLevel.None,
|
||||
"PlaylistInfo_Delete".GetLocalizedWithReplace("{title}", info.Name)
|
||||
@@ -74,14 +84,14 @@ public class PlaylistLibrary
|
||||
public async Task AddToPlaylist(PlaylistInfo info, IBriefSongInfoBase song)
|
||||
{
|
||||
await info.Add(song);
|
||||
StrongReferenceMessenger.Default.Send(new HavePlaylistMessage(Playlists.Count > 0));
|
||||
StrongReferenceMessenger.Default.Send(new PlaylistChangeMessage(info));
|
||||
Messenger.Send(new HavePlaylistMessage(Playlists.Count > 0));
|
||||
Messenger.Send(new PlaylistChangeMessage(info));
|
||||
var replacements = new Dictionary<string, string>
|
||||
{
|
||||
{ "{num}", "1" },
|
||||
{ "{title}", info.Name },
|
||||
};
|
||||
StrongReferenceMessenger.Default.Send(
|
||||
Messenger.Send(
|
||||
new LogMessage(
|
||||
LogLevel.None,
|
||||
"PlaylistInfo_AddItem".GetLocalizedWithReplace(replacements)
|
||||
@@ -96,15 +106,15 @@ public class PlaylistLibrary
|
||||
return;
|
||||
}
|
||||
await info.AddRange(songs);
|
||||
StrongReferenceMessenger.Default.Send(new HavePlaylistMessage(Playlists.Count > 0));
|
||||
StrongReferenceMessenger.Default.Send(new PlaylistChangeMessage(info));
|
||||
Messenger.Send(new HavePlaylistMessage(Playlists.Count > 0));
|
||||
Messenger.Send(new PlaylistChangeMessage(info));
|
||||
var count = songs.Count();
|
||||
var replacements = new Dictionary<string, string>
|
||||
{
|
||||
{ "{num}", $"{count}" },
|
||||
{ "{title}", info.Name },
|
||||
};
|
||||
StrongReferenceMessenger.Default.Send(
|
||||
Messenger.Send(
|
||||
new LogMessage(
|
||||
LogLevel.None,
|
||||
count == 1
|
||||
@@ -117,8 +127,8 @@ public class PlaylistLibrary
|
||||
public async Task DeleteFromPlaylist(PlaylistInfo info, IndexedPlaylistSong song)
|
||||
{
|
||||
await info.Delete(song);
|
||||
StrongReferenceMessenger.Default.Send(new HavePlaylistMessage(Playlists.Count > 0));
|
||||
StrongReferenceMessenger.Default.Send(new PlaylistChangeMessage(info));
|
||||
Messenger.Send(new HavePlaylistMessage(Playlists.Count > 0));
|
||||
Messenger.Send(new PlaylistChangeMessage(info));
|
||||
}
|
||||
|
||||
public void MoveUpInPlaylist(PlaylistInfo info, IndexedPlaylistSong song)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package
|
||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||
@@ -7,7 +7,8 @@
|
||||
xmlns:genTemplate="http://schemas.microsoft.com/appx/developer/templatestudio"
|
||||
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
|
||||
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
|
||||
IgnorableNamespaces="uap rescap genTemplate">
|
||||
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
|
||||
IgnorableNamespaces="uap rescap genTemplate uap3">
|
||||
|
||||
<Identity
|
||||
Name="C19A3696.28439CB846862"
|
||||
@@ -70,6 +71,7 @@
|
||||
<Capabilities>
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
<uap:Capability Name="musicLibrary"/>
|
||||
<uap3:Capability Name="backgroundMediaPlayback"/>
|
||||
</Capabilities>
|
||||
|
||||
<genTemplate:Metadata>
|
||||
|
||||
@@ -111,7 +111,14 @@ public partial class PlayListDetailViewModel
|
||||
public void DeleteButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Data.SelectedPlaylist = null;
|
||||
Data.ShellPage!.GetFrame().GoBack();
|
||||
if (Data.ShellViewModel!.CurrentPage == nameof(PlayListsPage))
|
||||
{
|
||||
Data.ShellPage!.GoBack();
|
||||
}
|
||||
else
|
||||
{
|
||||
Data.ShellPage!.GoBack();
|
||||
}
|
||||
Data.PlaylistLibrary.DeletePlaylist(Playlist);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using The_Untamed_Music_Player.Contracts.Services;
|
||||
using The_Untamed_Music_Player.Models;
|
||||
@@ -17,6 +18,8 @@ public partial class ShellViewModel : ObservableObject
|
||||
|
||||
public string CurrentPage { get; set; } = null!;
|
||||
|
||||
public PlaylistInfo? PrevPlaylistInfo { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial object SelectedItem { get; set; } = null!;
|
||||
|
||||
@@ -28,6 +31,10 @@ public partial class ShellViewModel : ObservableObject
|
||||
|
||||
public void NavigationFrame_Navigating(object sender, NavigatingCancelEventArgs e)
|
||||
{
|
||||
if (e.NavigationMode == NavigationMode.Back)
|
||||
{
|
||||
PrevPlaylistInfo = null;
|
||||
}
|
||||
CurrentPage = e.SourcePageType.Name;
|
||||
var navView = Data.ShellPage!.GetNavigationView();
|
||||
if (
|
||||
@@ -53,10 +60,27 @@ public partial class ShellViewModel : ObservableObject
|
||||
{
|
||||
SelectedItem = navView.MenuItems[3];
|
||||
}
|
||||
else if (CurrentPage is nameof(PlayListsPage) or nameof(PlayListDetailPage))
|
||||
else if (CurrentPage is nameof(PlayListsPage))
|
||||
{
|
||||
SelectedItem = navView.MenuItems[4];
|
||||
}
|
||||
else if (CurrentPage is nameof(PlayListDetailPage))
|
||||
{
|
||||
var playlistsNavItem = navView.MenuItems[4] as NavigationViewItem;
|
||||
var playlistSubItem = playlistsNavItem!
|
||||
.MenuItems.Cast<NavigationViewItem>()
|
||||
.FirstOrDefault(item =>
|
||||
item.DataContext is PlaylistInfo playlist && playlist == PrevPlaylistInfo
|
||||
);
|
||||
if (playlistSubItem is not null)
|
||||
{
|
||||
SelectedItem = playlistSubItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedItem = playlistsNavItem;
|
||||
}
|
||||
}
|
||||
else if (CurrentPage is nameof(SettingsPage))
|
||||
{
|
||||
SelectedItem = navView.FooterMenuItems[0];
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
Style="{StaticResource NavigationBackButtonNormalStyle}"/>
|
||||
<Image Width="16" Height="16"
|
||||
Margin="12,0,0,0" HorizontalAlignment="Left"
|
||||
Source="/Assets/AppIcon/WindowIcon.ico"/>
|
||||
Source="/Assets/AppIcon/Icon.png"/>
|
||||
<TextBlock x:Name="AppTitleBarText"
|
||||
Margin="12,0,0,0" VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
|
||||
@@ -252,7 +252,7 @@
|
||||
<Grid Background="{ThemeResource SolidBackgroundFillColorSecondaryBrush}" Canvas.ZIndex="0">
|
||||
<FontIcon Canvas.ZIndex="0"
|
||||
FontFamily="{StaticResource UntamedFontFamily}"
|
||||
FontSize="50"
|
||||
FontSize="64"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Glyph=""
|
||||
Visibility="{x:Bind ViewModel.Cover, Mode=OneWay, Converter={StaticResource InverseEmptyObjectToVisibilityConverter}}"/>
|
||||
|
||||
@@ -245,8 +245,7 @@
|
||||
Style="{StaticResource BodyStrongTextBlockStyle}"/>
|
||||
<toolkit:SettingsExpander x:Name="About" x:Uid="Settings_AboutDescription"
|
||||
Header="{x:Bind model:Data.AppDisplayName}"
|
||||
HeaderIcon="{ui:BitmapIcon ShowAsMonochrome=False,
|
||||
Source=ms-appx:///Assets/AppIcon/WindowIcon.ico}">
|
||||
HeaderIcon="{ui:BitmapIcon Source=ms-appx:///Assets/AppIcon/Icon.png}">
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.VersionDescription, Mode=OneWay}"/>
|
||||
<toolkit:SettingsExpander.Items>
|
||||
<toolkit:SettingsCard x:Uid="Settings_Thanks"/>
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
xmlns:helper="using:The_Untamed_Music_Player.Helpers"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
Loaded="OnLoaded">
|
||||
Loaded="ShellPage_Loaded"
|
||||
Unloaded="ShellPage_Unloaded">
|
||||
|
||||
<Page.Resources>
|
||||
<SolidColorBrush x:Key="NavigationViewContentBackground" Color="Transparent"/>
|
||||
@@ -25,7 +26,7 @@
|
||||
Canvas.ZIndex="1">
|
||||
<Image Width="16" Height="16"
|
||||
Margin="12,0,0,0" HorizontalAlignment="Left"
|
||||
Source="/Assets/AppIcon/WindowIcon.ico"/>
|
||||
Source="/Assets/AppIcon/Icon.png"/>
|
||||
<TextBlock Margin="40,0,0,0" VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind _appTitleBarText}"
|
||||
@@ -80,10 +81,11 @@
|
||||
<KeyboardAccelerator Key="Q" Modifiers="Control"/>
|
||||
</NavigationViewItem.KeyboardAccelerators>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItem x:Uid="Shell_PlayLists"
|
||||
<NavigationViewItem x:Name="PlaylistsNavItem" x:Uid="Shell_PlayLists"
|
||||
AccessKey="NP"
|
||||
Icon="{ui:FontIcon FontFamily={StaticResource UntamedFontFamily},
|
||||
Glyph=}"
|
||||
Loaded="PlaylistsNavItem_Loaded"
|
||||
Tag="PlayListsPage">
|
||||
<NavigationViewItem.KeyboardAccelerators>
|
||||
<KeyboardAccelerator Key="Y" Modifiers="Control"/>
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
using The_Untamed_Music_Player.Helpers;
|
||||
using The_Untamed_Music_Player.Messages;
|
||||
using The_Untamed_Music_Player.Models;
|
||||
using The_Untamed_Music_Player.ViewModels;
|
||||
|
||||
namespace The_Untamed_Music_Player.Views;
|
||||
|
||||
public sealed partial class ShellPage : Page
|
||||
public sealed partial class ShellPage : Page, IRecipient<HavePlaylistMessage>
|
||||
{
|
||||
public ShellViewModel ViewModel { get; }
|
||||
|
||||
@@ -15,17 +17,39 @@ public sealed partial class ShellPage : Page
|
||||
|
||||
public ShellPage() //注意修改, 不能有参数
|
||||
{
|
||||
StrongReferenceMessenger.Default.Register(this);
|
||||
InitializeComponent();
|
||||
Data.ShellPage = this;
|
||||
ViewModel = App.GetService<ShellViewModel>();
|
||||
Data.MainWindow!.SetTitleBar(AppTitleBar);
|
||||
}
|
||||
|
||||
private void OnLoaded(object sender, RoutedEventArgs e)
|
||||
public void Receive(HavePlaylistMessage message)
|
||||
{
|
||||
PlaylistsNavItem.MenuItems.Clear();
|
||||
foreach (var playlist in Data.PlaylistLibrary.Playlists)
|
||||
{
|
||||
var playlistItem = new NavigationViewItem
|
||||
{
|
||||
Content = playlist.Name,
|
||||
Tag = nameof(PlayListDetailPage),
|
||||
DataContext = playlist,
|
||||
};
|
||||
ToolTipService.SetToolTip(playlistItem, playlist.Name);
|
||||
PlaylistsNavItem.MenuItems.Add(playlistItem);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShellPage_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
TitleBarHelper.UpdateTitleBar(RequestedTheme);
|
||||
}
|
||||
|
||||
private void ShellPage_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
StrongReferenceMessenger.Default.Unregister<HavePlaylistMessage>(this);
|
||||
}
|
||||
|
||||
public void NavigationViewControl_DisplayModeChanged(
|
||||
NavigationView sender,
|
||||
NavigationViewDisplayModeChangedEventArgs args
|
||||
@@ -77,6 +101,25 @@ public sealed partial class ShellPage : Page
|
||||
}
|
||||
}
|
||||
|
||||
private void PlaylistsNavItem_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is NavigationViewItem navItem)
|
||||
{
|
||||
navItem.MenuItems.Clear();
|
||||
foreach (var playlist in Data.PlaylistLibrary.Playlists)
|
||||
{
|
||||
var playlistItem = new NavigationViewItem
|
||||
{
|
||||
Content = playlist.Name,
|
||||
Tag = nameof(PlayListDetailPage),
|
||||
DataContext = playlist,
|
||||
};
|
||||
ToolTipService.SetToolTip(playlistItem, playlist.Name);
|
||||
navItem.MenuItems.Add(playlistItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void NavigationViewControl_BackRequested(
|
||||
NavigationView sender,
|
||||
NavigationViewBackRequestedEventArgs args
|
||||
@@ -95,28 +138,41 @@ public sealed partial class ShellPage : Page
|
||||
{
|
||||
if (args.InvokedItemContainer is NavigationViewItem invokedItem)
|
||||
{
|
||||
Navigate($"{invokedItem.Tag}");
|
||||
var tag = $"{invokedItem.Tag}";
|
||||
if (
|
||||
tag == nameof(PlayListDetailPage)
|
||||
&& invokedItem.DataContext is PlaylistInfo playlist
|
||||
)
|
||||
{
|
||||
if (ViewModel.PrevPlaylistInfo == playlist)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Data.SelectedPlaylist = playlist;
|
||||
ViewModel.PrevPlaylistInfo = playlist;
|
||||
}
|
||||
else if (ViewModel.CurrentPage == tag)
|
||||
{
|
||||
return; // 避免重复导航到同一页面
|
||||
}
|
||||
else
|
||||
{
|
||||
ViewModel.PrevPlaylistInfo = null;
|
||||
}
|
||||
var pageToNavigate = tag switch
|
||||
{
|
||||
nameof(HomePage) => typeof(HomePage),
|
||||
nameof(MusicLibraryPage) => typeof(MusicLibraryPage),
|
||||
nameof(PlayQueuePage) => typeof(PlayQueuePage),
|
||||
nameof(PlayListsPage) => typeof(PlayListsPage),
|
||||
nameof(PlayListDetailPage) => typeof(PlayListDetailPage),
|
||||
nameof(SettingsPage) => typeof(SettingsPage),
|
||||
_ => typeof(HomePage),
|
||||
};
|
||||
NavigationFrame.Navigate(pageToNavigate);
|
||||
}
|
||||
}
|
||||
|
||||
public void Navigate(string tag)
|
||||
{
|
||||
if (ViewModel.CurrentPage == tag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var pageToNavigate = tag switch
|
||||
{
|
||||
nameof(HomePage) => typeof(HomePage),
|
||||
nameof(MusicLibraryPage) => typeof(MusicLibraryPage),
|
||||
nameof(PlayQueuePage) => typeof(PlayQueuePage),
|
||||
nameof(PlayListsPage) => typeof(PlayListsPage),
|
||||
nameof(SettingsPage) => typeof(SettingsPage),
|
||||
_ => typeof(HomePage),
|
||||
};
|
||||
NavigationFrame.Navigate(pageToNavigate);
|
||||
}
|
||||
|
||||
public void Navigate(
|
||||
string destPage,
|
||||
string parameter,
|
||||
@@ -141,4 +197,21 @@ public sealed partial class ShellPage : Page
|
||||
};
|
||||
NavigationFrame.Navigate(pageToNavigate, null, infoOverride);
|
||||
}
|
||||
|
||||
public void GoBack()
|
||||
{
|
||||
if (NavigationFrame.CanGoBack)
|
||||
{
|
||||
var page = NavigationFrame.BackStack.LastOrDefault();
|
||||
if (page?.SourcePageType == typeof(PlayListDetailPage) && Data.SelectedPlaylist is null)
|
||||
{
|
||||
NavigationFrame.BackStack.Remove(page);
|
||||
GoBack();
|
||||
}
|
||||
else
|
||||
{
|
||||
NavigationFrame.GoBack();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user