From 093c0ee7812b6731ecb15213496ea3a8899d5bee Mon Sep 17 00:00:00 2001 From: Nlick47 <2247717951@qq.com> Date: Fri, 21 Mar 2025 11:49:37 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E4=B8=AA=E4=BA=BA=E7=A9=BA?= =?UTF-8?q?=E9=97=B4-=E5=8E=86=E5=8F=B2=E8=AE=B0=E5=BD=95=20=E5=8A=A0?= =?UTF-8?q?=E5=85=A5=E5=A2=9E=E9=87=8F=E5=8A=A0=E8=BD=BD=EF=BC=8C=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=97=A0=E9=99=90=E6=BB=9A=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IncrementalLoadingBehavior.cs | 61 +++++ DownKyi/ViewModels/ViewMyHistoryViewModel.cs | 214 ++++++++---------- DownKyi/Views/ViewMyHistory.axaml | 9 +- 3 files changed, 167 insertions(+), 117 deletions(-) create mode 100644 DownKyi/CustomAction/IncrementalLoadingBehavior.cs diff --git a/DownKyi/CustomAction/IncrementalLoadingBehavior.cs b/DownKyi/CustomAction/IncrementalLoadingBehavior.cs new file mode 100644 index 0000000..2dfc80b --- /dev/null +++ b/DownKyi/CustomAction/IncrementalLoadingBehavior.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.ObjectModel; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Xaml.Interactivity; + +namespace DownKyi.CustomAction; + +public class IncrementalLoadingBehavior: Behavior +{ + + public static readonly StyledProperty>> LoadPageFuncProperty = + AvaloniaProperty.Register, Func>>(nameof(LoadPageFunc)); + + private int currentPage = 1; + private bool isLoading = false; + public Func> LoadPageFunc + { + get => GetValue(LoadPageFuncProperty); + set => SetValue(LoadPageFuncProperty, value); + } + + protected override void OnAttached() + { + base.OnAttached(); + AssociatedObject.AddHandler(ScrollViewer.ScrollChangedEvent, OnScrollChanged); + LoadNextPageAsync(); + } + + protected override void OnDetaching() + { + base.OnDetaching(); + AssociatedObject.RemoveHandler(ScrollViewer.ScrollChangedEvent, OnScrollChanged); + } + + private async void OnScrollChanged(object sender, ScrollChangedEventArgs e) + { + var scrollViewer = e.Source as ScrollViewer; + if (scrollViewer == null) return; + + if (scrollViewer.Offset.Y >= scrollViewer.Extent.Height - scrollViewer.Viewport.Height) + { + await LoadNextPageAsync(); + } + } + + private async Task LoadNextPageAsync() + { + if (isLoading || LoadPageFunc == null ) return; + + isLoading = true; + var items = await LoadPageFunc(currentPage); + foreach (var item in items) + { + AssociatedObject.Items.Add(item); + } + currentPage++; + isLoading = false; + } +} \ No newline at end of file diff --git a/DownKyi/ViewModels/ViewMyHistoryViewModel.cs b/DownKyi/ViewModels/ViewMyHistoryViewModel.cs index ad6392c..ccf560b 100644 --- a/DownKyi/ViewModels/ViewMyHistoryViewModel.cs +++ b/DownKyi/ViewModels/ViewMyHistoryViewModel.cs @@ -1,9 +1,12 @@ -using System.Collections; +using System; +using System.Collections; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Threading; using System.Threading.Tasks; using DownKyi.Core.BiliApi.History; +using DownKyi.Core.BiliApi.History.Models; using DownKyi.Core.BiliApi.VideoStream; using DownKyi.Core.Utils; using DownKyi.Events; @@ -246,6 +249,32 @@ public class ViewMyHistoryViewModel : ViewModelBase public DelegateCommand AddAllToDownloadCommand => _addAllToDownloadCommand ??= new DelegateCommand(ExecuteAddAllToDownloadCommand); + + private long _nextMax = 0; + + private long _nextViewAt = 0; + + public Func> LoadPageFunc => (page) => + { + int startIndex = (page - 1) * VideoNumberInPage; + return Task.Run(() => + { + var result = History.GetHistory(_nextMax, _nextViewAt, VideoNumberInPage); + foreach (var item in result.List) + { + var history = Convert(item, EventAggregator); + if (history != null) + { + Medias.Add(history); + } + } + + _nextMax = result.Cursor.Max; + _nextViewAt = result.Cursor.ViewAt; + return Medias.Skip(startIndex).Take(VideoNumberInPage).ToArray(); + }); + }; + /// /// 添加所有视频到下载列表事件 /// @@ -327,8 +356,6 @@ public class ViewMyHistoryViewModel : ViewModelBase await Task.Run(() => { - var cancellationToken = _tokenSource?.Token; - var historyList = History.GetHistory(0, 0, VideoNumberInPage); if (historyList?.List == null || historyList.List.Count == 0) { @@ -336,118 +363,13 @@ public class ViewMyHistoryViewModel : ViewModelBase NoDataVisibility = true; return; } - - foreach (var history in historyList.List) + App.PropertyChangeAsync(() => { - if (history.History == null) - { - continue; - } - - if (history.History.Business != "archive" && history.History.Business != "pgc") - { - continue; - } - - // 播放url - var url = history.History.Business switch - { - "archive" => "https://www.bilibili.com/video/" + history.History.Bvid, - "pgc" => history.Uri, - _ => "https://www.bilibili.com" - }; - - // 查询、保存封面 - var coverUrl = history.Cover; - if (!coverUrl.ToLower().StartsWith("http")) - { - coverUrl = $"https:{history.Cover}"; - } - - // 获取用户头像 - var upName = history.AuthorFace != null ? history.AuthorName : ""; - - - // 观看平台 - var platform = history.History.Dt switch - { - 1 or 3 or 5 or 7 => - // 手机端 - NormalIcon.Instance().PlatformMobile, - 2 => - // web端 - NormalIcon.Instance().PlatformPC, - 4 or 6 => - // pad端 - NormalIcon.Instance().PlatformIpad, - 33 => - // TV端 - NormalIcon.Instance().PlatformTV, - _ => null - }; - - // 是否显示Partdesc - var partdescVisibility = history.NewDesc != ""; - - // 是否显示UP主信息和分区信息 - var upAndTagVisibility = history.History.Business == "archive"; - - App.PropertyChangeAsync(() => - { - // 观看进度 - // -1 已看完 - // 0 刚开始 - // >0 看到 progress - string progress; - if (history.Progress == -1) - { - progress = DictionaryResource.GetString("HistoryFinished"); - } - else if (history.Progress == 0) - { - progress = DictionaryResource.GetString("HistoryStarted"); - } - else - { - progress = DictionaryResource.GetString("HistoryWatch") + " " + - Format.FormatDuration3(history.Progress); - } - - var media = new HistoryMedia(EventAggregator) - { - Business = history.History.Business, - Bvid = history.History.Bvid, - Url = url, - UpMid = history.AuthorMid, - Cover = coverUrl ?? "avares://DownKyi/Resources/video-placeholder.png", - Title = history.Title, - SubTitle = history.ShowTitle, - Duration = history.Duration, - TagName = history.TagName, - Partdesc = history.NewDesc, - Progress = progress, - Platform = platform, - UpName = upName, - UpHeader = history.AuthorFace ?? "", - - PartdescVisibility = partdescVisibility, - UpAndTagVisibility = upAndTagVisibility, - }; - - Medias.Add(media); - - ContentVisibility = true; - LoadingVisibility = false; - NoDataVisibility = false; - }); - - // 判断是否该结束线程,若为true,跳出循环 - if (cancellationToken?.IsCancellationRequested == true) - { - break; - } - } - }, (_tokenSource = new CancellationTokenSource()).Token); + ContentVisibility = true; + LoadingVisibility = false; + NoDataVisibility = false; + }); + }); } /// @@ -502,4 +424,68 @@ public class ViewMyHistoryViewModel : ViewModelBase UpdateHistoryMediaList(); } + + private static bool IsValidBusiness(string business) + => business is "archive" or "pgc"; + + private static string BuildMediaUrl(HistoryList history) => + history.History.Business switch + { + "archive" => $"https://www.bilibili.com/video/{history.History.Bvid}", + "pgc" => history.Uri, + _ => "https://www.bilibili.com" + }; + + private static string ProcessCoverUrl(string originalUrl) => + !string.IsNullOrEmpty(originalUrl) && !originalUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase) + ? $"https:{originalUrl}" + : originalUrl; + + private static VectorImage? GetPlatformIcon(int dt) => + dt switch + { + 1 or 3 or 5 or 7 => NormalIcon.Instance().PlatformMobile, + 2 => NormalIcon.Instance().PlatformPC, + 4 or 6 => NormalIcon.Instance().PlatformIpad, + 33 => NormalIcon.Instance().PlatformTV, + _ => null + }; + + private static string BuildProgressText(long progress) => + progress switch + { + -1 => DictionaryResource.GetString("HistoryFinished"), + 0 => DictionaryResource.GetString("HistoryStarted"), + _ => $"{DictionaryResource.GetString("HistoryWatch")} {Format.FormatDuration3(progress)}" + }; + + public static HistoryMedia Convert(HistoryList history, IEventAggregator eventAggregator) + { + if (history?.History == null || !IsValidBusiness(history.History.Business)) + return null; + + var url = BuildMediaUrl(history); + var coverUrl = ProcessCoverUrl(history.Cover); + var platform = GetPlatformIcon(history.History.Dt); + + return new HistoryMedia(eventAggregator) + { + Business = history.History.Business, + Bvid = history.History.Bvid, + Url = url, + UpMid = history.AuthorMid, + Cover = coverUrl ?? "avares://DownKyi/Resources/video-placeholder.png", + Title = history.Title, + SubTitle = history.ShowTitle, + Duration = history.Duration, + TagName = history.TagName, + Partdesc = history.NewDesc, + Progress = BuildProgressText(history.Progress), + Platform = platform, + UpName = history.AuthorFace != null ? history.AuthorName : "", + UpHeader = history.AuthorFace ?? "", + PartdescVisibility = !string.IsNullOrEmpty(history.NewDesc), + UpAndTagVisibility = history.History.Business == "archive" + }; + } } \ No newline at end of file diff --git a/DownKyi/Views/ViewMyHistory.axaml b/DownKyi/Views/ViewMyHistory.axaml index dde5420..cfef893 100644 --- a/DownKyi/Views/ViewMyHistory.axaml +++ b/DownKyi/Views/ViewMyHistory.axaml @@ -9,7 +9,8 @@ x:DataType="vm:ViewMyHistoryViewModel" xmlns:i="using:Avalonia.Xaml.Interactivity" xmlns:ia="clr-namespace:Avalonia.Xaml.Interactions.Core;assembly=Avalonia.Xaml.Interactions" - xmlns:asyncImageLoader="clr-namespace:DownKyi.CustomControl.AsyncImageLoader"> + xmlns:asyncImageLoader="clr-namespace:DownKyi.CustomControl.AsyncImageLoader" + xmlns:customAction="clr-namespace:DownKyi.CustomAction"> @@ -246,11 +247,13 @@ x:Name="NameMedias" Grid.Row="0" BorderThickness="0" - ItemsSource="{Binding Medias}" ItemContainerTheme="{StaticResource MediaListStyle}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectionMode="Multiple"> + @@ -258,7 +261,7 @@ - + From 9d3a7b66a646ca52bce1e3ebb6fb2a32a4f7d586 Mon Sep 17 00:00:00 2001 From: Nlick47 <2247717951@qq.com> Date: Fri, 21 Mar 2025 19:45:27 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E8=A7=82?= =?UTF-8?q?=E7=9C=8B=E5=8E=86=E5=8F=B2=E6=97=A0=E9=99=90=E6=BB=9A=E5=8A=A8?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IncrementalLoadingBehavior.cs | 2 ++ DownKyi/ViewModels/ViewMyHistoryViewModel.cs | 29 +++++++++++++------ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/DownKyi/CustomAction/IncrementalLoadingBehavior.cs b/DownKyi/CustomAction/IncrementalLoadingBehavior.cs index 2dfc80b..0f55c2b 100644 --- a/DownKyi/CustomAction/IncrementalLoadingBehavior.cs +++ b/DownKyi/CustomAction/IncrementalLoadingBehavior.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Avalonia; using Avalonia.Controls; using Avalonia.Xaml.Interactivity; +using ImTools; namespace DownKyi.CustomAction; @@ -51,6 +52,7 @@ public class IncrementalLoadingBehavior: Behavior isLoading = true; var items = await LoadPageFunc(currentPage); + if(items == null || items.Length == 0) return; foreach (var item in items) { AssociatedObject.Items.Add(item); diff --git a/DownKyi/ViewModels/ViewMyHistoryViewModel.cs b/DownKyi/ViewModels/ViewMyHistoryViewModel.cs index ccf560b..b2145cf 100644 --- a/DownKyi/ViewModels/ViewMyHistoryViewModel.cs +++ b/DownKyi/ViewModels/ViewMyHistoryViewModel.cs @@ -259,19 +259,30 @@ public class ViewMyHistoryViewModel : ViewModelBase int startIndex = (page - 1) * VideoNumberInPage; return Task.Run(() => { - var result = History.GetHistory(_nextMax, _nextViewAt, VideoNumberInPage); - foreach (var item in result.List) + App.PropertyChangeAsync(() => { - var history = Convert(item, EventAggregator); - if (history != null) + LoadingVisibility = true; + }); + var result = History.GetHistory(_nextMax, _nextViewAt, VideoNumberInPage); + if (result?.List?.Count > 0) + { + foreach (var item in result.List) { - Medias.Add(history); + var history = Convert(item, EventAggregator); + if (history != null) + { + Medias.Add(history); + } } + App.PropertyChangeAsync(() => + { + LoadingVisibility = false; + }); + _nextMax = result.Cursor.Max; + _nextViewAt = result.Cursor.ViewAt; + return Medias.Skip(startIndex).Take(VideoNumberInPage).ToArray(); } - - _nextMax = result.Cursor.Max; - _nextViewAt = result.Cursor.ViewAt; - return Medias.Skip(startIndex).Take(VideoNumberInPage).ToArray(); + return Array.Empty(); }); }; From 31f24e2188a5a633eb54b5968d5f7e1298f68da0 Mon Sep 17 00:00:00 2001 From: Nlick47 <2247717951@qq.com> Date: Sun, 23 Mar 2025 05:20:18 +0800 Subject: [PATCH 3/4] =?UTF-8?q?refactor:=20=E9=87=8D=E6=96=B0=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E6=97=A0=E9=99=90=E6=BB=9A=E5=8A=A8=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IncrementalLoadingBehavior.cs | 63 --------------- .../CustomAction/InfiniteScrollBehavior.cs | 63 +++++++++++++++ DownKyi/ViewModels/ViewMyHistoryViewModel.cs | 78 +++++++++---------- DownKyi/Views/ViewMyHistory.axaml | 16 ++-- 4 files changed, 112 insertions(+), 108 deletions(-) delete mode 100644 DownKyi/CustomAction/IncrementalLoadingBehavior.cs create mode 100644 DownKyi/CustomAction/InfiniteScrollBehavior.cs diff --git a/DownKyi/CustomAction/IncrementalLoadingBehavior.cs b/DownKyi/CustomAction/IncrementalLoadingBehavior.cs deleted file mode 100644 index 0f55c2b..0000000 --- a/DownKyi/CustomAction/IncrementalLoadingBehavior.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Collections.ObjectModel; -using System.Threading.Tasks; -using Avalonia; -using Avalonia.Controls; -using Avalonia.Xaml.Interactivity; -using ImTools; - -namespace DownKyi.CustomAction; - -public class IncrementalLoadingBehavior: Behavior -{ - - public static readonly StyledProperty>> LoadPageFuncProperty = - AvaloniaProperty.Register, Func>>(nameof(LoadPageFunc)); - - private int currentPage = 1; - private bool isLoading = false; - public Func> LoadPageFunc - { - get => GetValue(LoadPageFuncProperty); - set => SetValue(LoadPageFuncProperty, value); - } - - protected override void OnAttached() - { - base.OnAttached(); - AssociatedObject.AddHandler(ScrollViewer.ScrollChangedEvent, OnScrollChanged); - LoadNextPageAsync(); - } - - protected override void OnDetaching() - { - base.OnDetaching(); - AssociatedObject.RemoveHandler(ScrollViewer.ScrollChangedEvent, OnScrollChanged); - } - - private async void OnScrollChanged(object sender, ScrollChangedEventArgs e) - { - var scrollViewer = e.Source as ScrollViewer; - if (scrollViewer == null) return; - - if (scrollViewer.Offset.Y >= scrollViewer.Extent.Height - scrollViewer.Viewport.Height) - { - await LoadNextPageAsync(); - } - } - - private async Task LoadNextPageAsync() - { - if (isLoading || LoadPageFunc == null ) return; - - isLoading = true; - var items = await LoadPageFunc(currentPage); - if(items == null || items.Length == 0) return; - foreach (var item in items) - { - AssociatedObject.Items.Add(item); - } - currentPage++; - isLoading = false; - } -} \ No newline at end of file diff --git a/DownKyi/CustomAction/InfiniteScrollBehavior.cs b/DownKyi/CustomAction/InfiniteScrollBehavior.cs new file mode 100644 index 0000000..7d70bcf --- /dev/null +++ b/DownKyi/CustomAction/InfiniteScrollBehavior.cs @@ -0,0 +1,63 @@ +using Avalonia.Controls; +using System.Windows.Input; +using Avalonia; +using Avalonia.Threading; +using Avalonia.Xaml.Interactivity; + + +namespace DownKyi.CustomAction; +public class InfiniteScrollBehavior : Behavior +{ + private bool _isExecuting; + + public static readonly StyledProperty LoadMoreCommandProperty = + AvaloniaProperty.Register( + nameof(LoadMoreCommand)); + + public ICommand? LoadMoreCommand + { + get => GetValue(LoadMoreCommandProperty); + set => SetValue(LoadMoreCommandProperty, value); + } + + protected override void OnAttached() + { + base.OnAttached(); + AssociatedObject.AddHandler( + ScrollViewer.ScrollChangedEvent, + HandleScrollChanged); + } + + protected override void OnDetaching() + { + base.OnDetaching(); + AssociatedObject.RemoveHandler(ScrollViewer.ScrollChangedEvent, HandleScrollChanged); + } + + private void HandleScrollChanged(object sender, ScrollChangedEventArgs e) + { + if (_isExecuting || LoadMoreCommand == null) + return; + + var scrollViewer = e.Source as ScrollViewer; + + if (scrollViewer == null || + scrollViewer.Offset.Y + scrollViewer.Viewport.Height < scrollViewer.Extent.Height - 50) + return; + + _isExecuting = true; + + try + { + if (LoadMoreCommand?.CanExecute(null) == true) + { + LoadMoreCommand.Execute(null); + } + } + finally + { + _isExecuting = false; + } + } + +} \ No newline at end of file diff --git a/DownKyi/ViewModels/ViewMyHistoryViewModel.cs b/DownKyi/ViewModels/ViewMyHistoryViewModel.cs index b2145cf..50cc946 100644 --- a/DownKyi/ViewModels/ViewMyHistoryViewModel.cs +++ b/DownKyi/ViewModels/ViewMyHistoryViewModel.cs @@ -139,6 +139,8 @@ public class ViewMyHistoryViewModel : ViewModelBase private DelegateCommand? _backSpaceCommand; public DelegateCommand BackSpaceCommand => _backSpaceCommand ??= new DelegateCommand(ExecuteBackSpace); + + /// /// 返回事件 @@ -248,44 +250,33 @@ public class ViewMyHistoryViewModel : ViewModelBase public DelegateCommand AddAllToDownloadCommand => _addAllToDownloadCommand ??= new DelegateCommand(ExecuteAddAllToDownloadCommand); + + private DelegateCommand? _loadMoreCommand; - + public DelegateCommand LoadMoreCommand => + _addAllToDownloadCommand ??= new DelegateCommand(ExecuteLoadMoreCommand); + private long _nextMax = 0; private long _nextViewAt = 0; - public Func> LoadPageFunc => (page) => + public async void ExecuteLoadMoreCommand() { - int startIndex = (page - 1) * VideoNumberInPage; - return Task.Run(() => + if(NoDataVisibility) return; + LoadingVisibility = true; + var result = await Task.Run(() => { - App.PropertyChangeAsync(() => - { - LoadingVisibility = true; - }); - var result = History.GetHistory(_nextMax, _nextViewAt, VideoNumberInPage); - if (result?.List?.Count > 0) - { - foreach (var item in result.List) - { - var history = Convert(item, EventAggregator); - if (history != null) - { - Medias.Add(history); - } - } - App.PropertyChangeAsync(() => - { - LoadingVisibility = false; - }); - _nextMax = result.Cursor.Max; - _nextViewAt = result.Cursor.ViewAt; - return Medias.Skip(startIndex).Take(VideoNumberInPage).ToArray(); - } - return Array.Empty(); + return History.GetHistory(_nextMax, _nextViewAt, VideoNumberInPage); }); - }; - + if (result?.List?.Count > 0) + { + Medias.AddRange(result.List.Select(x => Convert(x,EventAggregator)) + .Where(v => v != null && !string.IsNullOrEmpty(v.Title)).ToList()); + _nextMax = result.Cursor.Max; + _nextViewAt = result.Cursor.ViewAt; + } + LoadingVisibility = false; + } /// /// 添加所有视频到下载列表事件 /// @@ -368,18 +359,23 @@ public class ViewMyHistoryViewModel : ViewModelBase await Task.Run(() => { var historyList = History.GetHistory(0, 0, VideoNumberInPage); - if (historyList?.List == null || historyList.List.Count == 0) + if (historyList?.List?.Count > 0) { - LoadingVisibility = false; - NoDataVisibility = true; - return; + App.PropertyChangeAsync(() => + { + ContentVisibility = true; + LoadingVisibility = false; + NoDataVisibility = false; + }); } - App.PropertyChangeAsync(() => + else { - ContentVisibility = true; - LoadingVisibility = false; - NoDataVisibility = false; - }); + App.PropertyChangeAsync(() => + { + LoadingVisibility = false; + NoDataVisibility = true; + }); + } }); } @@ -399,6 +395,8 @@ public class ViewMyHistoryViewModel : ViewModelBase LoadingVisibility = false; NoDataVisibility = false; + _nextMax = 0; + _nextViewAt = 0; Medias.Clear(); IsSelectAll = false; } @@ -470,7 +468,7 @@ public class ViewMyHistoryViewModel : ViewModelBase _ => $"{DictionaryResource.GetString("HistoryWatch")} {Format.FormatDuration3(progress)}" }; - public static HistoryMedia Convert(HistoryList history, IEventAggregator eventAggregator) + public static HistoryMedia? Convert(HistoryList history, IEventAggregator eventAggregator) { if (history?.History == null || !IsValidBusiness(history.History.Business)) return null; diff --git a/DownKyi/Views/ViewMyHistory.axaml b/DownKyi/Views/ViewMyHistory.axaml index cfef893..23a9e4b 100644 --- a/DownKyi/Views/ViewMyHistory.axaml +++ b/DownKyi/Views/ViewMyHistory.axaml @@ -42,7 +42,8 @@ CornerRadius="5"> - + @@ -145,7 +146,8 @@ VerticalAlignment="Center" CornerRadius="12"> - + - + + + + From ed7b61f4e5719ea5aab62bdf76b472c9b07c1b18 Mon Sep 17 00:00:00 2001 From: Nlick47 <2247717951@qq.com> Date: Sun, 23 Mar 2025 09:11:47 +0800 Subject: [PATCH 4/4] =?UTF-8?q?refactor:=20=E8=A7=A6=E5=8F=91=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E6=95=B0=E6=8D=AE=E4=BF=AE=E6=94=B9=E4=B8=BA=E5=BC=82?= =?UTF-8?q?=E6=AD=A5=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DownKyi/Commands/AsyncDelegateCommand.cs | 55 ++++++++++++++++++++ DownKyi/ViewModels/ViewMyHistoryViewModel.cs | 8 ++- 2 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 DownKyi/Commands/AsyncDelegateCommand.cs diff --git a/DownKyi/Commands/AsyncDelegateCommand.cs b/DownKyi/Commands/AsyncDelegateCommand.cs new file mode 100644 index 0000000..895a005 --- /dev/null +++ b/DownKyi/Commands/AsyncDelegateCommand.cs @@ -0,0 +1,55 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Input; + +namespace DownKyi.Commands; + +public class AsyncDelegateCommand : ICommand +{ + private readonly Func _execute; + private readonly Func _canExecute; + private CancellationTokenSource _cancellationTokenSource; + private bool _isExecuting; + + public event EventHandler CanExecuteChanged; + + public AsyncDelegateCommand(Func execute, Func canExecute = null) + { + _execute = execute ?? throw new ArgumentNullException(nameof(execute)); + _canExecute = canExecute; + } + + public bool CanExecute(object parameter) + { + return !_isExecuting && (_canExecute?.Invoke(parameter) ?? true); + } + + public async void Execute(object parameter) + { + _isExecuting = true; + RaiseCanExecuteChanged(); + + _cancellationTokenSource = new CancellationTokenSource(); + + try + { + await _execute(parameter, _cancellationTokenSource.Token); + } + finally + { + _isExecuting = false; + RaiseCanExecuteChanged(); + } + } + + public void Cancel() + { + _cancellationTokenSource?.Cancel(); + } + + protected void RaiseCanExecuteChanged() + { + CanExecuteChanged?.Invoke(this, EventArgs.Empty); + } +} \ No newline at end of file diff --git a/DownKyi/ViewModels/ViewMyHistoryViewModel.cs b/DownKyi/ViewModels/ViewMyHistoryViewModel.cs index 50cc946..16f70cf 100644 --- a/DownKyi/ViewModels/ViewMyHistoryViewModel.cs +++ b/DownKyi/ViewModels/ViewMyHistoryViewModel.cs @@ -5,6 +5,7 @@ using System.Collections.ObjectModel; using System.Linq; using System.Threading; using System.Threading.Tasks; +using DownKyi.Commands; using DownKyi.Core.BiliApi.History; using DownKyi.Core.BiliApi.History.Models; using DownKyi.Core.BiliApi.VideoStream; @@ -251,16 +252,13 @@ public class ViewMyHistoryViewModel : ViewModelBase public DelegateCommand AddAllToDownloadCommand => _addAllToDownloadCommand ??= new DelegateCommand(ExecuteAddAllToDownloadCommand); - private DelegateCommand? _loadMoreCommand; - - public DelegateCommand LoadMoreCommand => - _addAllToDownloadCommand ??= new DelegateCommand(ExecuteLoadMoreCommand); + public AsyncDelegateCommand LoadMoreCommand => new (ExecuteLoadMoreCommand); private long _nextMax = 0; private long _nextViewAt = 0; - public async void ExecuteLoadMoreCommand() + public async Task ExecuteLoadMoreCommand(object obj,CancellationToken token) { if(NoDataVisibility) return; LoadingVisibility = true;