2014年1月3日星期五

Silverlight MVVM 模式下与子窗体交互

Model View ViewModel(MVVM)是在 Silverlight WPF 项目开发中应用最多的结构模式,也是 Silverlight WPF 项目开发的最佳模式。本文的主要目的不是讲解 MVVM 模式,如果您不了解 MVVM 模式,可心参看这里和这里。目前已有很多 MVVM 框架可以用来简化 MVVM 开发,如 PrismSilverlightFXMvvmLightCaliburnSimple MVVM Toolkit等。

在程序开发中经常会遇到诸如弹出提示框、确认框、用户输入窗口等的情况,在 Silverlight 中这些情况都可以用子窗体(Child Window)来处理。在未使用 MVVM 模式的情况下,我们可以在用户控件或页面的后置代码(Code Behind)中实例化一个子窗体,调用子窗体的 Show 方法来弹出子窗体,然后通过子窗体的 Closed 方法用户的操作结果。但是在使用 MVVM 模式的情况下,为了使 Model View 最大限度的分离,我们要使用尽可能少的后置代码。如果在后置代码中实例化并显示子窗体,这就使用代码和视图结合在一起了;当然也可以在 ViewModel 里实例化并显示子窗体,这样又使子窗体和 ViewModel 结合在一起了。
本文将使用 MvvmLight 框架来演示如何在 MVVM 模式下与子窗体交互,即如何在 ViewModel 中弹出一个子窗体。关于 MvvmLight 的下载及安装请看这里。
首先我们来看一下示例程序的运行结果:



程序运行后当用户单击 New Customer 按钮时,就会弹出一个子窗体,用户输入Customer IdCustomer NameCustomer City 后单击 OK 按钮就会在主页面的客户列表中显示出刚才输入的客户信息。下面是本示例具体的实现。
新建一个 Silverlight 项目,然后在项目中添加ViewsModelsViewModelsLocators文件夹(如果是通过 MvvmLight 模板创建的 Silverlight 项目,默认 ViewModel Locator ViewModel 在同一文件夹中)。添加对程序集 GalaSoft.MvvmLight.Extras.SL4 GalaSoft.MvvmLight.SL4 的引用(如果通过 MvvLight 模板创建则会自动引用)。在 Model 文件夹中新建一个 Customer Model,完整代码如下:
01 public class Customer : INotifyPropertyChanged  

02 {  

03     private string customerId;  

04     public string CustomerId  

05     {  

06         get { return customerId; }  

07         set 

08         {  

09             customerId = value;  

10             NotifyPropertyChanged("CustomerID");  

11         }  

12     }  

13    

14     private string customerName;  

15     public string CustomerName  

16     {  

17         get { return customerName; }  

18         set 

19         {  

20             customerName = value;  

21             NotifyPropertyChanged("CustomerName");  

22         }  

23     }  

24    

25     private string city;  

26     public string City  

27     {  

28         get { return city; }  

29         set 

30         {  

31             city = value;  

32             NotifyPropertyChanged("City");  

33         }  

34     }  

35    

36     public event PropertyChangedEventHandler PropertyChanged;  

37    

38     public void NotifyPropertyChanged(string propertyName)  

39     {  

40         if (PropertyChanged != null)  

41             PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));  

42     }  

43 } 

下面是 MainPage 的后置代码:
01 public partial class MainPage : UserControl  

02 {  

03     public MainPage()  

04     {  

05         InitializeComponent();  

06    

07         Messenger.Default.Register<string>(  

08             this,  

09             "MainWindow",  

10             msg =>  

11             {  

12                 NewCustomerView newCustomer = new NewCustomerView();  

13                 newCustomer.Title = msg;  

14                 newCustomer.Show();  

15             });  

16     }  

17 } 

从上面的代码中可以看到,我使用 MvvmLight Messenger 在主窗体中注册了一个消息接收者,用于接收 ViewModel 发来的消息并弹出子窗体。下面是子窗体 NewCustomerView 的后置代码:
01 public partial class NewCustomerView : ChildWindow  

02 {  

03     public NewCustomerView()  

04     {  

05         InitializeComponent();  

06    

07         Messenger.Default.Register<Customer>(  

08             this,  

09             "ChildWindow",  

10             msg =>  

11             {  

12                 this.DialogResult = true;  

13             });  

14     }  

15 } 

