C#でMVVMアプリを作ってみる
C#のXAMLを使ったWPFアプリを、MVVMパターンで作ってみます。MVVMについては、他のページを参照してください。 下記の環境で試しました。
Visual Studio 2015 Express for Windows Desktop (C#)
目次
作ってみた
何を作る?
下記のようなアプリを作ります。
テキストボックスに入力した文字列を、ボタンを押したら過去に入力済みの文字列に付け加える形で保持します。
ラベルに保持している入力済み文字列を表示します。
入力済み文字列をクリアするボタンを付けます。
View
まず、Viewを作ります。フォームに、ボタン2つと、テキストボックスを1つと、ラベルを1つ配置します。上から順に、テキストボックス、ボタン、ラベル(見えませんが)、ボタンの順に並べてます。 XAMLは下記のようになります。
<Window x:Class="mvvm_trial.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:mvvm_trial"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="200">
<Grid>
<TextBox x:Name="textBox" Text="{Binding InputText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="175"/>
<Button x:Name="buttonAddItem" Command="{Binding AddItem}" Content="Add Item" HorizontalAlignment="Left" Margin="10,38,0,0" VerticalAlignment="Top" Width="175"/>
<Button x:Name="buttonReset" Command="{Binding ResetItem}" Content="Reset" HorizontalAlignment="Left" Margin="10,126,0,0" VerticalAlignment="Top" Width="175"/>
<Label x:Name="label" Content="{Binding StoredText, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Margin="10,83,0,0" VerticalAlignment="Top" Width="175"/>
</Grid>
</Window>
ViewのコントロールとViewModelのプロパティのバインディングは下表のようにしました。
TextBox |
Text |
InputText |
Text="{Binding InputText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" |
Button |
Command |
AddItem |
Command="{Binding AddItem}" |
Label |
Content |
StoredText |
Content="{Binding StoredText, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" |
Button |
Command |
ResetItem |
Command="{Binding ResetItem}" |
Mode=TweWay
は、ViewからViewModelを変更することもあるしViewModelからViewを変更することもあるということです。ユーザーがテキストボックスに入力した文字はデータに反映させなきゃいけないし、入力確定してボタンを押したらViewModelのデータをクリアしますのでそれをテキストボックスに反映しますので、両方向からプロパティが更新されるからです。
UpdateSourceTrigger=PropertyChanged
は表示の内容を更新するタイミングの指定です。PropertyChangedというイベントが生じたときに表示が更新されるようにします。
Modelの一部
先にデータストック用のクラスのコードです。
namespace mvvm_trial
{
public class DataStock
{
public string stockedItem { get; set; }
}
}
文字列のデータを保持するだけのクラスです。
ViewModel
ViewModelのコードです。
using System.Windows.Input;
using Microsoft.TeamFoundation.MVVM;
namespace mvvm_trial
{
public class MainWindowViewModel : ViewModelBase
{
// Addボタンとのバインド
private ICommand _AddItem;
public ICommand AddItem
{
get
{
if (_AddItem == null)
{
_AddItem = new RelayCommand(ExecuteAddItem);
}
return _AddItem;
}
}
private void ExecuteAddItem()
{
_stocker.stockedItem = _stocker.stockedItem + InputText;
InputText = string.Empty;
RaisePropertyChanged("StoredText");
}
// Resetボタンとのバインド
private ICommand _ResetItem;
public ICommand ResetItem
{
get
{
if (_ResetItem == null)
{
_ResetItem = new RelayCommand(ExecuteResetItem);
}
return _ResetItem;
}
}
private void ExecuteResetItem()
{
_stocker.stockedItem = string.Empty;
RaisePropertyChanged("StoredText");
}
// データの場所を保管するためのフィールドとプロパティ
private DataStock _stocker;
public DataStock Stocker
{
set
{
_stocker = value;
}
}
// テキストボックスとのバインド
private string _InputText;
public string InputText
{
get { return _InputText; }
set
{
_InputText = value;
RaisePropertyChanged("InputText");
}
}
// ラベルとのバインド
public string StoredText
{
get
{
return _stocker.stockedItem;
}
}
}
}
以下、小分けして解説をメモっておきます。 usingでMicrosoft.TeamFoundation.MVVM名前空間を追加します。このコードを書く前に、ソリューションエクスプローラーでMicrosoft.TeamFoundation.Controlsを追加しておいてください。
using Microsoft.TeamFoundation.MVVM;
Microsoft.TeamFoundation.MVVM名前空間のViewModelBaseを継承します。
public class MainWindowViewModel : ViewModelBase
AddItemボタンが押された時の動作の設定です。AddItemプロパティにアクセスされると、getアクセサーが呼び出されます。そうすると、RelayCommand()がExcecuteAddItemメソッドを呼び出します。ExcecuteAddItemメソッドは、保存されているデータの文字列にInputTextプロパティの中身を追加して、InputTextプロパティを空にし、RaisePropertyChanged()メソッドでStoredTextプロパティが変更されたというPropertyChangedイベントを発行します。そうすると、StoredTextプロパティにバインディングされたLabelのContentプロパティが更新されます。
private ICommand _AddItem;
public ICommand AddItem
{
get
{
if (_AddItem == null)
{
_AddItem = new RelayCommand(ExecuteAddItem);
}
return _AddItem;
}
}
private void ExecuteAddItem()
{
_stocker.stockedItem = _stocker.stockedItem + InputText;
InputText = string.Empty;
RaisePropertyChanged("StoredText");
}
もう一つのボタンのコードも同様の流れです。 Stockerプロパティは、データ保存用インスタンスの「参照」を受け取るプロパティです。というわけで、setアクセサーだけです。
private DataStock _stocker;
public DataStock Stocker
{
set
{
_stocker = value;
}
}
テキストボックスとのバインディングですが、setアクセサーでRaisePropertyChanged()メソッドを実行して、自身のPropertyChangedイベントを発行するようにします。InputTextプロパティに値が入れられたときに、このイベントでTextBoxのTextプロパティを更新するわけです。具体的には、ExcecuteAddItem()メソッドの中でInputTextプロパティに空文字を入れてますが、その際にPropertyChangedイベントを発行してTextBoxのTextプロパティを更新するのです。
private string _InputText;
public string InputText
{
get { return _InputText; }
set
{
_InputText = value;
RaisePropertyChanged("InputText");
}
}
Model
最後に、Modelの本体部分のコードです。
using System.Windows;
namespace mvvm_trial
{
public partial class MainWindow : Window
{
public DataStock stocker = new DataStock(); // データ用のインスタンスの生成
public MainWindow()
{
InitializeComponent();
stocker.stockedItem = string.Empty; // データの初期化
MainWindowViewModel mainWindowVM = new MainWindowViewModel(); // ViewModelの生成
mainWindowVM.Stocker = stocker; // ViewModelへデータ用インスタンスの「参照」を渡す
this.DataContext = mainWindowVM; // ViewModelとViewをバインド
}
}
}
このアプリではModel部分ではデータの保持しかしませんので、必要なインスタンスを作ってバインディングの処理をしているだけです。
まとめ
MVVMの処理の流れは、ざっくり言うとこんな感じでしょうか。
データ保持用インスタンス(Model)をViewModelのデータ用プロパティに参照渡しする。
ViewModelのデータ用プロパティと、データ表示用コントロール(View)(テキストボックスとか)のプロパティをバインディングする。
ViewModelの操作用プロパティと、入力用のコントロール(View)(ボタンとか)のプロパティをバインディングする。
ユーザーが入力用コントロールを操作すると、ViewModelのプロパティのgetアクセサーが呼び出される。
ViewModelのgetアクセサーがViewModelの処理用メソッドを呼び出す。
ViewModelの処理用メソッドがViewModelのデータ用プロパティを操作する。
ViewModelの処理用メソッドでデータ用プロパティ変更のイベントを発行する。
データ用プロパティにバインディングされたViewのプロパティが更新される。
なんというか、結構遠回りな感じがします。
公開日
広告
C#で作ってみたカテゴリの投稿
- C#でExcelの表のコピーをMarkdownに変換するアプリを作ってみた
- C#でHello Worldしてみた
- C#でMVVMアプリを作ってみる
- C#でMVVMパターンのアプリを作ってみた(Microsoft.TeamFoundation.Controls無し編)
- C#でWPFでHello worldしてみた
- C#でdirコマンドの出力を整形してみた
- C#で作った特許情報の入力を補助するツールをちょっと直してみた
- C#で数式を中置記法から後置記法(逆ポーランド記法)に変換してみた
- C#で正規表現を試すツールを作ってみた
- C#で特許情報の入力を補助するツールを作ってみた
- C#で画像のHSBを抽出して描くツールを作ってみた
- C#で画像をグレースケールや半透明に変換するアプリを作ってみた
- テキストファイルをCSV形式に変換するツールを作ってみた