65.9K
CodeProject 正在变化。 阅读更多。
Home

Expression Blendable Silverlight ViewModel 通信

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (10投票s)

2010年9月25日

Ms-PL

3分钟阅读

viewsIcon

56670

downloadIcon

522

轻松实现主 ViewModel 和子 ViewModel 之间的双向通信,该通信可与 Microsoft Expression Blend 一起使用。

简便的“Blendable” ViewModel 通信

Live example at this link.

在文章《Silverlight ViewModel 通信》中,我们已经介绍了ViewModel 通信,但那只是一方的通信。

Silverlight Debate Forum 项目中,开发者们要求实现双向通信,并且希望它是“blendable”的,也就是说,他们可以使用 Microsoft Expression Blend,轻松地从任何 View Model 控件中选择 ICommands 并设置任何 View Model 控件的属性。

最终结果是,允许您从任何 MasterChild 控件调用任何 MasterChild 控件。

工作原理

上图解释了这是如何工作的。如果您觉得困惑,不用担心,我们会尝试用几种不同的方式来讲解。

Child1ViewModel 在其 View Model 中持有一个 MainPageViewModel 的实例。它能够使用这个实例来调用 Main Page View Model 中的任何 ICommand,或者读取和设置任何 Properties

此外,Main Page View Model 还包含其他 Child View ModelsView Model,因此一个 Child View Model 能够通过 Main Page View Model 与其他 Child View Models 进行通信,调用它们的 ICommand,或者读取和设置它们的 Properties

代码 - Child Control

Child View Model 的代码如下:

public class Child1ViewModel : INotifyPropertyChanged
{
    public Child1ViewModel()
    {
        Child1RaiseCommand = new DelegateCommand(Child1Raise, CanChild1Raise);
    }
    
    public ICommand Child1RaiseCommand { get; set; }
    public void Child1Raise(object param)
    {
        Message = (String)param;
    }
    
    private bool CanChild1Raise(object param)
    {
        return true;
    }
    
    private string _Message;
    public string Message
    {
        get { return _Message; }
        set
        {
            if (Message == value)
            {
                return;
            }
            _Message = value;
            this.NotifyPropertyChanged("Message");
        }
    }
    
    private MainPageViewModel _objMainPageViewModel;
    public MainPageViewModel objMainPageViewModel
    {
        get { return _objMainPageViewModel; }
        set
        {
            if (objMainPageViewModel == value)
            {
                return;
            }
            _objMainPageViewModel = value;
            this.NotifyPropertyChanged("objMainPageViewModel");
        }
    }
    
    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    
    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
    #endregion
}

请注意,它包含:

  • 一个 Message Property,用于显示由 Child1RaiseCommand ICommand 设置的消息。
  • 一个 Property,用于保存 MainPageViewModel 的实例 (objMainPageViewModel)。

围绕 objMainPageViewModel 属性的蓝色框显示了如何通过该属性导航,以调用 Main Page View Model 或另一个 Child View Model 上的 ICommand

但是,重要的是,只能将 XAML 页面的 DataContext 设置为指向 Child 控件的 View Model。如果您为 XAML 中该控件的任何其他元素设置 Data Context通信将不起作用

代码 - Main Control

Main Page View Model 的代码如下:

public class MainPageViewModel : INotifyPropertyChanged
{
    public MainPageViewModel()
    {
        RaiseCommand = new DelegateCommand(Raise, CanRaise);
        
        // Create instances of each ViewModel for each Child Control
        // Set an instance of the MainPage View Model in each 
        // View Model for each Child Control
        // The Child Controls onb the View of The Main Page will
        // be bound to these properties
        objChild1ViewModel = new Child1ViewModel();
        objChild1ViewModel.objMainPageViewModel = this;
        
        objChild2ViewModel = new Child2ViewModel();
        objChild2ViewModel.objMainPageViewModel = this;
    }
    
    #region RaiseCommand
    public ICommand RaiseCommand { get; set; }
    public void Raise(object param)
    {
        Message = (String)param;
    }
    
    private bool CanRaise(object param)
    {
        return true;
    }
    #endregion
    
    private string _Message;
    public string Message
    {
        get { return _Message; }
        private set
        {
            if (Message == value)
            {
                return;
            }
            _Message = value;
            this.NotifyPropertyChanged("Message");
        }
    }
    
    private Child1ViewModel _objChild1ViewModel;
    public Child1ViewModel objChild1ViewModel
    {
        get { return _objChild1ViewModel; }
        set
        {
            if (objChild1ViewModel == value)
            {
                return;
            }
            _objChild1ViewModel = value;
            this.NotifyPropertyChanged("objChild1ViewModel");
        }
    }
    
    private Child2ViewModel _objChild2ViewModel;
    public Child2ViewModel objChild2ViewModel
    {
        get { return _objChild2ViewModel; }
        set
        {
            if (objChild2ViewModel == value)
            {
                return;
            }
            _objChild2ViewModel = value;
            this.NotifyPropertyChanged("objChild2ViewModel");
        }
    }
    
    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    
    private void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
    #endregion
}

请注意,它包含:

  • 一个 Property,用于保存每个 Child View Model 的实例 (objChild1ViewModelobjChild2ViewModel)。
  • 它在其构造函数中初始化每个 Child View Model Property 时,设置了它自身的实例 (objChild1ViewModel.objMainPageViewModel = this)。

上图显示了我们现在如何能够调用 Child View Models 中的 ICommands

将这一切联系起来的关键是,在 Microsoft Expression Blend 中,我们将一个 Child Control 放在 Main Page 上……

在控件的 Properties 中,我们选择 Advanced options

我们选择 Data Binding...

我们将 Child ControlDataContext 绑定到 Main Page View Model 中的属性。

我们还将其设置为 TwoWay 绑定。

现在它是 Blendable 的了

Expression Blend 的设计器中,如果我们点击 Child Control 中的一个 Button……

在 Properties 中,我们选择 Command (在 Miscellaneous 下) 旁边的 Advanced options

然后,我们可以导航到应用程序中任何 View Model 的任何 ICommandProperty

其他方法

还有其他方法可以实现 View Models 之间的通信。有各种 MVVM 框架提供消息传递。这些框架的优点是它们是松耦合的。然而,对于 Silverlight Debate Forum 项目,如果我们将来需要进行更改,我们只需对代码进行必要的修改。

该项目的主要优势在于,由于它使用 MEF 来动态加载 ViewsDesigner 将能够实现 View Model Communication 而无需修改任何代码。Designer 所需的一切都将在 Expression Blend 中提供。

© . All rights reserved.