我同样在子窗体中也注册一个消息接收者,接收 ViewModel 发来的消息并关闭子窗体。注意主窗体中注册的消息接收者的 Token MainWindow”,子窗体中注册的消息接收者的 Token ChildWindow”,在 ViewModel 中发送消息时,只有与发送的消息的 Token 相同的接收者才收到消息。下面是 MainViewModel 的代码,这里只是为了演示,主窗体和子窗体共用了一个 ViewModel
01 public class MainViewModel : ViewModelBase  

02 {          

03     public MainViewModel()  

04     {  

05         OKButtonCommand = new RelayCommand<Customer>(OKButtonClick);  

06         NewCustomerCommand = new RelayCommand(NewCustomer);  

07    

08         _customer = new Customer();  

09         _customers = new ObservableCollection<Customer>();  

10         // 注册一个接收者 Token  ChildWindow  

11         Messenger.Default.Register<Customer>(this"ChildWindow", AddCustomer);  

12     }  

13    

14     private void AddCustomer(Customer param)  

15     {  

16         _customers.Add(param);  

17         RaisePropertyChanged("Customers");  

18     }  

19    

20     // customer list  

21     private ObservableCollection<Customer> _customers;  

22     public ObservableCollection<Customer> Customers  

23     {  

24         get 

25         {  

26             return _customers;  

27         }  

28     }  

29    

30     // customer model  

31     private Customer _customer;  

32     public Customer Model  

33     {  

34         get 

35         {  

36             return _customer;  

37         }  

38         set 

39         {  

40             _customer = value;  

41             RaisePropertyChanged("Model");  

42         }  

43     }  

44    

45     // add customer command  

46     public RelayCommand NewCustomerCommand { getprivate set; }  

47     private void NewCustomer()  

48     {  

49         /*  

50             * 发送一个字符串信息 New Customer  

51             * Token  MainWindow 只有具有相同 Token 接收者都会接收到该信息  

52             */ 

53         Messenger.Default.Send("New Customer""MainWindow");  

54     }  

55    

56     public RelayCommand<Customer> OKButtonCommand { getprivate set; }  

57     private void OKButtonClick(Customer param)  

58     {  

59         /*  

60             * 发送一个 Customer 信息  

61             * Token  ChildWindow 只有具有相同 Token 接收者都会接收到该信息  

62             */ 

63         Messenger.Default.Send<Customer>(param, "ChildWindow");  

64     }  

65 } 

ViewModel 中也注册了一个消息接收者,用于接收子窗体返回的数据。ViewModel 中的 NewCustomerCommand 是绑定到主窗体的 NewCustomer 按钮的,单击按钮 NewCustomer 时调用 NewCustomer() 方法向主窗体发送一个消息, 主窗体接收到消息后弹出子窗体。ViewModel 中的 OKButtonCommand 是绑定到子窗体的 OKButton 的,单击按钮 OKButton 时调用 OKButtonClick() 向子窗体和 ViewModel 发送一个消息,子窗体接收到消息时关闭,ViewModel 接收到消息时将参数 Customer 添加 Customer 列表中。以下是按钮的事件绑定代码:
01 <Button Content="New Customer" Width="120" Height="30">  

02     <i:Interaction.Triggers>  

03         <i:EventTrigger EventName="Click">  

04             <cmd:EventToCommand Command="{Binding NewCustomerCommand}" />  

05         </i:EventTrigger>  

06     </i:Interaction.Triggers>  

07 </Button>  

08    

09 <Button x:Name="OkButton" Content="OK" Width="75" Height="23" 
HorizontalAlignment="Right" Margin="0,12,0,0" Grid.Row="1">  

10     <i:Interaction.Triggers>  

11         <i:EventTrigger EventName="Click">  

12             <cmd:EventToCommand Command="{Binding OKButtonCommand}" 
CommandParameter="{Binding ElementName=Customer, Path=DataContext}" />  

13         </i:EventTrigger>  

14     </i:Interaction.Triggers>  

15 </Button
本示例只是提供一个在 MVVM 模式下与子窗体交互的解决方法,这个解决方法也并不是纯粹的 MVVM,完整的示例请查看附件的示例代码。
示例代码下载:SLMvvmChildWindow
本文来自forgetu的博客,原文地址:http://www.cnblogs.com/forgetu/archive/2011/06/30/silverlight-mvvm-childwindow-interaction.html