Search code examples
c#blazormauiinotifypropertychangedpropertychanged

OnPropertyChanged not working correctly in Blazor (MAUI Hybrid)


I've registered a service in MauiProgram.cs:

builder.Services.AddSingleton<NetworkStatusService>();

the service class:

public class NetworkStatusService : INotifyPropertyChanged
{
    private NetwStatus _netwStat;
    public NetwStatus NetwStat
    {
        get => _netwStat;
        set
        {
            if (_netwStat != value)
            {
                _netwStat = value;
                OnPropertyChanged(nameof(NetwStat));
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public async Task CheckNetw()
    {
        NetwStat = NetwStatus.Loading;

        await Task.Delay(1000);

        if (await SomeClass.CheckDBConnection())
            NetwStat = NetwStatus.Success;
        else
            NetwStat = NetwStatus.Fail;
    }
}

my simplified blazor page would be as below:

@page "/LoginComponent"
<div class="Grid">
    @switch (NetworkStatusService.NetwStat)
    {
        case NetwStatus.Loading:
            <h6 style="font-size:0.8rem; margin-bottom:-12px; color:coral">Trying to connect...</h6>
            break;
        case NetwStatus.Success:
            <h6 style="font-size:0.8rem; margin-bottom:-12px; color:green">Connected uccessfully.</h6>
            break;
        case NetwStatus.Fail:
            <h6 style="font-size:0.8rem; margin-bottom:-12px; color:red">Connection failed.</h6>
            break;
        default:
            break;
    }    ‌ ‌
    <i @onclick="ViewModel.btnNetwOkCommand.ExecuteAsync">refresh</i>
</div>

the command would obviously run NetworkStatusService.CheckNetw()

but it NEVER changes to the "Trying to connect..." mode. it just waits and changes to either success or fail. It's super frustrating, I'm starting to hate my career.


Solution

  • You need to wire the component up to the NetworkStatusService event like this:

    @page "/"
    @using System.ComponentModel
    @inject NetworkStatusService NetworkStatusService
    @implements IDisposable
    
    <PageTitle>Home</PageTitle>
    
    <h1>Hello, world!</h1>
    
    Welcome to your new app.
    
    <div class="m-2">
        @switch (NetworkStatusService.NetwStat)
        {
            case NetwStatus.Loading:
                <h6 class="alert alert-warning" >Trying to connect...</h6>
                break;
            case NetwStatus.Success:
                <h6 class="alert alert-success">Connected uccessfully.</h6>
                break;
            case NetwStatus.Fail:
                <h6 class="alert alert-danger">Connection failed.</h6>
                break;
            default:
                break;
        }    ‌ ‌
        <button class="btn btn-primary" @onclick="ExecuteAsync">refresh</button>
    </div>
    
    @code {
        protected override void OnInitialized()
        {
            this.NetworkStatusService.PropertyChanged += OnPropertyChanged;
        }
    
        private void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
        {
            this.InvokeAsync(this.StateHasChanged);
        }
    
        private async Task ExecuteAsync()
        {
            await NetworkStatusService.CheckNetw();
        }
    
        public void Dispose()
        {
            this.NetworkStatusService.PropertyChanged -= OnPropertyChanged;
        }
    }
    

    You could also write your service like this:

    public class NetworkStatusService
    {
        public NetwStatus NetwStat { get; private set; }
    
        public event EventHandler? NetworkStatusChanged;
    
        protected virtual void NotifyStatusChanged()
        {
            NetworkStatusChanged?.Invoke(this, EventArgs.Empty);
        }
    
        public async Task CheckNetw()
        {
            this.NetwStat = NetwStatus.Loading;
            this.NotifyStatusChanged();
    
            await Task.Delay(1000);
    
            this.NetwStat = NetwStatus.Fail;
            this.NotifyStatusChanged();
        }
    }