3
votes

I have an extremely simple example from the standard Blazor server-side template that shows that a timer function will not update the UI even after a StateHasChanged(); call has been made.

The log output shows the timmer being triggered and if I wait a few seconds and click the IncrementCount button the count value jumps to the number of times the counter has been incremented by the timer.

Very curious ... any help would be greatly appreciated

Kind regards, Stuart

@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();
    }
}
1
No repro. Your code works as intended on my PC. - Henk Holterman
But your text, tags and results are in conflict. On Blazor/Wasm this works. On Blazor/Server this should throw an exception. - Henk Holterman
I am using server-side Blazor. Changing StateHasChanged(); to InvokeAsync(() => StateHasChanged()); makes this work. Can you point me in the direction to the documentation on this, I can't seem to find any reference to it? Why should this fail on Blazor/Server? Many thanks for your help with this. - Stuart Barnaby
It ought to throw an exception (on Server). And you tagged client-side (fixed). There is no configuration where it would just 'do nothing'. So you must be doing something else that was not posted. - Henk Holterman
And why should it throw an exception ( on the server ). This is a baseless assertion which leads you to a false conclusion that the OP "must be doing something else that was not posted." - enet

1 Answers

8
votes

You are running Blazor Server App, right ? In that case you should call the StateHasChanged method from within the ComponentBase's InvokeAsync method as follows:

InvokeAsync(() => StateHasChanged());

I guess this occurs because the timer is executed on a different thread than the UI thread, which requires synchronization of the threads involved. On Blazor WebAssembly this behavior is not likely to happen as all code is executed on the same UI thread.

Hope this helps...