【问题标题】:Propagate / Bubble events across several ViewModel in MVVM在 MVVM 中跨多个 ViewModel 传播/冒泡事件
【发布时间】:2013-12-31 06:56:43
【问题描述】:

我有一个MainWindow.xaml,其中DataContext 设置为MainWindowViewModel.cs。在用户单击按钮并将MyUserControl.xaml 添加到窗口之前,此窗口中没有任何内容。 MyUserControl.xaml 有一个 MyUserControlViewModel.cs 设置为 DataContext

我想在MyUserControlViewModel.cs 中触发一个被MainWindowViewModel.cs 捕获的事件。

到目前为止,我有这个。在我的MyUserControlViewModel.cs

public event EventHandler<LoadingEventArgs> LoadingEvent;
(...)

public void MyFunction() {
    LoadingEvent(this, EventLibrary.LoadingEvent(2));
}

在我的MainWindowViewModel.cs

private MyUserControlViewModel _myUserControlViewModel;
public MyUserControlViewModel _MyUserControlViewModel {
    get { return _myUserControlViewModel; }
    private set { _myUserControlViewModel = value; OnPropertyChangedEvent("MyUserControlViewModel"); }
}

public MainWindowViewModel() {
    // Can't attach event here
}

public void AddEventToUserControl() {
    _MyUserControlViewModel = new MyUserControlViewModel();
    _MyUserControlViewModel.LoadingEvent += MyUserControlViewModel_LoadingEvent;
}

private void MyUserControlViewModel_LoadingEvent(object sender, LoadingEventArgs e) {
    MessageBox.Show("I got here!");
}

消息"I got here" 永远不会显示,因为MyUserControlViewModel.cs 中的LoadingEventnull
我只在MainWindowViewModel() 函数之外附加MyUserControlViewModel_LoadingEvent 的原因是因为在加载窗口时不会添加MyUserControl.xaml,而是在用户单击按钮之后才添加。
我认为这里的问题是事件没有与MyUserControlViewModel.cs的创建同时添加。

我需要知道如何通过代码动态创建 ViewModel 来触发冒泡事件。


更新 - 添加了更多信息
鉴于我的上述情况可能很简单并且缺少重要的细节,我将描述我的确切情况。

我有三种观点——祖父、父亲和儿子。对于每一个我都有一个 ViewModel 类。在我父亲中,我在 XAML 中声明了儿子,因为它是同时创建的,所以当我的 FatherViewModel 中有 SonViewModel = new SonViewModel() 时,它指向正确的位置,我可以在 @987654345 中访问我需要的所有内容@。因此,SonViewModel 中触发的任何被 FatherViewModel 监听的事件都会被捕获。
这里最重要的两个事实是,父亲和儿子都是同时创建的,而儿子是在 XAML 中创建的,如下所示:

<View:SonView DataContext="{Binding SonViewModel, Mode=OneWay}" />

这完全符合我的要求。

问题出现在祖父和父亲身上,因为他们不是同时创建的。我首先创建 GrandfatherView.xaml 并在其中关联 GrandfatherView.cs(我知道这不是 MVVM 实践,但我正在尝试在不使用 MVVM 模式的项目中实现一些 MVVM 功能)我创建 FatherView 并将其添加到Grid,像这样:

FatherView _fatherView = new FatherView();
_fatherView.DataContext = new FatherViewModel();
mainGrid.Children.Add(_fatherView);

因为我在创建GrandfatherView 并通过代码添加它并且没有特定绑定之后创建FatherView,所以当我在GrandfatherViewModel 类中创建FatherViewModel 对象时,它没有指向我在我的应用程序中看到的 FatherView 的实际 ViewModel。
我不确定如何在代码中创建绑定&lt;View:SonView DataContext="{Binding SonViewModel, Mode=OneWay}" /&gt;,因此我可以确保属性和绑定相同,所以我在GrandfatherViewModel 中拥有的 ViewModel 对象实际上是我在我的应用程序中看到的对象,并且不是一个全新的对象。

那么,有什么建议吗?

