LoginSignup
2
4

More than 3 years have passed since last update.

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

Posted at

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

WPFとMVVMパターンのプログラミング

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

ModelとViewの分離と基本のBinding

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

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>
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というウィンドウを作って次のように書いてみます。

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を例にすると

<TextBox Text="{Binding InputName, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}" />

TextBoxText
InputNameに結び付けて
ModeOneWayToSource(ソース方向への一方通行)
UpdateSourceTriggerPropertyChanged(プロパティが変わったら)
という意味のようです。
ModeにはほかにTwoWayOneWay(ソースからの一方通行)などが
UpdateSourceTriggerにはExplicitLostFocusなどがあるようです。
またこのときのC#側の記述は

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したInputNameMorining,Noon,Eveningといったプロパティが含まれています。ただし単にプロパティを定義しただけではだめで、プロパティが変わったら変更通知をしなければならないようです。
INotifyPropertyChangedRaisePRopertyChangedというのがそれにあたるようですが、今回は省略します。

今回のまとめ

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

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

2
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4