gpt4 book ai didi

c# - Caliburn Micro MVVM : Handling data exchange from one View/ViewModel to other ones

转载 作者:行者123 更新时间:2023-12-03 10:54:10 25 4
gpt4 key购买 nike

我创建了一个带有2个 View 模型及其相关 View 的wpf项目(使用带有MVVM模式的caliburn micro和无代码隐藏程序):

  • ShellView.xaml和ShellViewModel.cs
  • OtherView.xaml和OtherViewModel.cs

  • ShellView包含:
  • ContentControl,它引用OtherView/OtherViewModel。
  • 一个TextBox,其中包含所谓的“目标文本”。

  • OtherView包含一个StackPanel,其中包含:
  • 一个TextBox,它接受来自用户的文本(​​作为“源文本”)。
  • 一个Button,它将Right-MouseButton Click事件上的源文本复制到目标。

  • 我的问题:
  • 如何将OtherView/ViewModel中的源文本复制到ShellView/ViewModel中的目标文本?有什么最佳实践吗?
  • ShellViewModel可以从源TextBox捕获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.cs
    public 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);
    }
    }
    }

    OtherView.xaml
    <UserControl
    x:Class="CmMultipleViewModelView.Views.OtherView"
    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="150"
    d:DesignWidth="150"
    mc:Ignorable="d">
    <StackPanel
    HorizontalAlignment="Center"
    VerticalAlignment="Top"
    Orientation="Vertical">
    <TextBox
    x:Name="SourceText"
    Width="80"
    Margin="3" />
    <Button
    x:Name="CopyText"
    Width="100"
    Margin="3"
    Content="Copy" />
    </StackPanel>
    </UserControl>

    OtherViewModel.cs
    public class OtherViewModel : Screen
    {
    private string _sourceText = "Source";
    public string SourceText
    {
    get => _sourceText;
    set
    {
    _sourceText = value;
    NotifyOfPropertyChange(() => SourceText);
    }
    }

    public void CopyText()
    {
    // How to copy the SourceText to TargetText using Caliburn Micro MVVM?
    // Can ShellViewModel catch the PropertyChange event from source textbox?
    }
    }

    编辑:

    AppBootstrapper.cs
    public class AppBootstrapper : BootstrapperBase
    {
    private readonly SimpleContainer _container = new SimpleContainer();

    public AppBootstrapper()
    {
    Initialize();
    }

    public ShellViewModel ShellViewModel { get; set; }

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

    protected override void Configure()
    {
    base.Configure();
    _container.Singleton<IWindowManager, WindowManager>();
    _container.Singleton<IEventAggregator, EventAggregator>();
    _container.Singleton<ShellViewModel>();
    _container.PerRequest<OtherViewModel>(); // Or Singleton if there'll only ever be one

    }

    protected override void OnStartup(object sender, StartupEventArgs e)
    {
    try
    {
    base.OnStartup(sender, e);
    DisplayRootViewFor<ShellViewModel>();
    }
    catch (Exception ex)
    {
    Debug.WriteLine(ex.StackTrace);
    Debug.WriteLine(ex.Message);
    }
    }
    }

    ShellViewModel.cs
    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 sealed override void ActivateItem(object item)
    {
    base.ActivateItem(item);
    }

    public OtherViewModel OtherViewModel { get; set; }

    private string _targetText = "Target";
    public string TargetText
    {
    get => _targetText;
    set
    {
    _targetText = value;
    NotifyOfPropertyChange(() => TargetText);
    }
    }

    public void Handle(string message)
    {
    TargetText = message;
    }
    }

    OtherViewModel.cs
    public class OtherViewModel : Screen
    {
    private readonly IEventAggregator _eventAggregator;

    public OtherViewModel(IEventAggregator eventAgg)
    {
    _eventAggregator = eventAgg;
    }

    private string _sourceText = "Source";
    public string SourceText
    {
    get => _sourceText;
    set
    {
    _sourceText = value;
    NotifyOfPropertyChange(() => SourceText);
    }
    }

    public void CopyText()
    {
    _eventAggregator.PublishOnUIThreadAsync(SourceText);
    }
    }

    再次编辑

    添加
    _container.Singleton<IWindowManager, WindowManager>();

    在AppBootstraper::Configure中

    问题解决了!

    最佳答案

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

    如果在Caliburn.Micro中使用SimpleContainer,则在OnConfigure覆盖中放置:

    _container.Singleton<IEventAggregator>();

    首次访问时,这将创建一个IEventAggregator的实例。现在,您可以选择访问方式。通过注入(inject)构造函数或使用IoC.GetInstance方法。

    如果要注入(inject),则需要修改 View 模型:
    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中,您需要注册两个 View 模型:
    _container.Singleton<ShellViewModel>();
    _container.PerRequest<OtherViewModel>(); // Or Singleton if there'll only ever be one

    那么,这是做什么的呢?

    在您的ShellViewModel中,我们告诉它为字符串实现IHandle接口(interface)。
    IHandle<string>

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

    IHandle<yourtype>

    当事件聚合器接收到字符串事件时,它将调用任何监听器的Handle方法。在您的情况下Handle(字符串消息)。如果更改IHandle类型,则还需要将Handle方法更改为相同类型。
    public void Handle(string message)
    {
    TargetText = message;
    }

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

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

    一旦我们在本地存储了对事件聚合器的引用,我们将调用:
    _eventAggregator.Subscribe(this);

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

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

    在您的CopyText方法中,您将调用:
    _eventAggregator.PublishOnUIThread(SourceText);

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

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

    流程将去:

    ShellViewModel订阅字符串事件

    _eventAggregator.Subscribe(this);

    用户在SourceText中输入一些文本
    用户按下鼠标右键,将调用:
    CopyText()

    哪个调用:
    _eventAggregator.PublishOnUIThread(SourceText);

    然后,事件聚合器检查具有IHandle接口(interface)的所有订阅的 View 模型,然后调用:
    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我们正在使用什么ViewModel,以及在哪里设置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现已全部设置好。
    最后,在您的启动覆盖中,您将确保已将其设置为如下所示:
    protected override void OnStartup(object sender, StartupEventArgs e)
    {
    base.OnStartup(sender, e);

    DisplayRootViewFor<ShellViewModel>();
    }

    如果现在运行应用程序,则在创建ShellViewModel时,Caliburn.Micro将查看ShellViewModel的构造函数参数以查看其需要提供的内容。它将看到它需要一个事件聚合器和OtherViewModel,因此它将在SimpleContainer中查找是否已注册。如果有的话,它将创建实例(如果需要)并将其注入(inject)到构造函数中。在创建OtherViewModel时,还将检查构造函数的参数,并创建所需的内容。

    最后,它将显示ShellViewModel。

    关于c# - Caliburn Micro MVVM : Handling data exchange from one View/ViewModel to other ones,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51499017/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com