※本記事は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)を分けたいから、ということのようです。
さっそく一つプロジェクトを作成しコードを書いてみます。
<Windowx: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><TextBoxName="NameBox"TextChanged="TextChanged"/><TextBlockName="Morining"/><TextBlockName="Noon"/><TextBlockName="Evening"/></StackPanel></Window>
///usingは省略していますnamespaceBIBOROKU_001{/// <summary>/// MainWindow.xaml の相互作用ロジック/// </summary>publicpartialclassMainWindow:Window{publicMainWindow(){InitializeComponent();}privatevoidTextChanged(objectsender,TextChangedEventArgse){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というウィンドウを作って次のように書いてみます。
<Windowx: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><TextBoxText="{Binding InputName,Mode=OneWayToSource,UpdateSourceTrigger=PropertyChanged}"/><TextBlockText="{Binding Morining,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"/><TextBlockText="{Binding Noon,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"/><TextBlockText="{Binding Evening,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"/></StackPanel></Window>
なにやら少しにぎやかになってきました。MainWindow.xaml
と比べName="xxxxx"
という記述が
なくなり代わりにText="{Binding xxxx}"
となっています。Bindは結びつけるといった意味ですのでTextBox
を例にすると
<TextBoxText="{Binding InputName, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"/>
TextBox
のText
は
①InputName
に結び付けて
②Mode
はOneWayToSource
(ソース方向への一方通行)
③UpdateSourceTrigger
はPropertyChanged
(プロパティが変わったら)
という意味のようです。
ModeにはほかにTwoWay
やOneWay
(ソースからの一方通行)などが
UpdateSourceTriggerにはExplicit
やLostFocus
などがあるようです。
またこのときのC#側の記述は
///usingは省略していますnamespaceBIBOROKU_001{/// <summary>/// MainWindowWPF.xaml の相互作用ロジック/// </summary>publicpartialclassMainWindowWPF:Window{publicMainWindowWPF(){InitializeComponent();this.DataContext=newMainWindowWPFVM();}///実際にはここから先は分離して別ファイルにpublicclassMainWindowWPFVM:INotifyPropertyChanged{publicMainWindowWPFVM(){}privatestring_InputName;publicstringInputName{get{return_InputName;}set{_InputName=value;RaisePropertyChanged();RaisePropertyChanged("Morining");RaisePropertyChanged("Noon");RaisePropertyChanged("Evening");}}publicstringMorining{get{if(InputName!="")returnInputName+"さん、おはようございます!";elsereturn"";}}publicstringNoon{get{if(InputName!="")returnInputName+"さん、こんにちは!";elsereturn"";}}publicstringEvening{get{if(InputName!="")returnInputName+"さん、こんばんは!";elsereturn"";}}//INotifyPropertyChanged実装publiceventPropertyChangedEventHandlerPropertyChanged=delegate{};//INotifyPropertyChanged.PropertyChangedイベントを発生させるprivatevoidRaisePropertyChanged([CallerMemberName]stringpropertyName=""){if(propertyName!=null)PropertyChanged(this,newPropertyChangedEventArgs(propertyName));}}}}
こちらもにぎやかですね。ただMainWindowWPF
の中はずいぶんとすっきりしました。
唯一増えたのはthis.DataContext = new MainWindowWPFVM();
の部分だけです。
Context自体は"文脈"のような意味があるようですが、分かりにくいのでここでは"情報"と捉えると
この(MainWindowWPF)データの情報はMainWindowWPFVMのインスタンスにありますよ
という感じでしょうか。
そしてそのMainWindowWPFVM
内には、XAML側でBinding
したInputName
やMorining,Noon,Evening
といったプロパティが含まれています。ただし単にプロパティを定義しただけではだめで、プロパティが変わったら変更通知をしなければならないようです。INotifyPropertyChanged
とRaisePRopertyChanged
というのがそれにあたるようですが、今回は省略します。
今回のまとめ
初回ということで、WPFとMVVMパターンについて、基本的なBindingについて書きました。
1.構造をModel-View-ViewModelに分ける
2.ViewではViewModelクラスのインスタンスの生成しDataContextにBindingする
3.Bindingではプロパティを変更したら変更した通知がないと更新されない
今回はこのあたりで失礼します。