mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
fix gachalog refresh crash
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Core.Threading;
|
||||
|
||||
internal class DispatcherQueueProgress<T> : IProgress<T>
|
||||
{
|
||||
private readonly SynchronizationContext synchronizationContext;
|
||||
private readonly Action<T>? handler;
|
||||
private readonly SendOrPostCallback invokeHandlers;
|
||||
|
||||
public DispatcherQueueProgress(Action<T> handler, SynchronizationContext synchronizationContext)
|
||||
{
|
||||
this.synchronizationContext = synchronizationContext;
|
||||
invokeHandlers = new SendOrPostCallback(InvokeHandlers);
|
||||
|
||||
ArgumentNullException.ThrowIfNull(handler);
|
||||
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
public event EventHandler<T>? ProgressChanged;
|
||||
|
||||
[SuppressMessage("", "VSTHRD001")]
|
||||
public void Report(T value)
|
||||
{
|
||||
Action<T>? handler = this.handler;
|
||||
EventHandler<T>? 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<T>? handler = this.handler;
|
||||
EventHandler<T>? changedEvent = ProgressChanged;
|
||||
|
||||
handler?.Invoke(value);
|
||||
changedEvent?.Invoke(this, value);
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ namespace Snap.Hutao.Core.Threading;
|
||||
/// 等待此类型对象后上下文会被切换至主线程
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
internal readonly struct DispatherQueueSwitchOperation : IAwaitable<DispatherQueueSwitchOperation>, ICriticalAwaiter
|
||||
internal readonly struct DispatcherQueueSwitchOperation : IAwaitable<DispatcherQueueSwitchOperation>, ICriticalAwaiter
|
||||
{
|
||||
private readonly DispatcherQueue dispatherQueue;
|
||||
|
||||
@@ -19,7 +19,7 @@ internal readonly struct DispatherQueueSwitchOperation : IAwaitable<DispatherQue
|
||||
/// 构造一个新的调度器队列等待器
|
||||
/// </summary>
|
||||
/// <param name="dispatherQueue">调度器队列</param>
|
||||
public DispatherQueueSwitchOperation(DispatcherQueue dispatherQueue)
|
||||
public DispatcherQueueSwitchOperation(DispatcherQueue dispatherQueue)
|
||||
{
|
||||
this.dispatherQueue = dispatherQueue;
|
||||
}
|
||||
@@ -31,7 +31,7 @@ internal readonly struct DispatherQueueSwitchOperation : IAwaitable<DispatherQue
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DispatherQueueSwitchOperation GetAwaiter()
|
||||
public DispatcherQueueSwitchOperation GetAwaiter()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
@@ -8,6 +8,8 @@ namespace Snap.Hutao.Core.Threading;
|
||||
/// </summary>
|
||||
internal interface ITaskContext
|
||||
{
|
||||
IProgress<T> CreateProgressForMainThread<T>(Action<T> handler);
|
||||
|
||||
/// <summary>
|
||||
/// 在主线程上同步等待执行操作
|
||||
/// </summary>
|
||||
@@ -26,5 +28,5 @@ internal interface ITaskContext
|
||||
/// </summary>
|
||||
/// <remarks>使用 <see cref="SwitchToBackgroundAsync"/> 异步切换到 后台线程</remarks>
|
||||
/// <returns>等待体</returns>
|
||||
DispatherQueueSwitchOperation SwitchToMainThreadAsync();
|
||||
DispatcherQueueSwitchOperation SwitchToMainThreadAsync();
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -30,7 +31,7 @@ internal sealed class TaskContext : ITaskContext
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DispatherQueueSwitchOperation SwitchToMainThreadAsync()
|
||||
public DispatcherQueueSwitchOperation SwitchToMainThreadAsync()
|
||||
{
|
||||
return new(dispatcherQueue);
|
||||
}
|
||||
@@ -47,4 +48,9 @@ internal sealed class TaskContext : ITaskContext
|
||||
dispatcherQueue.Invoke(action);
|
||||
}
|
||||
}
|
||||
|
||||
public IProgress<T> CreateProgressForMainThread<T>(Action<T> handler)
|
||||
{
|
||||
return new DispatcherQueueProgress<T>(handler, dispatcherQueueSynchronizationContext);
|
||||
}
|
||||
}
|
||||
@@ -34,4 +34,14 @@ internal sealed class GachaLogFetchStatus
|
||||
/// 当前获取的物品
|
||||
/// </summary>
|
||||
public List<Item> Items { get; set; } = new(20);
|
||||
|
||||
public string Header
|
||||
{
|
||||
get
|
||||
{
|
||||
return AuthKeyTimeout
|
||||
? SH.ViewDialogGachaLogRefreshProgressAuthkeyTimeout
|
||||
: SH.ViewDialogGachaLogRefreshProgressDescription.Format(ConfigType.GetLocalizedDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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">
|
||||
|
||||
<ContentDialog.Resources>
|
||||
<DataTemplate x:Key="GachaItemDataTemplate">
|
||||
<Grid Width="40" Height="40">
|
||||
<shvc:ItemIcon
|
||||
Badge="{Binding Badge}"
|
||||
Icon="{Binding Icon}"
|
||||
Quality="{Binding Quality}"/>
|
||||
</Grid>
|
||||
<shvc:ItemIcon
|
||||
shvh:FrameworkElementHelper.SquareLength="60"
|
||||
Badge="{Binding Badge}"
|
||||
Icon="{Binding Icon}"
|
||||
Quality="{Binding Quality}"/>
|
||||
</DataTemplate>
|
||||
</ContentDialog.Resources>
|
||||
|
||||
<StackPanel>
|
||||
<TextBlock Text="{x:Bind Status.Header, Mode=OneWay}"/>
|
||||
<cwc:HeaderedItemsControl
|
||||
x:Name="GachaItemsPresenter"
|
||||
Padding="0,8,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
ItemTemplate="{StaticResource GachaItemDataTemplate}">
|
||||
ItemTemplate="{StaticResource GachaItemDataTemplate}"
|
||||
ItemsSource="{x:Bind Status.Items, Mode=OneWay}">
|
||||
<cwc:HeaderedItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<cwc:UniformGrid
|
||||
|
||||
@@ -30,30 +30,9 @@ internal sealed partial class GachaLogRefreshProgressDialog : ContentDialog
|
||||
/// <summary>
|
||||
/// 接收进度更新
|
||||
/// </summary>
|
||||
/// <param name="state">状态</param>
|
||||
public void OnReport(GachaLogFetchStatus state)
|
||||
/// <param name="status">状态</param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -160,7 +160,7 @@ internal sealed partial class GachaLogViewModel : Abstraction.ViewModel
|
||||
|
||||
GachaLogRefreshProgressDialog dialog = await contentDialogFactory.CreateInstanceAsync<GachaLogRefreshProgressDialog>().ConfigureAwait(false);
|
||||
ContentDialogHideToken hideToken = await dialog.BlockAsync(taskContext).ConfigureAwait(false);
|
||||
Progress<GachaLogFetchStatus> progress = new(dialog.OnReport);
|
||||
IProgress<GachaLogFetchStatus> progress = taskContext.CreateProgressForMainThread<GachaLogFetchStatus>(dialog.OnReport);
|
||||
bool authkeyValid;
|
||||
|
||||
try
|
||||
|
||||
Reference in New Issue
Block a user