Search code examples
.netwebeventsevent-handlingblazor

Web event handling in multiple windows (Blazor)


I'm just starting out on the web and this time I'm having trouble with event handling. I develop with blazor, but this seems like a universally applicable problem.

Where I'm stuck is this. I want to notify the page when I get a message. I had no problem with this when I had one browser screen, but when I test my web with two screens open, the events don't work properly. how do I fix this?

Below is my code.

ServiceInfoReceiveHandler.cs
public event EventHandler<(string contentType, string serverName, string message)> InstallServiceInfoReceived;
...
public void Receive(BasicDeliverEventArgs eventArgs)
{
  if (contentType == "SVCMGMT/INSTALL/SVCINFO")
  {
    InstallServiceInfoReceived?.Invoke(this, (contentType, serverName, message));
    ...
  }
}

I fire the event like this and the receive function is always called whenever a message comes in. It's not a matter of the message not arriving.

page.razor

@inject ServiceInfoReceiveHandler _serviceInfoReceiveHandler

protected override async Task OnInitializedAsync()
{ 
  _serviceInfoReceiveHandler.InstallServiceInfoReceived += OnServiceInfoReceived;
}
private async void OnServiceInfoReceived(object sender, (string contentType, string serverName, string message) e)
{
...
}

The razor page I want the result of is like above, but OnInfoReceived is not called.


Solution

  • Here's a simple demonstration of the Blazor Notification pattern used to communicate between pages with events.

    First a notification service.

    namespace SO76170542.Data;
    
    public class HelloService
    {
        public event EventHandler<HelloEventArgs>? HelloChanged;
    
        private string? _hello;
    
        public string HelloMessage => _hello ?? "No Message Set";
    
        public void NotifyHelloChanged(string? hello)
        {
            _hello = hello;
            this.HelloChanged?.Invoke(this, new(hello));
        }
    }
    
    public class HelloEventArgs : EventArgs
    {
        public string? Hello { get; set; }
    
        public HelloEventArgs(string? hello)
            => Hello = hello;
    }
    

    Registered in Program.

    // Add services to the container.
    builder.Services.AddRazorPages();
    builder.Services.AddServerSideBlazor();
    builder.Services.AddSingleton<WeatherForecastService>();
    builder.Services.AddSingleton<HelloService>();
    
    var app = builder.Build();
    

    And a page to generate and consume messages.

    @page "/"
    @inject HelloService helloService
    
    <PageTitle>Index</PageTitle>
    
    <h1>Hello, world!</h1>
    
    Welcome to your new app.
    
    <div class="m-2">
        <button class="btn btn-dark" @onclick=this.UpdateHello>Update Hello</button>
    </div>
    
    <div class="alert alert-primary">@helloService.HelloMessage</div>
    
    @code {
    
        // Register the event handler
        protected override void OnInitialized()
            => this.helloService.HelloChanged += this.OnHelloChanged;
    
        private Task UpdateHello()
        {
            helloService.NotifyHelloChanged($"Hello Blazor at {DateTime.Now.ToLongTimeString()}");
            return Task.CompletedTask;
        }
    
        public void OnHelloChanged(object? sender, HelloEventArgs e)
            => InvokeAsync(this.StateHasChanged);
    
        // We must de-register the even handler
        public void Dispose()
            => this.helloService.HelloChanged -= this.OnHelloChanged;
    }
    

    You can see the same pattern applied to FetchData here: How can I trigger/refresh my main .RAZOR page from all of its sub-components within that main .RAZOR page when an API call is complete?

    enter image description here