【问题讨论】:

  • AddEventToUserControl() 中是否有错字?您的意思是 _MyUserControlViewModel 而不是 _MainContentViewModel?
  • 是的,这是一个错字,感谢您的注意。
  • 如果LoadingEvent为空,则表示没有人成功订阅该事件。问题中没有提供足够的信息来说明原因。例如,我无法判断您在AddEventToUserControl 中创建的MyUserControlViewModel 实例是否实际分配给了控件的DataContext
  • 旁注:您或许应该查看Caliburn Micro,这是 IMO 的天赐之物。
  • @McGarnagle,确实 MyUserControlViewModel 的实例没有分配给 DataContext。如果我尝试使用该对象更改属性,则没有任何反应,所以我猜它没有指向正确的位置。

标签: c# wpf xaml mvvm


【解决方案1】:

问题的完整解决方案(使用 Mvvm Light):

GrandFatherView.xaml

<Window x:Class="FunWithDynamicallyCreatedViewModels.GrandFatherView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid Name="MainGrid" />
</Window>

GrandFatherView.xaml.cs

public sealed partial class GrandFatherView
{
    public GrandFatherView()
    {
        InitializeComponent();

        var fatherView = new FatherView
                         {
                             DataContext = new FatherViewModel
                                           {
                                               SonViewModel = new SonViewModel()
                                           }
                         };
        MainGrid.Children.Add(fatherView);
    }
}

FatherView.xaml

<UserControl x:Class="FunWithDynamicallyCreatedViewModels.FatherView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:FunWithDynamicallyCreatedViewModels">

    <local:SonView DataContext="{Binding SonViewModel}" />

</UserControl>

FatherViewModel.cs

public sealed class FatherViewModel : ViewModelBase
{
    private SonViewModel _sonViewModel;

    public SonViewModel SonViewModel
    {
        get { return _sonViewModel; }
        set
        {
            if (SonViewModel != null)
                SonViewModel.Event -= OnUserControlViewModelEvent;

            Set(() => SonViewModel, ref _sonViewModel, value);

            if (SonViewModel != null)
                SonViewModel.Event += OnUserControlViewModelEvent;
        }
    }

    private void OnUserControlViewModelEvent(object sender, EventArgs args)
    {
        // violation of MVVM for the sake of simplicity
        MessageBox.Show("I got here!");
    }
}

SonView.xaml

<UserControl x:Class="FunWithDynamicallyCreatedViewModels.SonView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Button Command="{Binding InvokeEventCommand}" 
            Content="Invoke SonViewModel Event" />

</UserControl>

SonViewModel.cs

public sealed class SonViewModel
{
    public SonViewModel()
    {
        InvokeEventCommand = new RelayCommand(() => Event(this, new EventArgs()));
    }

    public event EventHandler Event;

    public ICommand InvokeEventCommand { get; private set; }
}

【讨论】:

  • Set(() =&gt; UserControlViewModel, ref _userControlViewModel, value); 应该如何工作?它返回一个错误,指出Set 在当前上下文中不存在。我错过了什么吗?
  • @josemartins.grupoma 您是否在项目中添加了对 MvvmLightLibs NuGet 包的引用? Set(...) 是 ViewModelBase 类的一个方法,它是 MvvmLight 库的一部分。
  • 那不见了,我的错。如何在没有Command 绑定到UserControlView 按钮的情况下直接在代码中触发UserControlViewModel 中的InvokeEventCommand
  • 在您的问题中不清楚谁负责触发事件。它可以是用户(如我的示例),可以是一些第三方代码,也可以是 UserControlViewModel 本身。
  • 究竟在哪个代码中:UserControlViewModel、WindowViewModel、AnotherViewModel?无论如何,这段代码是由某事或某人触发的:)我不清楚程序的执行流程。你想达到什么目的?也许您可以提供问题的高级描述?如果您需要在视图模型之间传递值,您可以使用 Mediator 方法来替代事件(MvvmLight 中的 IMessenger,Caliburn.Micro 中的 IEventAggregator)。
猜你喜欢
  • 2011-12-09
  • 1970-01-01
  • 2017-12-26
  • 2011-03-19
  • 1970-01-01
  • 2018-07-26
  • 2010-10-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多