This needs to be approached a bit differently as the EventHandler<T>
type doesn't work as expected here. (At Least not for me)
First off, for the EventArgs
, remember that this is a type, so you can't assign them to the Status
property (which you have as a string) without a cast. The way to do this is to define your own arguments type that derives from EventArgs, something like this:
public class PatientEventArgs: EventArgs
{
public string PatientName {get; set;}
public string StatusValue {get; set;}
}
Next for the handler method that you need to use, set it up as an async method. I found that the async was important so you can use an InvokeAsync
farther down and avoid an exception when the thread and dispatcher don't agree, as in other windows open or other users signed in elsewhere, through this post:
Discussion on thread vs. Synchronization Context
private async void OnStatusChanged(object sender, EventArgs e) {
// Make sure the args are the type you are expecting
if(e.GetType() == typeof(PatientEventArgs))
//Cast to the correct Args type to access properties
var patientStatus = e as PatientEvendArgs;
status = patientStatus.StatusValue;
Console.Writeline(patientStatus.PatientName);
/* Use InvokeAsync method with await to make sure
StateHasChanged runs correctly here without interfering with another
thread (open window or other users) */
await InvokeAsync(() => StateHasChanged());
}
Next, and important to your scenario, you will hit a wall with the Partial Class declaration as you have it since you need to implement IDisposable
to clean up after yourself as the component tears down. Instead, use an inheritance structure as follows and use the OnInitialized and Dispose overrides
AppHeader.razor.cs
public class AppHeaderBase : OwningComponentBase
{
// OnStatusChanged method as described above
protected override void OnInitialized() //Can use the Async version as well
{
// Unsubscribe once to make sure you are only connected once
// Prevents event propogation
// If this component is not subscribed this doesn't do anything
state.StatusHandler -= onStatusChanged;
// Subscribe to the event
state.StatusHandler += onStatusChanged;
}
protected override void Dispose(bool disposing)
{
// Unsubscribe on teardown, prevent event propogation and memory leaks
state.StatusHandler -= onStatusChanged;
}
}
This takes advantage of some built in Blazor features in OwningComponentBase
and includes a Dispose
Method, while doing a much better job of managing your Dependency Injection for you.
Further reading HERE (Note that I didn't go too deep on this for this example as it's using a singleton, but worth the reading to understand DI lifetimes in Blazor)
And then in your AppHeader.razor
....
@inherits AppHeaderBase
....
Now when you use the event handler in the StateService
from somewhere else, build up a new PatientEventArgs
type with the values you need to pass:
var newArgs = new PatientEventArgs(){
PatientName = "SomeName",
StatusValue = "SomeStatus"
};
And pass it in as needed in your code:
state.OnStatusChanged(this, newArgs);
Or direct from Razor syntax:
<button @onclick="@(() => state.OnStatusChanged(this, new PatientEventArgs(){ PatientName = "SomeName", StatusValue = "SomeStatus"})">Sender Button</button>
This should multicast your event out as needed, and all subscribers should pick it up and update.
Here is a quick working demo if needed, adapted from another version of this I've been working on.