在EventHandler中调用this.StateHasChanged [英] Call this.StateHasChanged in EventHandler
问题描述
我有以下问题。
我创建了一个事件并订阅了它,现在我希望事件触发时UI会更改。
I have the following problem. I created an event and subscribe to it, now I want that the UI changes when the Event triggers.
using System;
using MintWebApp.Data;
using MintWebApp.Models;
using Microsoft.AspNetCore.Components;
namespace WebApp.UI.Core
{
public partial class AppHeader
{
public string status { get; set; }
[Inject]
public StateService state { get; set; }
EventHandler<string> onStatusChanged= (sender, eventArgs) => {
//Here i get the error, I can't access this and status
status = eventArgs;
this.StateHasChanged();
Console.WriteLine(eventArgs.PatientName);
};
protected override void OnInitialized() => state.StatusHandler += onStatusChanged;
}
}
我收到此错误
字段初始值设定项不能引用非静态字段,方法或属性'AppHeader。PatientContext'
I get this Error A field initializer cannot reference the non-static field, method, or property 'AppHeader.patientContext'
关键字 this在当前上下文中不可用
Keyword 'this' is not available in the current context
如何订阅事件并更新UI
How can I subscripe to an event and update the UI
推荐答案
由于 EventHandler< T>
类型不能按此处预期的那样工作,因此处理方法有所不同。 (至少不适合我)
This needs to be approached a bit differently as the EventHandler<T>
type doesn't work as expected here. (At Least not for me)
首先,对于 EventArgs
,请记住,这是一种类型,因此如果不进行强制转换,则无法将它们分配给 Status
属性(作为字符串包含)。做到这一点的方法是定义您自己的从EventArgs派生的参数类型,如下所示:
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;}
}
接下来需要使用的处理程序方法进行设置作为异步方法。我发现异步很重要,因此可以在更远的地方使用 InvokeAsync
,避免在线程和调度程序不同意时发生异常,例如在其他打开的窗口或其他用户中通过这篇文章在其他地方登录:
讨论线程与同步上下文
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());
}
下一步, 重要 到您的情况,您将遇到Partial Class声明,因为您需要实现 IDisposable
来清理,因为组件被拆除后需要清理。而是使用如下继承结构,并使用OnInitialized和Dispose替代
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;
}
}
Blazor在 OwningComponentBase
中具有功能,并包括 Dispose
方法,同时为您更好地管理依赖注入。
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.
进一步阅读这里(请注意,在此示例中,我并未对此进行深入研究一个单例,但值得一读,以了解Blazor的DI生命)
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)
然后在您的 AppHeader.razor
....
@inherits AppHeaderBase
....
现在在 StateService $ c中使用事件处理程序时$ c>从其他地方建立一个新的
PatientEventArgs
类型wi您需要传递的值:
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);
或直接从Razor语法中获取:
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.
如果需要的话,这是一个快速工作的演示</a>,它是我正在研究的另一个版本的改编。
Here is a quick working demo if needed, adapted from another version of this I've been working on.
这篇关于在EventHandler中调用this.StateHasChanged的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!