8

我需要弄清楚如何在 ViewModel 之间进行通信。我是 MVVM 的新手,所以请善待。

这是一个愚蠢的例子

类定义(假设我已经在 ParentViewModel 中挂钩 Child.PropertyChanged 事件):

public class ParentViewModel : ViewModelBase
{
    public ChildViewModel Child { get; set; }
}

public class ChildViewModel : ViewModelBase
{
    String _FirstName;
    public String FirstName 
    {
        get { return _FirstName; }
        set
        {
            _FirstName = value;
            OnPropertyChanged("FirstName");
        }
    }
}

这是您在资源字典中看到的内容

<DataTemplate DataType="{x:Type vm:ParentViewModel}">
    <vw:ParentView/>
</DataTemplate>

<DataTemplate DataType="{x:Type vm:ChildViewModel}">
    <vw:ChildView/>
</DataTemplate>

和 ChildView 的代码隐藏:

public partial class ChildView : UserControl
{
    public QueueView()
    {
        InitializeComponent();
        DataContext = new ChildViewModel();
    }
}

明显的问题是,当 ChildView 被实例化(通过从 DataTemplate 中选择)时,它会创建一个新的 ChildViewModel 类,而 ParentViewModel 无权访问它。

那么如何将 View 的 DataContext 实例化为导致 DataTemplate 被选中的原始 ViewModel 呢?

一个明显的解决方法是将 ChildViewModel 中的属性合并到 ParentViewModel 中,但我宁愿将它分开,因为以便重用。

我敢肯定答案是微不足道的,我只是想知道它是什么。:)

提前致谢。

4

3 回答 3

8

您应该简单地删除该行:

DataContext = new ChildViewModel();

视图的DataContext将由 WPF 自动设置。DataTemplates始终将其数据上下文设置为模板的数据(在本例中为 ViewModel):

<DataTemplate DataType="{x:Type vm:ChildViewModel}">
    <vw:ChildView/>
</DataTemplate>

最终结果是您可以单独构建视图模型对象(父类和子类),然后通过简单地将它们插入内容控件来显示它们。

于 2009-05-05T16:42:51.383 回答
5

使用 MVVM 方法在 ViewModel 之间进行通信的最简单方法是使用 Mediator 模式(Prism 中的 EventAggregator)。在以下链接中可以看到这种方法的一个很好的例子:

  1. Sacha Barber 的 MVVM 中介者模式
  2. MVVM + 调解器 by marlon grech

另请查看 MVVM示例项目框架。

于 2009-05-06T16:04:32.747 回答
1

假设您有一个使用 QueueViewModel 的 QueueView。

public class QueueViewModel : INotifyPropertyChanged
{
    public ParentType Parent { get; set; }

    public QueueViewModel(ParentType parent)
    {
        this.Parent = parent;
        foreach (ChildType child in Parent)
        {
            child.PropertyChanged += delegate(object sender,
                PropertyChangedEventArgs e)
            {
                if (e.PropertyName != "IsSelected")
                    return;

                //do something like this:
                Parent.IsSelected = AllChildrenAreSelected();
            };
        }
    }

}

public class ParentType : INotifyPropertyChanged
{
    private bool _isSelected;

    public IList<ChildType> Children { get; set; }
    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            _isSelected = value;
            OnPropertyChanged("IsSelected");
        }
    }
}

public class ChildType : INotifyPropertyChanged
{
    private string _name;
    private bool _isSelected;

    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged("Name");
        }
    }

    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            _isSelected = value;
            OnPropertyChanged("IsSelected");
        }
    }
}

-- 队列视图部分

<StackPanel>
<CheckBlock Text="{Binding Path=Parent.Name}" 
            IsChecked="{Binding Parent.IsSelected}"/>
<ItemsControl ItemsSource="{Binding Path=Parent.Children}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>                                    
            <CheckBox Content="{Binding Path=Name}"
                      IsChecked="{Binding Path=IsSelected, Mode=TwoWay}"/>
        </DataTemplate>
    <ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
于 2009-05-05T20:12:39.477 回答