在EventHandler中调用this.StateHasChanged [英] Call this.StateHasChanged in EventHandler

查看:225
本文介绍了在EventHandler中调用this.StateHasChanged的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下问题。
我创建了一个事件并订阅了它,现在我希望事件触发时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 从其他地方建立一个新的 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