В настоящее время я пишу небольшой веб-интерфейс Blazor.Я пытаюсь принять / реализовать шаблон MVVM.Для этого я создал два компонента базовых классов.Один, который просто обрабатывает методы жизненного цикла Blazor (добавляет некоторую обработку исключений), а другой - инициализацию ViewModel на основе выполнения этих методов жизненного цикла.Компонент также реализует интерфейс IDisposable, который автоматически вызывается Blazor, когда компонент становится невидимым.
Вот фрагмент кода из моего класса WebComponentBase и класса ViewModelAwareComponent, чтобы примерно дать представление о шаблоне:
public abstract class WebFpComponentBase : ComponentBase, IDisposable, IHtmlStyles
{
private const string DEFAULT_DIM_VALUE = "auto";
[Inject]
protected IExceptionUiHandler<ErrorRedirectViewModel> ExceptionUiHandler { get; set; }
//fields and parameters omitted for brevity
#region Blazor Component LifeCycle
/// <summary>
/// OnInitialized is called after OnInitialized, when the component has received all initial parameters. Place any asynchronous operations here,
/// which require the component to re-render.
/// </summary>
/// <returns></returns>
protected override async Task OnInitializedAsync()
{
try
{
await base.OnInitializedAsync();
_logger.Info($"{nameof(OnInitializedAsync)} - method invoked in component of type ${this.GetType().FullName}");
await OnInitializedInternalAsync();
_logger.Info($"{nameof(OnInitializedAsync)} - method finished in component of type ${this.GetType().FullName}");
} catch(Exception ex)
{
//Exception, if any happend, is forwared using the IExceptionUiHandler in the AfterRenderAsync() method
_forwardException = ex;
_logger.Error($"{nameof(OnInitializedAsync)}: Catching and forwarding exception of type {_forwardException.GetType().FullName}");
}
}
protected abstract Task OnInitializedInternalAsync();
//other methods of the class omitted for brevity
}
Далее мой ViewModelAwareComponent, который имеет свойство, содержащее ViewModelи автоматически запускает инициализацию и деинициализацию ViewModel (закрытие любых подключений к службам, сброс любых значений и т. д.) путем реализации внутренних абстрактных методов [BlazorLifecycleMethod].
public abstract class ViewModelAwareComponent<TViewModel> : WebFpComponentBase where TViewModel : BaseViewModel
{
private Logger _logger = LogManager.GetCurrentClassLogger();
[Parameter]
public virtual TViewModel ViewModel { get; set; }
protected override async Task OnInitializedInternalAsync()
{
await ViewModel.InitializeAsync();
ViewModel.PropertyChanged += this.FireComponentStateHasChanged;
}
public override async void Dispose()
{
base.Dispose();
await ViewModel.DeactivateAsync();
}
protected virtual async void FireComponentStateHasChanged(object sender, PropertyChangedEventArgs e)
{
_logger.Trace($"FireComponentStateHasChanged: property {e.PropertyName} has changed!");
await InvokeAsync(this.StateHasChanged);
}
protected override async Task OnParametersSetAsyncInternal()
{
if (ViewModel == null)
{
throw new ArgumentNullException($"{nameof(ViewModel)}", "Parameter must be supplied!");
}
}
}
Тип BaseViewModel реализует INotifyPropertyChanged только в обычном виде,У меня есть «MainViewModel», который должен быть создан только один раз для всего соединения (Blazor Circuit).Поэтому я добавил его через services.AddScoped()
в Startup.cs
.Поскольку он не привязан к какому-либо конкретному компоненту, я вставляю его в мой MainLayout.razor
, который является макетом для каждого компонента Razor, который я написал.
Макет действительно реализует protected override async Task OnInitializedAsync()
, как указано ниже:
protected override async Task OnInitializedAsync()
{
MainViewModel.PropertyChanged += (obj, args) => InvokeAsync(StateHasChanged);
await MainViewModel.InitializeAsync();
//some other stuff happening afterwards
}
Моя проблема сейчас заключается в том, что инициализация и выполняется дважды каждый раз, когда я запускаю приложение, а не только один раз для каждого соединения.То же самое верно и для деинициализации, потому что Dispose () компонента вызывается также дважды.
При отладке я заметил, что OnInitializedAsync
не вызывается повторно при переключении между двумя моими существующими страницами(маршрутизируемые компоненты).Он вызывается только дважды при запуске.
Возможно, у вас есть какие-то предложения для такого поведения?Есть ли лучший способ добиться желаемого поведения для MainViewModel?
С наилучшими пожеланиями,
tilt