Silverlight MVVM 模式下與子窗體交互

Model View ViewModel(MVVM)是在 Silverlight 和 WPF 項目開發中應用最多的結構模式,也是 Silverlight 和 WPF 項目開發的最佳模式。本文的主要目的不是講解 MVVM 模式。目前已有很多 MVVM 框架可以用來簡化 MVVM 開發,如 Prism、SilverlightFX、MvvmLight、Caliburn、Simple MVVM Toolkit等。


  本文將使用 MvvmLight 框架來演示如何在 MVVM 模式下與子窗體交互,即如何在 ViewModel 中彈出一個子窗體。  在 程序開發中經常會遇到諸如彈出提示框、確認框、用戶輸入窗口等的情況,在 Silverlight 中這些情況都可以用子窗體(Child Window)來處理。在未使用 MVVM 模式的情況下,我們可以在用戶控件或頁面的後置代碼(Code Behind)中實例化一個子窗體,調用子窗體的 Show 方法來彈出子窗體,然後通過子窗體的 Closed 方法用戶的操作結果。但是在使用 MVVM 模式的情況下,爲了使 Model 和 View 最大限度的分離,我們要使用儘可能少的後置代碼。如果在後置代碼中實例化並顯示子窗體,這就使用代碼和視圖結合在一起了;當然也可以在 ViewModel 裏實例化並顯示子窗體,這樣又使子窗體和 ViewModel 結合在一起了。

  首先我們來看一下示例程序的運行結果:

 

  程序運行後當用戶單擊 New Customer 按鈕時,就會彈出一個子窗體,用戶輸入Customer Id、Customer Name、Customer City 後單擊 OK 按鈕就會在主頁面的客戶列表中顯示出剛纔輸入的客戶信息。下面是本示例具體的實現。

  新 建一個 Silverlight 項目,然後在項目中添加Views、Models、ViewModels、Locators文件夾(如果是通過 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(this, new 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 { get; private 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 { get; private 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,完整的示例請查看附件的示例代碼。

發佈了13 篇原創文章 · 獲贊 1 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章