From cb06949e608a47aa190695aa66d4372276d4b185 Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Sat, 19 Aug 2023 18:16:42 +0800 Subject: [PATCH] fix gachalog refresh crash --- .../Core/Threading/DispatcherQueueProgress.cs | 46 +++++++++++++++++++ ...n.cs => DispatcherQueueSwitchOperation.cs} | 6 +-- .../Snap.Hutao/Core/Threading/ITaskContext.cs | 4 +- .../Snap.Hutao/Core/Threading/TaskContext.cs | 12 +++-- .../Service/GachaLog/GachaLogFetchStatus.cs | 10 ++++ .../Dialog/GachaLogRefreshProgressDialog.xaml | 18 ++++---- .../GachaLogRefreshProgressDialog.xaml.cs | 27 ++--------- .../ViewModel/GachaLog/GachaLogViewModel.cs | 2 +- 8 files changed, 84 insertions(+), 41 deletions(-) create mode 100644 src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatcherQueueProgress.cs rename src/Snap.Hutao/Snap.Hutao/Core/Threading/{DispatherQueueSwitchOperation.cs => DispatcherQueueSwitchOperation.cs} (83%) diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatcherQueueProgress.cs b/src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatcherQueueProgress.cs new file mode 100644 index 00000000..c4b9b1b1 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatcherQueueProgress.cs @@ -0,0 +1,46 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Core.Threading; + +internal class DispatcherQueueProgress : IProgress +{ + private readonly SynchronizationContext synchronizationContext; + private readonly Action? handler; + private readonly SendOrPostCallback invokeHandlers; + + public DispatcherQueueProgress(Action handler, SynchronizationContext synchronizationContext) + { + this.synchronizationContext = synchronizationContext; + invokeHandlers = new SendOrPostCallback(InvokeHandlers); + + ArgumentNullException.ThrowIfNull(handler); + + this.handler = handler; + } + + public event EventHandler? ProgressChanged; + + [SuppressMessage("", "VSTHRD001")] + public void Report(T value) + { + Action? handler = this.handler; + EventHandler? changedEvent = ProgressChanged; + if (handler is not null || changedEvent is not null) + { + synchronizationContext.Post(invokeHandlers, value); + } + } + + [SuppressMessage("", "SH007")] + private void InvokeHandlers(object? state) + { + T value = (T)state!; + + Action? handler = this.handler; + EventHandler? changedEvent = ProgressChanged; + + handler?.Invoke(value); + changedEvent?.Invoke(this, value); + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatherQueueSwitchOperation.cs b/src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatcherQueueSwitchOperation.cs similarity index 83% rename from src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatherQueueSwitchOperation.cs rename to src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatcherQueueSwitchOperation.cs index f600a20d..0f68e4df 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatherQueueSwitchOperation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Threading/DispatcherQueueSwitchOperation.cs @@ -11,7 +11,7 @@ namespace Snap.Hutao.Core.Threading; /// 等待此类型对象后上下文会被切换至主线程 /// [HighQuality] -internal readonly struct DispatherQueueSwitchOperation : IAwaitable, ICriticalAwaiter +internal readonly struct DispatcherQueueSwitchOperation : IAwaitable, ICriticalAwaiter { private readonly DispatcherQueue dispatherQueue; @@ -19,7 +19,7 @@ internal readonly struct DispatherQueueSwitchOperation : IAwaitable /// 调度器队列 - public DispatherQueueSwitchOperation(DispatcherQueue dispatherQueue) + public DispatcherQueueSwitchOperation(DispatcherQueue dispatherQueue) { this.dispatherQueue = dispatherQueue; } @@ -31,7 +31,7 @@ internal readonly struct DispatherQueueSwitchOperation : IAwaitable - public DispatherQueueSwitchOperation GetAwaiter() + public DispatcherQueueSwitchOperation GetAwaiter() { return this; } diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Threading/ITaskContext.cs b/src/Snap.Hutao/Snap.Hutao/Core/Threading/ITaskContext.cs index e2c7b8f0..ab389d0e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Threading/ITaskContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Threading/ITaskContext.cs @@ -8,6 +8,8 @@ namespace Snap.Hutao.Core.Threading; /// internal interface ITaskContext { + IProgress CreateProgressForMainThread(Action handler); + /// /// 在主线程上同步等待执行操作 /// @@ -26,5 +28,5 @@ internal interface ITaskContext /// /// 使用 异步切换到 后台线程 /// 等待体 - DispatherQueueSwitchOperation SwitchToMainThreadAsync(); + DispatcherQueueSwitchOperation SwitchToMainThreadAsync(); } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Threading/TaskContext.cs b/src/Snap.Hutao/Snap.Hutao/Core/Threading/TaskContext.cs index 5ae43f2c..5462d81f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Threading/TaskContext.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Threading/TaskContext.cs @@ -11,6 +11,7 @@ namespace Snap.Hutao.Core.Threading; [Injection(InjectAs.Singleton, typeof(ITaskContext))] internal sealed class TaskContext : ITaskContext { + private readonly DispatcherQueueSynchronizationContext dispatcherQueueSynchronizationContext; private readonly DispatcherQueue dispatcherQueue; /// @@ -19,8 +20,8 @@ internal sealed class TaskContext : ITaskContext public TaskContext() { dispatcherQueue = DispatcherQueue.GetForCurrentThread(); - DispatcherQueueSynchronizationContext context = new(dispatcherQueue); - SynchronizationContext.SetSynchronizationContext(context); + dispatcherQueueSynchronizationContext = new(dispatcherQueue); + SynchronizationContext.SetSynchronizationContext(dispatcherQueueSynchronizationContext); } /// @@ -30,7 +31,7 @@ internal sealed class TaskContext : ITaskContext } /// - public DispatherQueueSwitchOperation SwitchToMainThreadAsync() + public DispatcherQueueSwitchOperation SwitchToMainThreadAsync() { return new(dispatcherQueue); } @@ -47,4 +48,9 @@ internal sealed class TaskContext : ITaskContext dispatcherQueue.Invoke(action); } } + + public IProgress CreateProgressForMainThread(Action handler) + { + return new DispatcherQueueProgress(handler, dispatcherQueueSynchronizationContext); + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogFetchStatus.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogFetchStatus.cs index 118f5221..c77de33c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogFetchStatus.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogFetchStatus.cs @@ -34,4 +34,14 @@ internal sealed class GachaLogFetchStatus /// 当前获取的物品 /// public List Items { get; set; } = new(20); + + public string Header + { + get + { + return AuthKeyTimeout + ? SH.ViewDialogGachaLogRefreshProgressAuthkeyTimeout + : SH.ViewDialogGachaLogRefreshProgressDescription.Format(ConfigType.GetLocalizedDescription()); + } + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/GachaLogRefreshProgressDialog.xaml b/src/Snap.Hutao/Snap.Hutao/View/Dialog/GachaLogRefreshProgressDialog.xaml index 546a0bd0..7f552aac 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/GachaLogRefreshProgressDialog.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/GachaLogRefreshProgressDialog.xaml @@ -7,27 +7,27 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:shcm="using:Snap.Hutao.Control.Markup" xmlns:shvc="using:Snap.Hutao.View.Control" + xmlns:shvh="using:Snap.Hutao.View.Helper" Title="{shcm:ResourceString Name=ViewDialogGachaLogRefreshProgressTitle}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> - - - + + + ItemTemplate="{StaticResource GachaItemDataTemplate}" + ItemsSource="{x:Bind Status.Items, Mode=OneWay}"> /// 接收进度更新 /// - /// 状态 - public void OnReport(GachaLogFetchStatus state) + /// 状态 + public void OnReport(GachaLogFetchStatus status) { - Status = state; - - // TODO: test new binding approach - GachaItemsPresenter.Header = state.AuthKeyTimeout - ? SH.ViewDialogGachaLogRefreshProgressAuthkeyTimeout - : SH.ViewDialogGachaLogRefreshProgressDescription.Format(state.ConfigType.GetLocalizedDescription()); - - // Binding not working here. - GachaItemsPresenter.Items.Clear(); - - // System.InvalidOperationException: Collection was modified; enumeration operation may not execute. - foreach (Item item in state.Items.ToList()) - { - GachaItemsPresenter.Items.Add(new ItemIcon - { - Width = 60, - Height = 60, - Quality = item.Quality, - Icon = item.Icon, - Badge = item.Badge, - }); - } + Status = status; } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModel.cs index ceb29fbe..6eac70f1 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/GachaLog/GachaLogViewModel.cs @@ -160,7 +160,7 @@ internal sealed partial class GachaLogViewModel : Abstraction.ViewModel GachaLogRefreshProgressDialog dialog = await contentDialogFactory.CreateInstanceAsync().ConfigureAwait(false); ContentDialogHideToken hideToken = await dialog.BlockAsync(taskContext).ConfigureAwait(false); - Progress progress = new(dialog.OnReport); + IProgress progress = taskContext.CreateProgressForMainThread(dialog.OnReport); bool authkeyValid; try