为了账号安全,请及时绑定邮箱和手机立即绑定

Caliburn Micro MVVM:处理从一个 View/ViewModel 到另一个

Caliburn Micro MVVM:处理从一个 View/ViewModel 到另一个

C#
幕布斯6054654 2021-10-24 19:33:55
我创建了一个 wpf 项目(使用 caliburn micro 和 MVVM 模式,没有代码隐藏),其中包含 2 个视图模型及其相关视图:ShellView.xaml 和 ShellViewModel.csOtherView.xaml 和 OtherViewModel.csShellView 包含:ContentControl 指的是OtherView/OtherViewModel。一个 TextBox,其中包含所谓的“目标文本”。OtherView 包含一个 StackPanel,其中包含:接受来自用户的文本(作为“源文本”)的 TextBox。在鼠标右键单击事件上将源文本复制到目标的按钮。我的问题:如何将 OtherView/ViewModel 中的源文本复制到 ShellView/ViewModel 中的目标文本?任何最佳实践?ShellViewModel 可以从源文本框捕获 PropertyChange 事件吗?如何反向复制(从目标到源)?在此先感谢您,如果需要,请随时修改下面的代码。ShellView.xaml<UserControl    x:Class="CmMultipleViewModelView.Views.ShellView"    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"    d:DesignHeight="450"    d:DesignWidth="800"    mc:Ignorable="d">    <Grid Width="800" Height="450">        <Grid.ColumnDefinitions>            <ColumnDefinition Width="*" />            <ColumnDefinition Width="*" />        </Grid.ColumnDefinitions>        <ContentControl            x:Name="ActiveItem"            Grid.Column="0"            HorizontalAlignment="Center"            VerticalAlignment="Center" />        <TextBox            x:Name="TargetText"            Grid.Column="1"            Width="80"            HorizontalAlignment="Center"            VerticalAlignment="Center" />    </Grid></UserControl>ShellViewModel.cspublic class ShellViewModel : Conductor<object>{    public ShellViewModel()    {        DisplayName = "Shell Window";        var otherVM = new OtherViewModel();        ActivateItem(otherVM);    }    public string DisplayName { get; set; }    private string _targetText = "Target";    public string TargetText    {        get => _targetText;        set        {            _targetText = value;             NotifyOfPropertyChange(() => TargetText);        }    }}
查看完整描述

1 回答

?
繁华开满天机

TA贡献1816条经验 获得超4个赞

正如其他人所说,正确的方法是使用事件聚合器。


如果您在 Caliburn.Micro 中使用 SimpleContainer,那么在您的 OnConfigure 覆盖中,您将输入:


_container.Singleton<IEventAggregator>();

当您第一次访问它时,这将创建一个 IEventAggregator 的实例。现在,您可以选择如何访问它。通过注入构造函数或使用 IoC.GetInstance 方法。


如果要注入,则需要修改视图模型:


public class ShellViewModel : Conductor<object>, IHandle<string>

{

    private readonly IEventAggregator _eventAggregator;


    public ShellViewModel(IEventAggregator eventagg, OtherViewModel otherVM)

    {

        _eventAggregator = eventagg;

        _eventAggregator.Subscribe(this);

        ActivateItem(otherVM);

    }


    public void Handle(string message)

    {

        TargetText = message;

    }

}


public class OtherViewModel : Screen

{

    private readonly IEventAggregator _eventAggregator;


    public OtherViewModel(IEventAggregator eventagg)

    {

        _eventAggregator = eventagg;

    }


    public void CopyText()

    {

        _eventAggregator.PublishOnUIThread(SourceText);

    }

}

在 Bootstrapper 中,您需要注册两个视图模型:


_container.Singleton<ShellViewModel>();

_container.PerRequest<OtherViewModel>(); // Or Singleton if there'll only ever be one

那么,这一切是为了什么?


在您的 ShellViewModel 中,我们告诉它为字符串实现 IHandle 接口。


IHandle<string>

每当触发字符串事件时,ShellViewModel 都会调用具有相同签名的 Handle 方法。如果您只想处理特定类型,则创建一个新类来保存您的复制文本并将处理程序从字符串更改为您的类型。


IHandle<string>


IHandle<yourtype>

当事件聚合器接收到字符串事件时,它将调用任何侦听器的 Handle 方法。在你的情况下处理(字符串消息)。如果更改 IHandle 类型,则还需要将 Handle 方法更改为相同类型。


