※本記事は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)を分けたいから、ということのようです。
さっそく一つプロジェクトを作成しコードを書いてみます。
<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>
///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というウィンドウを作って次のように書いてみます。
<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}" />
TextBox
のText
は
①InputName
に結び付けて
②Mode
はOneWayToSource
(ソース方向への一方通行)
③UpdateSourceTrigger
はPropertyChanged
(プロパティが変わったら)
という意味のようです。
ModeにはほかにTwoWay
やOneWay
(ソースからの一方通行)などが
UpdateSourceTriggerにはExplicit
やLostFocus
などがあるようです。
またこのときのC#側の記述は
///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について書きました。
1.構造をModel-View-ViewModelに分ける
2.ViewではViewModelクラスのインスタンスの生成しDataContextにBindingする
3.Bindingではプロパティを変更したら変更した通知がないと更新されない
今回はこのあたりで失礼します。