WPF – MVVM (三)

31 December 2011 — Written by Sky Chang
#Design Patten#Silveright#WPF

做到這邊,我們已經做了很大部分的分離了,但是實際上,我們還有一個地方還沒處理,那就是放在View那邊的邏輯,也就是按下Button時的事件,所以我們這邊再進一步的去處理。

移除View裡面的邏輯

首先我們先把View裡面的邏輯移除,並把它放到ViewModel裡面去,所以我們先將View裡面的東西移除掉,現在裡面真的是空空沒有東西了。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WPFMVVM3
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

接下來我們來看看移到ViewModel產生了甚麼變化。

邏輯移到ViewModel

到這邊,我相信大家就會產生一個疑問,那View裡面的按鈕按下後,要怎樣去呼叫ViewModel裡面的東西呢??其實我們還是會使用一個Binding的方式,只是這次Binding的是一個ICommand的型別;接下來我們繼續處理ViewModel,這次在ViewModel加了一個ICommand型別的參數UpdateTitleNmae,他會回傳一個實作ICommand的RelayCommand實體,並且傳入兩個方法UpdateTitleExecute,CanUpdateTitleExecute ( 沒錯,不要懷疑,傳入進去的是方法)。等下我們會建立RelayCommand類別,到時候就會看到,另外UpdateTitleExecute這個方法裡面定義著要處理的邏輯,也就是從View搬過來的邏輯;而CanUpdateTitleExecute代表著是否可以執行此方法,因為我們這邊一定都會讓此方法( UpdateTitleExecute )可以動作,所以CanUpdateTitleExecute就直接回傳True。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Windows.Input;

namespace WPFMVVM3
{
    //實作InotifyPropertyChanged
    public class PostsViewModel : INotifyPropertyChanged
    {
        public Posts posts{ get; set;}

        public event PropertyChangedEventHandler PropertyChanged;

        //定義一個ICommand型別的參數,他會回傳實作ICommand介面的RelayCommand類別。
        public ICommand UpdateTitleName { get { return new RelayCommand(UpdateTitleExecute, CanUpdateTitleExecute); } }

        public PostsViewModel()
        {
            posts = new Posts { postsText = "", postsTitle = "Unknown" };
        }

        public string PostsTitle
        {
            get { return posts.postsTitle; }
            set 
            {
                if (posts.postsTitle != value)
                {
                    posts.postsTitle = value;
                    RaisePropertyChanged("postsTitle");
                }
            }
        } 

        //產生事件的方法
        private void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        //更新Title,原本放在View那邊的邏輯,藉由繫結的方式來處理按下Button的事件。
        void UpdateTitleExecute()
        {
            PostsTitle = "SkyMVVM";
        }

        //定義是否可以更新Title
        bool CanUpdateTitleExecute()
        {
            return true;
        }
    }
}

這樣,ViewModel就完成了,接下來是處理RelayCommand。

撰寫RelayCommand

RelayCommand是實作ICommand的類別,一開始我們就可以看到定義了Func和Action,這兩個就是用來參考到剛剛傳入進來的兩個方法,並進行了一些的處理。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Diagnostics;

namespace WPFMVVM3
{
    public class RelayCommand :ICommand
    {

        readonly Func _canExecute;
        readonly Action _execute;

        public RelayCommand(Action execute)
            : this(execute, null)
        {
        }

        public RelayCommand(Action execute, Func canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");
            _execute = execute;
            _canExecute = canExecute;
        }

        public event EventHandler CanExecuteChanged
        {
            add
            {

                if (_canExecute != null)
                    CommandManager.RequerySuggested += value;
            }
            remove
            {

                if (_canExecute != null)
                    CommandManager.RequerySuggested -= value;
            }
        }

        [DebuggerStepThrough]
        public Boolean CanExecute(Object parameter)
        {
            return _canExecute == null ? true : _canExecute();
        }

        public void Execute(Object parameter)
        {
            _execute();
        }
    }
}

最後,我們必須重新調整XMAL。

調整XMAL

最後的一個動作,我們將Button的Click屬性拿掉了,改成Command屬性,並將之Binding到ViewModel的UpdateTitleName屬性,就完成了。

<Window x:Class="WPFMVVM3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WPFMVVM3"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <!-- 實例化PostViewModel -->
        <local:PostsViewModel />
    </Window.DataContext>
    <Grid>
        <Label  Content="{Binding PostsTitle}"    Height="28" HorizontalAlignment="Left" Margin="12,12,0,0" Name="label1" VerticalAlignment="Top" />
        <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="145,13,0,0" Name="button1" VerticalAlignment="Top" Width="75" Command="{Binding UpdateTitleName}" />
    </Grid>
</Window>

以上就完成了!

結尾

到這邊就算是手工打造MVVM的一個段落了,其實現在有許多的Framework可以幫我們處理一些繁雜的事物,但如果能了解原理,我相信會更有幫助,所以這次用了循序漸進的方式,來記錄這些內容,也希望能給大家一點幫助!

參考資料

  • http://www.codeproject.com/KB/WPF/WpfMvvmQuickStart.aspx

  • [http://www.codeproject.com/KB/WPF/MVVMMadeSimple.aspx](http://www.codeproject.com/KB/WPF/MVVMMadeSimple.aspx)
  • [http://www.codeproject.com/KB/WPF/MVVMQuickTutorial.aspx](http://www.codeproject.com/KB/WPF/MVVMQuickTutorial.aspx)
  • [http://csharperimage.jeremylikness.com/2010/04/model-view-viewmodel-mvvm-explained.html](http://csharperimage.jeremylikness.com/2010/04/model-view-viewmodel-mvvm-explained.html)
Sky & Study4.TW