在 WinForms .NET 8 表单中使用 BlazorWebView 控件(简介),我确实显示了这个“Counter.razor”文件:
<p><button @onclick="IncrementCount">Increment counter</button></p>
<p>@_counterValue</p>
@code
{
private int _counterValue = 0;
private void IncrementCount()
{
_counterValue++;
}
}
上面的代码完美运行:
现在我将
IncrementCount()
方法更改为:
private void IncrementCount()
{
SynchronizationContext.Current!.Post(
delegate
{
_counterValue++;
},
null);
}
现在它的行为是这样的:
即我总是有一个相差一的错误。
要解决此问题,我必须将代码更改为:
private void IncrementCount()
{
SynchronizationContext.Current!.Post(
delegate
{
_counterValue++;
StateHasChanged();
},
null);
}
现在,添加对
StateHasChanged()
的调用后,它的行为符合预期:
上面
SynchronizationContext
的用法是一个大型应用程序的最小示例,我必须通过这种机制调度调用(主要是因为我必须调用其他WinForms对话框)。
我确实想了解为什么需要调用“StateHasChanged”,因为我想避免它。
在我的实际应用程序中(不是上面的最小示例),这将强制进行大量重新渲染以适合应用程序。
有人可以向我解释一下为什么我直接更改变量与通过
SynchronizationContext
更改变量的行为不同吗?
是否有更改可以使其工作而无需额外调用
StateHasChanged()
?
在我的现实世界应用程序中,我大致执行以下步骤:
所以这就是我在
SynchronizationContext.Current!.Post()
调用中执行代码的原因,如建议的 here。
我还尝试将我的代码放入队列中并在
Application.Idle
事件处理程序中处理该队列,但这行为完全相同(相差一错误)。
IncrementCounter
由渲染器的 UI 事件进程发布到 同步上下文。它在 callback
UI 处理程序中作为 ComponentBase
传递。
Task IHandleEvent.HandleEventAsync(EventCallbackWorkItem callback, object? arg)
{
var task = callback.InvokeAsync(arg);
var shouldAwaitTask = task.Status != TaskStatus.RanToCompletion &&
task.Status != TaskStatus.Canceled;
// After each event, we synchronously re-render (unless !ShouldRender())
// This just saves the developer the trouble of putting "StateHasChanged();"
// at the end of every event callback.
StateHasChanged();
return shouldAwaitTask ?
CallStateHasChangedOnAsyncCompletion(task) :
Task.CompletedTask;
}
所以你正在做的就是发布这个
{
_counterValue++;
}
作为代码块进入IncrementCounter
后面的
同步上下文队列。
IncrementCounter
在执行代码之前完成所有渲染并改变 _counterValue
。
当直接更改时,突变会在 UI 处理程序中调用
StateHasChanged
之前发生。
问题:为什么要直接将代码发布到同步上下文?
ComponentBase
具有内置函数InvokeAsync
来调用必须在同步上下文上执行的任何代码,请参阅 - https://github.com/dotnet/aspnetcore/blob/63c8031b6a6af5009b3c5bb4291fcc4c32b06b10/src/Components/Components/ src/ComponentBase.cs#L178
注:
我现在从来没有编写过这个代码:
private void IncrementCount()
{ }
我总是这样做:
private Task IncrementCount()
{
//work
return Task.CompletedTask;
}
它可以防止这种情况发生:
private async void IncrementCount()
{ }