ViewModel赢得了EventAggregator事件的处理

发布于 2025-01-17 10:28:49 字数 3592 浏览 3 评论 0原文

我似乎只能从 ShellViewModel 处理 EventAggregator 事件,但我想从 LoginViewModel 处理它。

ShellViewModel 构造 LoginViewModel 作为活动项。我还将其设置为从 IHandle 继承,作为事件发布是否正常工作的测试。它能够处理该事件。为了简洁起见,我没有在代码中显示任何取消订阅事件。

public class ShellViewModel : Conductor<IScreen>.Collection.OneActive, IHandle<AuthenticatedMessage>    
{
    public ShellViewModel(IEventAggregator eventAggregator, LoginViewModel loginViewModel)
    {
        _eventAggregator = eventAggregator;
        _loginViewModel = loginViewModel;
    }
    
    protected override async Task OnActivateAsync(CancellationToken cancellationToken)
    {
        await base.OnActivateAsync(cancellationToken);
        _eventAggregator.SubscribeOnPublishedThread(this);
    
        await ActivateItemAsync(_loginViewModel);
    }
    
    public async Task HandleAsync(AuthenticatedMessage authCode, CancellationToken cancellationToken)
    {
        // this is reached! So the event is publishing successfully.
        await Task.CompletedTask;
    }
}

LoginViewModel 也订阅了这个事件,但它的 Handle 方法没有被调用。

Login 方法负责创建发布事件的 LoginWindowViewModel 窗口(如下所示)。

public class LoginViewModel : Screen, IHandle<AuthenticatedMessage>
{
    private IEventAggregator _eventAggregator;
    private readonly IWindowManager _windowManager;
    private readonly ILoginWindowViewModelFactory _LoginWindowViewModelFactory;

    public LoginViewModel(IEventAggregator eventAggregator, 
        IWindowManager windowManager, 
        ILoginWindowViewModelFactory loginWindowViewModelFactory)
    {
        _eventAggregator = eventAggregator; 
        _windowManager = windowManager;
        _LoginWindowViewModelFactory = loginWindowViewModelFactory;
    }
    
    protected override async Task OnActivateAsync(CancellationToken cancellationToken)
    {
        await base.OnActivateAsync(cancellationToken);
        
        _eventAggregator.SubscribeOnPublishedThread(this);
    }
    
    // This is bound to a button click event. It creates a window.
    public async void Login()
    {
        Uri loginUri = new Uri(_api.BaseLoginUrl);

        await _windowManager.ShowWindowAsync(
            _ndLoginWindowViewModelFactory.Create(loginUri, _eventAggregator));
    }       
    
    
    public async Task HandleAsync(AuthenticatedMessage authCode, CancellationToken cancellationToken)
    {
        // why is this is never reached?
        await Task.CompletedTask;
    }
}

发布 AuthenticatedMessage 事件的 LoginWindowViewModel

public class LoginWindowViewModel : Screen
{
    private readonly IEventAggregator _eventAggregator;
    private readonly Uri _initialUri;         

    public NDLoginWindowViewModel(Uri initialUri, IEventAggregator eventAggregator)
    {
        _initialUri = initialUri;
        _eventAggregator = eventAggregator;
    }       
    
    // bound to the WebView2 (browser control) event
    public async void NavigationStarting(object sender, CoreWebView2NavigationStartingEventArgs e)
    {            
            string authCode = HttpUtility.ParseQueryString(new Uri(e.Uri).Query).Get("code");
            
            // Publish event here. LoginViewModel should handle this, but currently only ShellViewModel can.
            await _eventAggregator.PublishOnUIThreadAsync(new AuthenticatedMessage(authCode));                              
        }
    }
}

I only seem to be able to handle EventAggregator events from the ShellViewModel, but I want to handle it from LoginViewModel.

The ShellViewModel constructs LoginViewModel as it's Active Item. I've also set it up to inherit from IHandle<AuthenticatedMessage> as a test that event publishing is working. It is able to handle that event. I haven't shown any Unsubscribe events in my code for brevity.

public class ShellViewModel : Conductor<IScreen>.Collection.OneActive, IHandle<AuthenticatedMessage>    
{
    public ShellViewModel(IEventAggregator eventAggregator, LoginViewModel loginViewModel)
    {
        _eventAggregator = eventAggregator;
        _loginViewModel = loginViewModel;
    }
    
    protected override async Task OnActivateAsync(CancellationToken cancellationToken)
    {
        await base.OnActivateAsync(cancellationToken);
        _eventAggregator.SubscribeOnPublishedThread(this);
    
        await ActivateItemAsync(_loginViewModel);
    }
    
    public async Task HandleAsync(AuthenticatedMessage authCode, CancellationToken cancellationToken)
    {
        // this is reached! So the event is publishing successfully.
        await Task.CompletedTask;
    }
}

LoginViewModel also subscribes to this event, but it's Handle method is not invoked.

The Login method is responsible for creating the LoginWindowViewModel Window (shown underneath) which publishes the event.

public class LoginViewModel : Screen, IHandle<AuthenticatedMessage>
{
    private IEventAggregator _eventAggregator;
    private readonly IWindowManager _windowManager;
    private readonly ILoginWindowViewModelFactory _LoginWindowViewModelFactory;

