在MVVM中跨多个ViewModel传播/冒泡事件

时间:2013-12-12 17:55:29

标签: c# wpf xaml mvvm

我有一个MainWindow.xamlDataContext设置为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"从未显示,因为LoadingEvent中的MyUserControlViewModel.csnull
我只在MyUserControlViewModel_LoadingEvent函数之外附加MainWindowViewModel()的原因是因为在加载窗口时不会添加MyUserControl.xaml,而是在用户单击按钮后才会添加MyUserControlViewModel.cs。 /> 我认为这里的问题是事件不会与创建SonViewModel = new SonViewModel()同时添加。

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


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

我有三种观点 - 祖父,父亲和儿子。对于每一个我有一个ViewModel类。在我的父亲中,我在XAML中声明了儿子,因为它是在同一时间创建的,所以当我FatherViewModel中有SonViewModel时,它指向正确的位置,我可以访问我需要的一切在SonViewModel。因此,FatherViewModel中正在被<View:SonView DataContext="{Binding SonViewModel, Mode=OneWay}" /> 收听的任何事件都会被捕获 这里最重要的两个事实是父子俩都是同时创建的,儿子是在XAML中创建的,如下所示:

GrandfatherView.xaml

这很好,完全符合我的要求。

问题来自祖父和父亲,因为他们不是同时创造的。我首先创建GrandfatherView.cs并在其中关联FatherView(我知道它不是MVVM实践,但我试图在一个没有MVVM模式的项目中实现一些MVVM功能)我创建并添加了GridFatherView _fatherView = new FatherView(); _fatherView.DataContext = new FatherViewModel(); mainGrid.Children.Add(_fatherView); ,如此:

FatherView

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

那么,有什么建议吗?

1 个答案:

答案 0 :(得分:2)

问题的完整解决方案(使用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; }
}