1

I just changed the default Blazor sample to illustrate this issue.

MainLayout.razor (This is the top of the page, just replaced the About to display the counter)

@inherits LayoutComponentBase
@inject TestClass testClass
<div class="sidebar">
   <NavMenu />
</div>

<div class="main">
  <div class="top-row px-4">
    Counter: @testClass.Counter
</div>

<div class="content px-4">
    @Body
</div>

Counter.razor (This one was modified to instead use the local variable, invoke the method to increment via class and display the value from the class)

@page "/counter"
@inject TestClass testClass
<h1>Counter</h1>

<p>Current count:@testClass.Counter</p>

<button class="btn btn-primary" @onclick="(e => testClass.Increment())">Click me</button>

@code {

}

TestClass.cs (This one is the class with the current value and method to increment)

using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks;

namespace TestBlazor.Classes
{
   public class TestClass
    {
     public int Counter { get; set; } = 10;
     public void Increment() {
        Counter++;
     }
 }
}

This is the result after few presses on the increment button:

enter image description here

So, the page itself can display the current value every time it's changes, however the component on the top of the page cannot. It's just displays the default value of 10.

This is also my Program.cs

using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using TestBlazor.Classes;

namespace TestBlazor
{
  public class Program
  {
    public static async Task Main(string[] args)
    {
        var builder = WebAssemblyHostBuilder.CreateDefault(args);
        builder.RootComponents.Add<App>("app");

        builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
        builder.Services.AddSingleton<TestClass>();

        await builder.Build().RunAsync();
    }
  }
}

Using Core5

1 Answer 1

4

You'll need to notify the MainLayout component that its state has changed, and that he should re-render. This is doable by defining an event delegate that is executed whenever the counter variable is incremented:

TestClass.cs

public class TestClass
    {
        public int Counter { get; set; } = 10;
        public void Increment()
        {
            Counter++;
            Notify?.Invoke();
        }
        public event Action Notify;
    }

Add this code to the MainLayout page

@code{
    protected override void OnInitialized()
    {
        testClass.Notify += () => InvokeAsync(StateHasChanged);

        base.OnInitialized();
    }
}

You'll also have to implement IDisposable

Sign up to request clarification or add additional context in comments.

4 Comments

can't we just use binding since TestClass is a singleton ?
It has nothing to do with the scope of the service, and this is a one-way binding, from a service to a component, but you need to notify MainLaout that she should re-render. In other example you can create a two-way binding, or can define in TestClass an event that has data you can pass to subscribers.
Thanks ! Yes, it does the trick. I read this solution in somewhere else, but I thought it could have a more direct approach. Thanks anyway !

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.