    public LoginViewModel(IEventAggregator eventAggregator, 
        IWindowManager windowManager, 
        ILoginWindowViewModelFactory loginWindowViewModelFactory)
    {
        _eventAggregator = eventAggregator; 
        _windowManager = windowManager;
        _LoginWindowViewModelFactory = loginWindowViewModelFactory;
    }
    
    protected override async Task OnActivateAsync(CancellationToken cancellationToken)
    {
        await base.OnActivateAsync(cancellationToken);
        
        _eventAggregator.SubscribeOnPublishedThread(this);
    }
    
    // This is bound to a button click event. It creates a window.
    public async void Login()
    {
        Uri loginUri = new Uri(_api.BaseLoginUrl);

        await _windowManager.ShowWindowAsync(
            _ndLoginWindowViewModelFactory.Create(loginUri, _eventAggregator));
    }       
    
    
    public async Task HandleAsync(AuthenticatedMessage authCode, CancellationToken cancellationToken)
    {
        // why is this is never reached?
        await Task.CompletedTask;
    }
}

The LoginWindowViewModel that publishes a AuthenticatedMessage event:

public class LoginWindowViewModel : Screen
{
    private readonly IEventAggregator _eventAggregator;
    private readonly Uri _initialUri;         

    public NDLoginWindowViewModel(Uri initialUri, IEventAggregator eventAggregator)
    {
        _initialUri = initialUri;
        _eventAggregator = eventAggregator;
    }       
    
    // bound to the WebView2 (browser control) event
    public async void NavigationStarting(object sender, CoreWebView2NavigationStartingEventArgs e)
    {            
            string authCode = HttpUtility.ParseQueryString(new Uri(e.Uri).Query).Get("code");
            
            // Publish event here. LoginViewModel should handle this, but currently only ShellViewModel can.
            await _eventAggregator.PublishOnUIThreadAsync(new AuthenticatedMessage(authCode));                              
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

一抹微笑 2025-01-24 10:28:49

我在移动eventAggregator.subscribeoneparperemendthread(this); loginViewModel 构造器之后解决了问题,而不是loginViewModel sonstructor,而不是onactivateasync() method。

从这里:

protected override async Task OnActivateAsync(CancellationToken cancellationToken)
{
    await base.OnActivateAsync(cancellationToken);
    
    _eventAggregator.SubscribeOnPublishedThread(this);
}

到这里:

public LoginViewModel(IEventAggregator eventAggregator, 
    IWindowManager windowManager, 
    ILoginWindowViewModelFactory loginWindowViewModelFactory)
{
    _eventAggregator = eventAggregator; 
    _windowManager = windowManager;
    _LoginWindowViewModelFactory = loginWindowViewModelFactory;

    _eventAggregator.SubscribeOnPublishedThread(this);
}

编辑:

onactivateasync方法在首次在shellViewModel中首次创建时未调用方法,因为它是我的根屏幕和导体。因此,订阅从未进行过。

public class ShellViewModel : Conductor<IScreen>.Collection.OneActive
{
...
    protected override async Task OnActivateAsync(CancellationToken cancellationToken)
    {
        await base.OnActivateAsync(cancellationToken);            
    
        await ActivateItemAsync(_loginViewModel);
        // IsActive = false here, therefore the child Screen `_loginViewModel` 
        // is also not active. Result is that OnActivateAsync 
        // in this view model does not get called.
    }
}

它与这个问题和答案

这解释了为什么将其移至构造函数解决问题。

最终的解决方案是在构造函数和 onactivateasync 方法中添加_EVENTAGGREGATOR.SUBSCRIBENENPUBLINGENREDTHREAD(this);。这使我可以在我远离此ViewModel并回到它之后重新订阅该事件。

I resolved the issue after moving eventAggregator.SubscribeOnPublishedThread(this); to the LoginViewModel constructor, instead of the OnActivateAsync() method.

From here:

protected override async Task OnActivateAsync(CancellationToken cancellationToken)
{
    await base.OnActivateAsync(cancellationToken);
    
    _eventAggregator.SubscribeOnPublishedThread(this);
}

To here:

public LoginViewModel(IEventAggregator eventAggregator, 
    IWindowManager windowManager, 
    ILoginWindowViewModelFactory loginWindowViewModelFactory)
{
    _eventAggregator = eventAggregator; 
    _windowManager = windowManager;
    _LoginWindowViewModelFactory = loginWindowViewModelFactory;

    _eventAggregator.SubscribeOnPublishedThread(this);
}

EDIT:

The OnActivateAsync method isn't being called when the View is first created in the ShellViewModel because it is my root Screen and Conductor. So the Subscription was never taking place.

public class ShellViewModel : Conductor<IScreen>.Collection.OneActive
{
...
    protected override async Task OnActivateAsync(CancellationToken cancellationToken)
    {
        await base.OnActivateAsync(cancellationToken);            
    
        await ActivateItemAsync(_loginViewModel);
        // IsActive = false here, therefore the child Screen `_loginViewModel` 
        // is also not active. Result is that OnActivateAsync 
        // in this view model does not get called.
    }
}

It is directly related to this problem and answer.

That explains why moving it to the constructor solved the problem.

The final solution was to add _eventAggregator.SubscribeOnPublishedThread(this); in both the Constructor AND OnActivateAsync method. This allows me to resubscribe to the event after I navigate away from this viewmodel and come back to it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文