Вызовите это. StateHasChanged в EventHandler - PullRequest
0 голосов
/ 20 апреля 2020

У меня следующая проблема. Я создал событие и подписался на него, теперь я хочу, чтобы пользовательский интерфейс изменялся при срабатывании события.

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

}

Я получаю эту ошибку. Инициализатор поля не может ссылаться на нестатическое c поле, метод, или свойство 'AppHeader.patientContext'

Ключевое слово "this" недоступно в текущем контексте

Как подписаться на событие и обновить пользовательский интерфейс

1 Ответ

3 голосов
/ 20 апреля 2020

К этому нужно подходить немного по-другому, так как тип EventHandler<T> здесь не работает, как ожидалось. (По крайней мере, не для меня)

Прежде всего, для EventArgs, помните, что это тип, поэтому вы не можете присвоить их свойству Status (которое у вас есть в виде строки) без актерского состава. Способ сделать это - определить свой собственный тип аргументов, производный от EventArgs, что-то вроде этого:

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

Далее для метода-обработчика, который вам нужно использовать, установите его как asyn c метод. Я обнаружил, что асин c был важен, так что вы можете использовать InvokeAsync дальше и избежать исключения, когда поток и диспетчер не согласны, как в других открытых windows или других пользователях, вошедших в другое место, через это post: Обсуждение потока против контекста синхронизации

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

Далее и важно для вашего сценария, вы попадете в стену с объявление Частичного класса в том виде, в каком оно у вас есть, поскольку вам нужно реализовать 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;
    } 

}

В этом используются некоторые встроенные функции Blazor в OwningComponentBase и включает метод Dispose, в то же время выполняя намного лучшую работу по управлению инъекцией зависимостей для вас.

Дополнительная информация ЗДЕСЬ (Обратите внимание, что я не go слишком глубоко для этого примера, так как он использует синглтон, но стоит прочитать, чтобы понять время жизни DI в Blazor)

А потом в вашем 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>

Это должно обеспечить многоадресную передачу вашего события по мере необходимости, и все подписчики должны забрать его и обновить.

Вот быстро работающий дем o при необходимости , адаптированный из другой версии, над которой я работал.

...