2014年8月14日木曜日

データバインディングを使ったHello World

今度は前回(Lesson 1 : クラシックなHello World)と同じ動作をする画面で、オブジェクトのプロパティを使用しない方法を試してみたいと思います。



まずは、この画面に使用される、「名前」と「メッセージ」をもつクラスを作成します。

MainViewModel.cs
namespace HelloWorld
{
    class MainViewModel
    {
        private string _Name;
        private string _Message;

        public MainViewModel()
        {
        }

        public string Name
        {
            get
            {
                return _Name;
            }
            set
            {
                _Name = value;
            }
        }

        public string Message
        {
            get
            {
                return _Message;
            }
            set
            {
                _Message = value;
            }
        }
    }
}

ローカル変数 _Name と _Message を定義して、そのgetter, setterをpublicにしています。

これを「ViewModel」として、xamlの中に取り入れていきます。

MainWindow.xaml.cs
<Window x:Class="HelloWorld.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <Label Content="名前を入れてね" />
        <TextBox Text="{Binding Name}" />
        <Button Name="btnEnter" Content="Enter" Click="btnEnter_Click" />
        <TextBlock Text="{Binding Message}" />
    </StackPanel>
</Window>

前回から大きく変わったのが、次の2点。
・TextBox, TextBlockから、Nameプロパティが消えた。
・TextBox, TextBlockの値プロパティTextの値が「{Binding 。。。}」となった。

この「{Binding 。。。}」がMVVMのキモっぽいです。どうやら。

次に、先ほどのMainViewModelと、このxamlをどう接続させるか。
それがコードビハインドに入りました。

MainWindow.xaml.cs
using System.Windows;

namespace HelloWorld
{
    public partial class MainWindow : Window
    {
        private MainViewModel _viewmodel;

        public MainWindow()
        {
            InitializeComponent();
            _viewmodel = new MainViewModel();
            DataContext = _viewmodel;
        }

        private void btnEnter_Click(object sender, RoutedEventArgs e)
        {
            _viewmodel.Message = "こんにちは、" + _viewmodel.Name + "さん!";
        }
    }
}

ローカル変数としてMainViewModelを定義し、コンストラクタでnewしています。
そして、DataContextに割り当てています。

これで接続は完了なのです。ふむふむ。

また、ボタンイベントの処理は、Nameプロパティで探して.Textプロパティで値を・・・という処理から、直接stringの編集にもっていけるのがコーディングが楽になるポイントでしょうか。

さて、これを実行・・・!としても、実は動きません。

ここがMVVMのややこしいところ。

どうして動かないのかというと、xamlのほうがViewModelの変数が変わったことに気づいていないからなのです(xamlの気持ちになったことがなく、よく分からないので、たぶん、気づいていないのです)。

では気づいてもらうために・・・RaisePropertyChangedというメッセージを送ってあげます。

さて、どうするか。
面倒なので、答えのコードをここに載せます。

ViewModelBase.cs
using System.ComponentModel;

namespace HelloWorld
{
    /// 
    /// ViewModelの基底クラス
    /// 
    class ViewModelBase : INotifyPropertyChanged
    {
        /// 
        /// プロパティ変更のイベント
        /// 
        public event PropertyChangedEventHandler PropertyChanged;

        /// 
        /// プロパティ変更のイベントを発行する
        /// 
        /// 
        internal void RaisePropertyChanged(string prop)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(prop));
            }
        }
    }
}
このクラスを作成して、ViewModelのほうはこのクラスを継承させます。
もうこれは完全にテンプレート、呪文だと思って黙って入れる、でいいんですね。

MainViewModel.cs 改訂版
namespace HelloWorld
{
    class MainViewModel : ViewModelBase
    {
        private string _Name;
        private string _Message;

        public MainViewModel()
        {
        }

        public string Name
        {
            get
            {
                return _Name;
            }
            set
            {
                if (_Name != value)
                {
                    _Name = value;
                    RaisePropertyChanged("Name");
                }
            }
        }

        public string Message
        {
            get
            {
                return _Message;
            }
            set
            {
                if (_Message!= value)
                {
                    _Message = value;
                    RaisePropertyChanged("Message");
                }
            }
        }
    }
}
変わったのは、ViewModelBaseを継承したことと、setterでRaisePropertyChangedを書いたことです。

こう変更したことでどう動くのか?
・NameまたはMessageが変更になったとき、RaisePropertyChangedが動く。
・ViewModelBaseにより、INotifyPropertyChangedにより、プロパティの変更がxamlに通知される。
・xamlが気づいて、表示を変える。
んだと思う。。。

少しだけコードがスッキリしたような、余計にファイルが増えてややこしくなったような。

続いて、コマンドバインディングを使ってコードビハインドを完全にフリーにしてやります。
と思っていたのですが、やはりファイルが余計に増えてしまい、ViewModelも冗長なコーディングの塊になってしまうことから、MVVMって実際どうなの?っていう気持ちが強くなってきました。

ちょっと、考えてみます。


0 件のコメント:

コメントを投稿