public void Handle(string message)

{

    TargetText = message;

}

这会将 TargetText 设置为您在事件中触发的任何字符串值。


我们有一个 IEventAggregator 的实例,这是一个单例对象,所以它被引用的任何地方都应该是相同的。我们已修改您的 ShellViewModel 构造函数以接受 IEventAggregator 对象和您的 OtherViewModel 的实例。


一旦我们在本地存储了对事件聚合器的引用,我们就调用:


_eventAggregator.Subscribe(this);

这告诉事件聚合器我们对将由我们在类上定义的 IHandle 处理的任何事件感兴趣(只要它们处理不同的类型,您就可以有多个)。


与 OtherViewModel 有点不同,我们再次将 IEventAggregator 添加到构造函数,以便我们可以在启动时注入它,但这次我们不订阅任何事件,因为 OtherViewModel 只触发一个事件。


在您的 CopyText 方法中,您将调用:


_eventAggregator.PublishOnUIThread(SourceText);

这会在事件聚合器上引发事件。然后将其传播到使用 Handle 方法处理它的 ShellViewModel。


只要您在 Bootstrapper 的 SimpleContainer 实例中注册您的视图模型和事件聚合器,Caliburn.Micro 就会知道在创建 VM 实例时将哪些项目注入到构造函数中。


流程如下:


ShellViewModel 订阅字符串事件


_eventAggregator.Subscribe(this);


用户在 SourceText 中键入一些文本用户按鼠标右键,这会调用:


CopyText()

其中调用:


_eventAggregator.PublishOnUIThread(SourceText);

然后事件聚合器检查所有具有 IHandle 接口的订阅视图模型,然后调用:


Handle(string message)

每一个。


在您的情况下,这会将 TargetText 设置为消息:


TargetText = message;

为文字墙道歉!


有一种更简单的方法是让您的 ShellViewModel 订阅 OtherViewModel 上的 PropertyChanged 事件:


otherVM.PropertyChange += OtherVMPropertyChanged;

然后在处理程序中,您必须查找 SourceText 属性的通知并更新目标文本。一个更简单的解决方案,但意味着您将紧密耦合您的 ShellVM 和 OtherVM,而且您必须确保在关闭 OtherVM 时取消订阅该事件,否则它将永远不会被垃圾收集。


下面是如何设置 DI 容器

在您的 Bootstrapper 类中,您需要添加 SimpleContainer:


private SimpleContainer _simplecontainer = new SimpleContainer();

然后你需要覆盖一些方法并确保代码如下:


protected override object GetInstance(Type serviceType, string key)

{

    return _container.GetInstance(serviceType, key);

}


protected override IEnumerable<object> GetAllInstances(Type serviceType)

{

    return _container.GetAllInstances(serviceType);

}


protected override void BuildUp(object instance)

{

    _container.BuildUp(instance);

}

现在覆盖 OnConfigure 方法。这是我们告诉 Caliburn.Micro 我们正在使用的 ViewModels 以及我们设置 EventAggregator 和 WindowManager 的地方(因此它可以将您的 ShellViewModel 包装在一个窗口中):


protected override void Configure()

{

    base.Configure();


    _container.Singleton<IWindowManager, WindowManager>();

    _container.Singleton<IEventAggregator, EventAggregator>();


    _container.Singleton<ShellViewModel>();

    _container.PerRequest<OtherViewModel>(); // If you'll only ever have one OtherViewModel then you can set this as a Singleton instead of PerRequest

}

您的 DI 现已全部设置完毕。最后,在您的 StartUp 覆盖中,您只需确保它看起来像这样:


protected override void OnStartup(object sender, StartupEventArgs e)

{

    base.OnStartup(sender, e);


    DisplayRootViewFor<ShellViewModel>();

}

如果您现在运行您的应用程序,当 ShellViewModel 创建时,Caliburn.Micro 将查看 ShellViewModel 的构造函数参数以查看它需要提供什么。它将看到它需要一个事件聚合器和 OtherViewModel,因此它会在 SimpleContainer 中查看它们是否已被注册。如果有,那么它将创建实例(如果需要)并将它们注入到构造函数中。当它创建 OtherViewModel 时,它还会检查构造函数参数并创建任何需要的东西。


最后它将显示 ShellViewModel。


查看完整回答
反对 回复 2021-10-24
  • 1 回答
  • 0 关注
  • 441 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信