【问题标题】:Call this.StateHasChanged in EventHandler在 EventHandler 中调用 this.StateHasChanged
【发布时间】:2020-08-03 00:40:47
【问题描述】:

我有以下问题。 我创建了一个事件并订阅了它,现在我希望在事件触发时 UI 会发生变化。

 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”

关键字“this”在当前上下文中不可用

如何订阅事件并更新 UI

【问题讨论】:

  • 我缺少部分范围,我看不到这个部分的其他部分。也许 StateHasChanged 是一个静态方法?你能把代码也给我们吗?

标签: c# razor .net-core blazor blazor-server-side


【解决方案1】:

这需要以不同的方式处理,因为EventHandler&lt;T&gt; 类型在这里不能按预期工作。 (至少不适合我)

首先,对于EventArgs,请记住这是一个类型,因此您不能在没有强制转换的情况下将它们分配给Status 属性(作为字符串)。这样做的方法是定义您自己的从 EventArgs 派生的参数类型,如下所示:

public class PatientEventArgs: EventArgs
{
    public string PatientName {get; set;}
    public string StatusValue {get; set;}
}

接下来对于您需要使用的处理程序方法,将其设置为异步方法。我发现异步很重要,因此您可以更远地使用InvokeAsync,并避免在线程和调度程序不同意时出现异常,例如在其他窗口打开或其他用户在其他地方登录时,通过这篇文章: 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 覆盖

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;
    } 

}

这利用了 OwningComponentBase 中的一些内置 Blazor 功能并包括 Dispose 方法,同时更好地为您管理依赖注入。

进一步阅读 HERE(请注意,我没有对此示例进行深入探讨,因为它使用的是单例,但值得一读以了解 Blazor 中的 DI 生命周期)

然后在你的 AppHeader.razor

....

@inherits AppHeaderBase

....

现在,当您在其他地方使用 StateService 中的事件处理程序时,使用您需要传递的值构建一个新的 PatientEventArgs 类型:

var newArgs = new PatientEventArgs(){
    PatientName = "SomeName",
    StatusValue = "SomeStatus"  
};

并根据需要在代码中传递:

state.OnStatusChanged(this, newArgs);  

或直接来自 Razor 语法:

<button @onclick="@(() => state.OnStatusChanged(this, new PatientEventArgs(){ PatientName = "SomeName", StatusValue = "SomeStatus"})">Sender Button</button>

这应该会根据需要多播您的事件,并且所有订阅者都应该选择并更新它。

Here is a quick working demo if needed,改编自我一直在研究的另一个版本。

【讨论】:

  • 哇,太棒了。谢谢您的帮助!!它有效
  • await InvokeAsync(StateHasChanged) 和您调用await InvokeAsync(()=&gt;StateHasChaged()) 的方式在执行方面有什么区别?还是结果?
  • @Moritz 很高兴我能帮上忙。
  • @LucianBumb 你的方式似乎工作得很好,我刚刚启动了那个演示并搞砸了它。我认为关键是我在上面链接到的线程上 bmiller 的回答中提到的,“InvokeAsync 是继承的,它将调用同步回渲染线程”,因此将它与 StateHasChanged 一起使用可以让你回到处理 DOM 的线程上然后在正确的位置更新 DOM,因此也不例外。我对 Synchro 上下文了解得不够多,不知道哪个对性能更好,所以在执行方面我不能说。
  • 为什么要这么努力? *.razor.cs 文件顶部的简单 @implements IDisposable 也可以。
猜你喜欢
  • 2011-11-17
  • 1970-01-01
  • 2012-02-22
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多