C#初心者のWPF備忘録 P.01 ~MVVMパターンと基本のBinding~

※本記事はC#やWPFはあまり経験がないけれど、他の言語は触ったことがある方にお勧めします
※本記事は初心者の備忘録です


##WPFとMVVMパターンのプログラミング
早速ですがWPFはMVVMというプログラミングの構造(?)で書かれるようです。
MVVMはModel-View-ViewModelの大文字部分をつなげたもので
Model・・・見た目(UI)には関係のないロジックの部分
View・・・見た目(UI)に関係するデザインの部分
ViewModel・・・ModelとViewの間に立ちModelとViewを直接結ばない役割をもつ部分
という解釈をしています。
(MVVMについては様々な考え方があるようです)
WPFではView(見た目)は基本的にXAML(ザムルと読むようです)を使って書きます。
基本的な書き方は<XXXX>中身</XXXX>のように開始タグと終了タグで挟みます。
例えばLabelであれば
\<Label>Hello World!\</Label>
と書くことで"Hello World!"と書かれたLabelを置くことができます。
また、TextBoxのように中身が必要のないものについては
\<TextBox>\</TextBox>
と中身を省略するか
\<TextBox />
のように書くこともできます。
ModelとViewModelはC#を使って記述するようですが、C#の記述についてはここでは省略します。

##ModelとViewの分離と基本のBinding
なぜMVVMという構造をとろうとするのかという話になりますが
ロジック(Model)部分と見た目(View)を分けたいから、ということのようです。
さっそく一つプロジェクトを作成しコードを書いてみます。

```XML:MainWindow.xaml
<Window x:Class="BIBOROKU_001.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:BIBOROKU_001"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <StackPanel>
        <TextBox Name="NameBox" TextChanged="TextChanged" />
        <TextBlock Name="Morining" />
        <TextBlock Name="Noon" />
        <TextBlock Name="Evening" />
    </StackPanel>
</Window>
```
```cs:MainWindow.xaml.cs
///usingは省略しています

namespace BIBOROKU_001
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void TextChanged(object sender, TextChangedEventArgs e)
        {
            if(NameBox.Text != "")
            {
                Morining.Text = NameBox.Text + "さん、おはようございます！";
                Noon.Text = NameBox.Text + "さん、こんにちは！";
                Evening.Text = NameBox.Text + "さん、こんばんは！"; 
            }
            else
            {
                Morining.Text = "";
                Noon.Text =  "";
                Evening.Text =  "";
            }
        }
    }
}
```
WindowsFormアプリケーションなんかではこのようにTextBoxのTextChangedイベントに絡めて
TextBlockの中身を書き換えるような記述になると思います。ただこのような記述は修正が大変で
XAML側の"NameBox"という名前を"NameBox1"にしたいとなると、それに合わせてC#側のコードも
書き換えなければなりません。これが分離できていない、という一例だと思います。
そこで新たにMainWindowWPF.xamlというウィンドウを作って次のように書いてみます。

```XML:MainWindowWPF.xaml
<Window x:Class="BIBOROKU_001.MainWindowWPF"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:BIBOROKU_001"
        mc:Ignorable="d"
        Title="MainWindowWPF" Height="450" Width="800">
    <StackPanel>
        <TextBox Text="{Binding InputName,Mode=OneWayToSource,UpdateSourceTrigger=PropertyChanged}" />
        <TextBlock Text="{Binding Morining,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}" />
        <TextBlock Text="{Binding Noon,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}" />
        <TextBlock Text="{Binding Evening,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}" />
    </StackPanel>
</Window>
```
なにやら少しにぎやかになってきました。`MainWindow.xaml`と比べ`Name="xxxxx"`という記述が
なくなり代わりに`Text="{Binding xxxx}"`となっています。Bindは結びつけるといった意味ですので`TextBox`を例にすると

```XML
<TextBox Text="{Binding InputName, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}" />
```
`TextBox`の`Text`は
①`InputName`に結び付けて
②`Mode`は`OneWayToSource`(ソース方向への一方通行)
③`UpdateSourceTrigger`は`PropertyChanged`(プロパティが変わったら)
という意味のようです。
Modeにはほかに`TwoWay`や`OneWay`(ソースからの一方通行)などが
UpdateSourceTriggerには`Explicit`や`LostFocus`などがあるようです。
またこのときのC#側の記述は

```cs:MainWindowWPF.xaml.cs

///usingは省略しています

namespace BIBOROKU_001
{
    /// <summary>
    /// MainWindowWPF.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindowWPF : Window
    {
        public MainWindowWPF()
        {
            InitializeComponent();
            this.DataContext = new MainWindowWPFVM();
        }

        ///実際にはここから先は分離して別ファイルに

        public class MainWindowWPFVM : INotifyPropertyChanged
        {
            public MainWindowWPFVM()
            {
            }
            private string _InputName;
            public string InputName
            {
                get
                {
                    return _InputName;
                }

                set
                {
                    _InputName = value;
                    RaisePropertyChanged();
                    RaisePropertyChanged("Morining");
                    RaisePropertyChanged("Noon");
                    RaisePropertyChanged("Evening");
                }
            }

            public string Morining
            {
                get
                {
                    if (InputName != "")
                        return InputName + "さん、おはようございます！";
                    else
                        return "";
                }
            }
            public string Noon
            {
                get
                {
                    if (InputName != "")
                        return InputName + "さん、こんにちは！";
                    else
                        return "";
                }
            }
            public string Evening
            {
                get
                {
                    if (InputName != "")
                        return InputName + "さん、こんばんは！";
                    else
                        return "";
                }
            }

            //INotifyPropertyChanged実装
            public event PropertyChangedEventHandler PropertyChanged = delegate { };

            //INotifyPropertyChanged.PropertyChangedイベントを発生させる
            private void RaisePropertyChanged([CallerMemberName]string propertyName = "")
            {
                if (propertyName != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }


}

```
こちらもにぎやかですね。ただ`MainWindowWPF`の中はずいぶんとすっきりしました。
唯一増えたのは`this.DataContext = new MainWindowWPFVM();`の部分だけです。
Context自体は"文脈"のような意味があるようですが、分かりにくいのでここでは"情報"と捉えると
この(MainWindowWPF)データの情報はMainWindowWPFVMのインスタンスにありますよ
という感じでしょうか。
そしてその`MainWindowWPFVM`内には、XAML側で`Binding`した`InputName`や`Morining,Noon,Evening`といったプロパティが含まれています。ただし単にプロパティを定義しただけではだめで、プロパティが変わったら変更通知をしなければならないようです。
`INotifyPropertyChanged`と`RaisePRopertyChanged`というのがそれにあたるようですが、今回は省略します。

##今回のまとめ
初回ということで、WPFとMVVMパターンについて、基本的なBindingについて書きました。
１.構造をModel-View-ViewModelに分ける
２.ViewではViewModelクラスのインスタンスの生成しDataContextにBindingする
３．Bindingではプロパティを変更したら変更した通知がないと更新されない

今回はこのあたりで失礼します。