Blazor UI未在StateHasChanged调用上更新



我有一个来自标准Blazor服务器端模板的非常简单的例子,它显示计时器函数即使在StateHasChanged((之后也不会更新UI;已拨打电话。

日志输出显示触发的timmer,如果我等待几秒钟并单击IncrementCount按钮,计数值会跳到计时器增加计数器的次数。

非常好奇。。。如有任何帮助,将不胜感激

致以亲切的问候,斯图尔特·

@page "/counter"
@using System.Timers;
@using Microsoft.Extensions.Logging
@inject ILogger<Counter> Logger
<h1>Counter</h1>
<p>Current count: @(currentCount.ToString())</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
public System.Timers.Timer timer;
protected override async Task OnInitializedAsync()
{
timer = new Timer(1000);
timer.Elapsed += this.OnTimedEvent;
timer.AutoReset = true;
timer.Enabled = true;
timer.Start();
}
public void OnTimedEvent(Object source, ElapsedEventArgs e)
{
Logger.LogInformation("Timer triggered");
IncrementCount();
StateHasChanged();
}
}

您正在运行Blazor服务器应用程序,对吗?在这种情况下,您应该从ComponentBase的InvokeAsync方法中调用StateHasChanged方法,如下所示:

InvokeAsync(() => StateHasChanged());

我想发生这种情况是因为计时器是在不同于UI线程的线程上执行的,这需要同步所涉及的线程。在Blazor WebAssembly上,这种行为不太可能发生,因为所有代码都在同一个UI线程上执行。

希望这能帮助。。。

Timer事件可以在后台线程上执行。当您的代码没有在正常的生命周期事件中运行时,请使用

InvokeAsync(StateHasChanged);  // no () => ()  required. 

此外,Timer类是IDisposable。因此添加:

@implements IDisposable
...
@code
{
...
public void Dispose()
{
timer?.Dispose();
}
}

说明:

计时器事件处理程序不应直接调用StatehasChanged()。计时器事件是在默认(null(同步上下文上运行的池线程上处理的。

当您调用StatehasChanged((时,它将启动一个Render。渲染操作将调用Dispatcher.AssertAccess();

AssertAccess((的代码是

if (!CheckAccess()) throw new InvalidOperationException(...);

WebAssembly使用过载

public override bool CheckAccess() => true;

因此,在WebAssembly中,错误不会被注意到,但它仍然是一个错误。当WebAssembly将来获取线程时,此代码可能会开始失败。

对于服务器端,我们有

public override bool CheckAccess() => SynchronizationContext.Current == _context;

在服务器应用程序中,OP应该得到一个异常。也许记录器与没有发生的事情有关,我没有检查。

相关内容

  • 没有找到相关文章

最新更新