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>
|
/// </summary>
|
||||||
[HighQuality]
|
[HighQuality]
|
||||||
internal readonly struct DispatherQueueSwitchOperation : IAwaitable<DispatherQueueSwitchOperation>, ICriticalAwaiter
|
internal readonly struct DispatcherQueueSwitchOperation : IAwaitable<DispatcherQueueSwitchOperation>, ICriticalAwaiter
|
||||||
{
|
{
|
||||||
private readonly DispatcherQueue dispatherQueue;
|
private readonly DispatcherQueue dispatherQueue;
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ internal readonly struct DispatherQueueSwitchOperation : IAwaitable<DispatherQue
|
|||||||
/// 构造一个新的调度器队列等待器
|
/// 构造一个新的调度器队列等待器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dispatherQueue">调度器队列</param>
|
/// <param name="dispatherQueue">调度器队列</param>
|
||||||
public DispatherQueueSwitchOperation(DispatcherQueue dispatherQueue)
|
public DispatcherQueueSwitchOperation(DispatcherQueue dispatherQueue)
|
||||||
{
|
{
|
||||||
this.dispatherQueue = dispatherQueue;
|
this.dispatherQueue = dispatherQueue;
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ internal readonly struct DispatherQueueSwitchOperation : IAwaitable<DispatherQue
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public DispatherQueueSwitchOperation GetAwaiter()
|
public DispatcherQueueSwitchOperation GetAwaiter()
|
||||||
{
|
{
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,8 @@ namespace Snap.Hutao.Core.Threading;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal interface ITaskContext
|
internal interface ITaskContext
|
||||||
{
|
{
|
||||||
|
IProgress<T> CreateProgressForMainThread<T>(Action<T> handler);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 在主线程上同步等待执行操作
|
/// 在主线程上同步等待执行操作
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -26,5 +28,5 @@ internal interface ITaskContext
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>使用 <see cref="SwitchToBackgroundAsync"/> 异步切换到 后台线程</remarks>
|
/// <remarks>使用 <see cref="SwitchToBackgroundAsync"/> 异步切换到 后台线程</remarks>
|
||||||
/// <returns>等待体</returns>
|
/// <returns>等待体</returns>
|
||||||
DispatherQueueSwitchOperation SwitchToMainThreadAsync();
|
DispatcherQueueSwitchOperation SwitchToMainThreadAsync();
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,7 @@ namespace Snap.Hutao.Core.Threading;
|
|||||||
[Injection(InjectAs.Singleton, typeof(ITaskContext))]
|
[Injection(InjectAs.Singleton, typeof(ITaskContext))]
|
||||||
internal sealed class TaskContext : ITaskContext
|
internal sealed class TaskContext : ITaskContext
|
||||||
{
|
{
|
||||||
|
private readonly DispatcherQueueSynchronizationContext dispatcherQueueSynchronizationContext;
|
||||||
private readonly DispatcherQueue dispatcherQueue;
|
private readonly DispatcherQueue dispatcherQueue;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -19,8 +20,8 @@ internal sealed class TaskContext : ITaskContext
|
|||||||
public TaskContext()
|
public TaskContext()
|
||||||
{
|
{
|
||||||
dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||||
DispatcherQueueSynchronizationContext context = new(dispatcherQueue);
|
dispatcherQueueSynchronizationContext = new(dispatcherQueue);
|
||||||
SynchronizationContext.SetSynchronizationContext(context);
|
SynchronizationContext.SetSynchronizationContext(dispatcherQueueSynchronizationContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -30,7 +31,7 @@ internal sealed class TaskContext : ITaskContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public DispatherQueueSwitchOperation SwitchToMainThreadAsync()
|
public DispatcherQueueSwitchOperation SwitchToMainThreadAsync()
|
||||||
{
|
{
|
||||||
return new(dispatcherQueue);
|
return new(dispatcherQueue);
|
||||||
}
|
}
|
||||||
@@ -47,4 +48,9 @@ internal sealed class TaskContext : ITaskContext
|
|||||||
dispatcherQueue.Invoke(action);
|
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>
|
/// </summary>
|
||||||
public List<Item> Items { get; set; } = new(20);
|
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
xmlns:shcm="using:Snap.Hutao.Control.Markup"
|
||||||
xmlns:shvc="using:Snap.Hutao.View.Control"
|
xmlns:shvc="using:Snap.Hutao.View.Control"
|
||||||
|
xmlns:shvh="using:Snap.Hutao.View.Helper"
|
||||||
Title="{shcm:ResourceString Name=ViewDialogGachaLogRefreshProgressTitle}"
|
Title="{shcm:ResourceString Name=ViewDialogGachaLogRefreshProgressTitle}"
|
||||||
Style="{StaticResource DefaultContentDialogStyle}"
|
Style="{StaticResource DefaultContentDialogStyle}"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<ContentDialog.Resources>
|
<ContentDialog.Resources>
|
||||||
<DataTemplate x:Key="GachaItemDataTemplate">
|
<DataTemplate x:Key="GachaItemDataTemplate">
|
||||||
<Grid Width="40" Height="40">
|
<shvc:ItemIcon
|
||||||
<shvc:ItemIcon
|
shvh:FrameworkElementHelper.SquareLength="60"
|
||||||
Badge="{Binding Badge}"
|
Badge="{Binding Badge}"
|
||||||
Icon="{Binding Icon}"
|
Icon="{Binding Icon}"
|
||||||
Quality="{Binding Quality}"/>
|
Quality="{Binding Quality}"/>
|
||||||
</Grid>
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ContentDialog.Resources>
|
</ContentDialog.Resources>
|
||||||
|
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
|
<TextBlock Text="{x:Bind Status.Header, Mode=OneWay}"/>
|
||||||
<cwc:HeaderedItemsControl
|
<cwc:HeaderedItemsControl
|
||||||
x:Name="GachaItemsPresenter"
|
|
||||||
Padding="0,8,0,0"
|
Padding="0,8,0,0"
|
||||||
HorizontalAlignment="Left"
|
ItemTemplate="{StaticResource GachaItemDataTemplate}"
|
||||||
ItemTemplate="{StaticResource GachaItemDataTemplate}">
|
ItemsSource="{x:Bind Status.Items, Mode=OneWay}">
|
||||||
<cwc:HeaderedItemsControl.ItemsPanel>
|
<cwc:HeaderedItemsControl.ItemsPanel>
|
||||||
<ItemsPanelTemplate>
|
<ItemsPanelTemplate>
|
||||||
<cwc:UniformGrid
|
<cwc:UniformGrid
|
||||||
|
|||||||
@@ -30,30 +30,9 @@ internal sealed partial class GachaLogRefreshProgressDialog : ContentDialog
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 接收进度更新
|
/// 接收进度更新
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">状态</param>
|
/// <param name="status">状态</param>
|
||||||
public void OnReport(GachaLogFetchStatus state)
|
public void OnReport(GachaLogFetchStatus status)
|
||||||
{
|
{
|
||||||
Status = state;
|
Status = status;
|
||||||
|
|
||||||
// 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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,7 +160,7 @@ internal sealed partial class GachaLogViewModel : Abstraction.ViewModel
|
|||||||
|
|
||||||
GachaLogRefreshProgressDialog dialog = await contentDialogFactory.CreateInstanceAsync<GachaLogRefreshProgressDialog>().ConfigureAwait(false);
|
GachaLogRefreshProgressDialog dialog = await contentDialogFactory.CreateInstanceAsync<GachaLogRefreshProgressDialog>().ConfigureAwait(false);
|
||||||
ContentDialogHideToken hideToken = await dialog.BlockAsync(taskContext).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;
|
bool authkeyValid;
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|||||||
Reference in New Issue
Block a user