2014年8月21日木曜日

Hello World 完成版。

ViewModelを使った、私個人なりのベストなHello World。

動作は今まで通り



です。

MainWindow.xaml
<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 Content="Enter" Click="ButtonEnter_Click" />
        <TextBlock Text="{Binding Message}" />
    </StackPanel>
</Window>


MainWindow.xaml.cs
using System.Windows;

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

        /// 
        /// Constructor
        /// 
        public MainWindow()
        {
            InitializeComponent();
            _viewmodel = new MainViewModel();
            this.DataContext = _viewmodel;
        }

        /// 
        /// ボタン押下時のイベント処理
        /// 
        /// 
        /// 
        private void ButtonEnter_Click(object sender, RoutedEventArgs e)
        {
            _viewmodel.SetMessage();
        }
    }
}


MainViewModel.cs
using System;
using System.Windows;
using System.Windows.Input;
namespace HelloWorld
{
    /// 
    /// MainWindowのViewModel
    /// 
    class MainViewModel : ViewModelBase
    {
        private string _Name;
        private string _Message;

        /// 
        /// Constructor
        /// 
        public MainViewModel()
        {
        }

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

        /// 
        /// Message Property
        /// 
        public string Message
        {
            get
            {
                return _Message;
            }
            set
            {
                if (_Message != value)
                {
                    _Message = value;
                    RaisePropertyChanged("Message");
                }
            }
        }
        #endregion

        /// 
        /// メッセージをセットする処理
        /// 
        public void SetMessage()
        {
            Message = "こんにちは、" + Name + "さん!";
        }
    }
}


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));
            }
        }
    }
}


変わったところは、MainWindow.xaml.csのボタンのイベントハンドラの処理内容をViewModelに書いたことでしょうか。
イベントの受けはxaml.csでやっても、ビジネスロジックは、ViewModelに書きたい。というところがポイントです。
しっかし、ViewModelは長くなるなぁ。。。なんかいい方法ないかなぁ。

さぁて、次はどんなプログラムを作ろうかな。

Coffee Break : 私の結論(MVVMは果たして効率的なのか?)

しばらく悩んでおりました。
適当な理由をつけて、結果を出しました。

「MVVMにするのは、コードビハインドを排除していく一つの手段であるが

何が何でもコードビハインドを排除する必要はないんじゃないか。」

まぁ
XAMLとプログラムを別の人が作るってのは理想ではあるけれど

実際は
全部自分たちで作るのがSEなわけで

まぁ
BtoCみたいなサイトならまだしも

inBだったりBtoBのシステムに関してXAMLに凝るものなんて無いじゃん?

じゃぁコードビハインドに書いたっていいじゃんね?

コマンドまでRelayだなんだで面倒なことわざわざやる必要ない!
ClickだったらClickのイベントハンドラからViewModelにアクセスしにいけばいいじゃん。


ただ、データバインディングという便利な機能は活用していくとして、画面とデータのやり取りをするような場合にはViewModelは必要なんだろうね。

という結論です。
それが生産性・可視性・保守性が一番バランス良さそう。

ではHello Worldの締めに入るか。

2014年8月14日木曜日

Coffee Break : MVVMは果たして効率的なのか?

ラストエントリの最後にも書きましたが、MVVMのViewModelにおけるコーディングが肥大化してしまうことが問題点に思えてきてなりません。

さらに、ViewModelに記述した名称と、xamlに書いた名称が合わなくても、xamlはサラッとエラーも出さずに澄ました顔してます。

もし何十と入力項目のあるような画面で、一部うまく表示されない、という場合にコードのどこに原因があるのか、開発者以外の人間が見て、すぐにわかるでしょうか?

MVVMフレームワークとして、「書き方は、こう!」という定型化されたものがあれば良いのですが、他人の書いたコードを見ていて、人によって書き方にバラツキが出てきているのが実情のようです。

google先生に聞いても、いろんな書き方がばらついているような気がします。



モノによっては、コードビハインドに処理をガリガリ書いたほうが視認性が高かったり、デバッグしやすかったり、MVVMを使うより楽なものも多そうです。

MVVMとして得た知識は経験として置いておき、もうちょっと違う目線でブログにしていこうかな、と思っています。


なのでブログタイトルは多分変わりますw

それではまた。

データバインディングを使った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って実際どうなの?っていう気持ちが強くなってきました。

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


クラシックなHello World

まずはMVVMとか難しいこと考えずに、昔ながらの方法で、HelloWorldしてみたいと思います。

画面にはラベルとテキストボックス、ボタンを配置しました。
ボタンを押すと、テキストボックスの内容を読んで、メッセージを表示します。




メッセージを入れると・・・




くっそ、おっさん呼ばわりですww

コードはこちら。

MainWindow.xaml
<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 Name="txtName" Text="" />
        <Button Name="btnEnter" Content="Enter" Click="btnEnter_Click" />
        <TextBlock Name="txtMessage" Text="" />
    </StackPanel>
</Window>


TextBox、TextBlock、それぞれのオブジェクトにName属性を指定して、Clickイベントを指定します。

MainWindow.xaml.cs
using System.Windows;

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

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


Clickイベントにて、xamlのTextBlockの入力値を取得して、TextBoxの入力値にセットする・・・

というクラシックなHello Worldができました。


さて、MVVMでは、「データバインド」という方法でコードビハインドを無くしてくことができるようですので、次はこの「 .Text 」を無くしていってみようと思います。