4.サンプル
ObservableCollectionを使ったサンプル
モデルとObservableCollectionの関係をDataGridを使っての解説なので、それ以外の機能は極力省いています。
下のテキストボックスに、No、名前、就任日、退任日を入力して、「追加」ボタンをクリックしますと、ObservableCollectionにデータが追加されて、これとバインディングしているDataGridに追加されという仕組みです。
PropertyChanged
Noを入力するまでは、「追加」ボタンは非使用になっています。入力されると使用可能となります。これは、NoテキストボックスにUpdateSourceTrigger=PropertyChangedという記述をして実現しています。
「追加」ボタンのクリック後
このサンプルのZAMLは下記の通ります。DataGridとデータとの結びつけが、「ItemsSource="{Binding personList~"」の ところです。データの追加は、「IsReadOnly="false"」の記述で可能となっています。
View
<Window x:Class="LivetWPFDataGrid.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
xmlns:v="clr-namespace:LivetWPFDataGrid.Views"
xmlns:vm="clr-namespace:LivetWPFDataGrid.ViewModels"
Title="MainWindow" Height="350" Width="525"
WindowStartupLocation="CenterScreen">
<Window.DataContext>
<vm:MainWindowViewModel/>
</Window.DataContext>
<i:Interaction.Triggers>
<!--WindowのContentRenderedイベントのタイミングでViewModelのInitialize
メソッドが呼ばれます-->
<i:EventTrigger EventName="ContentRendered">
<l:LivetCallMethodAction MethodTarget="{Binding}"
MethodName="Initialize"/>
</i:EventTrigger>
<!--Windowが閉じたタイミングでViewModelのDisposeメソッドが呼ばれます-->
<i:EventTrigger EventName="Closed">
<l:DataContextDisposeAction/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
<DataGrid Name="dataGrid" ItemsSource="{Binding personList,
Mode=OneWay}" IsReadOnly="false"
AutoGenerateColumns="False" Margin="9,12,12,74">
<DataGrid.Columns>
<DataGridTextColumn Header="No" Width="40"
Binding="{Binding No, StringFormat=D2}" />
<DataGridTextColumn Header="名前" Width="80"
Binding="{Binding Name}" />
<DataGridTextColumn Header="就任日" Width="70"
Binding="{Binding InaugurationDay,
StringFormat=yyyy/MM/dd}" />
<DataGridTextColumn Header="退任日" Width="70"
Binding="{Binding RetiringDay,
StringFormat=yyyy/MM/dd}" />
</DataGrid.Columns>
</DataGrid>
<Button Content="追加" Command="{Binding InsertData}"
Height="23" HorizontalAlignment="Left" Margin="416,276,0,0"
Name="button1"
VerticalAlignment="Top" Width="75" />
<TextBox Text="{Binding Path=txtNo, Mode=TwoWay}" Height="18"
HorizontalAlignment="Left" Margin="9,243,0,0" Name="textBox1"
VerticalAlignment="Top" Width="82" />
<TextBox Text="{Binding Path=txtName, Mode=TwoWay}" Height="18"
HorizontalAlignment="Left" Margin="97,243,0,0" Name="textBox2"
VerticalAlignment="Top" Width="90" />
<TextBox Text="{Binding Path=txtInaugurationDay, Mode=TwoWay}" Height="18"
HorizontalAlignment="Left" Margin="193,243,0,0" Name="textBox3"
VerticalAlignment="Top" Width="104" />
<TextBox Text="{Binding Path=txtRetiringDay, Mode=TwoWay}" Height="18"
HorizontalAlignment="Left" Margin="303,243,0,0" Name="textBox4"
VerticalAlignment="Top" Width="104" />
</Grid>
</Window>
ViewModelであるMainWindowViewModel.csは下記です。
7~13行は、Livetのライブラリの宣言です。16行目は、ObservableCollectionを使用する為の宣言です。
Livetを使用するとプログラマーはICommandの宣言は不要です。
ICommandとは、WPFが提供するコマンド用のインターフェイスです。
24~37行はtxtNoの変更通知プロパティの宣言ですが、 txtNoは入力値が変わったら、PropertyChangedイベントを発して、合わせて、34行目でRaiseCanExecuteChangedを発しています。
39行目は、txtNameの変更通知プロパティの宣言
40行目は、txtInaugurationDay(就任日)の変更通知プロパティの宣言
41行目は、txtRetiringDay(退任日)の変更通知プロパティの宣言
で、2.WPFの「One Point Lesson 【プロパティの自動実装】」で解説した「プロパティの自動実装」で記述しています。
48~58行でモデルPersonのプロパティを設定しています。
62~73行でPersonのObservableCollectionの設定をしています。
コンストラクターの82~92行で、new を使って
PersonのObservableCollectionであるPersonsの
インスタンスを生成して、値を代入しています。
93~94行で、new を使ってViewModelCommandのインスタンスを生成います。
引数のExecuteInsertDataCommand, CanExecuteInsertDataCommand
は、下で記述しています。
ViewModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using Livet;
using Livet.Commands;
using Livet.Messaging;
using Livet.Messaging.IO;
using Livet.EventListeners;
using Livet.Messaging.Windows;
using LivetWPFDataGrid.Models;
//ObservableCollection
using System.Collections.ObjectModel;
namespace LivetWPFDataGrid.ViewModels
{
public class MainWindowViewModel : ViewModel
{
#region 変更通知プロパティ
//-----------------------------------------------
private string _txtNo;
public string txtNo
{
get { return this._txtNo; }
set
{
if (this._txtNo != value)
{
this._txtNo = value;
RaisePropertyChanged("txtNo");
this.InsertDataCommand.RaiseCanExecuteChanged();
}
}
}
//-----------------------------------------------
public string txtName { get; set; }
public string txtInaugurationDay { get; set; }
public string txtRetiringDay { get; set; }
//-----------------------------------------------
#endregion
#region Person変更通知プロパティ
private Person _Person;
public Person Person
{
get { return _Person; }
set
{
if (_Person == value)
return;
_Person = value;
RaisePropertyChanged("Person");
}
}
#endregion
#region Persons変更通知プロパティ
private ObservableCollection<Person> _Persons;
public ObservableCollection<Person> Persons
{
get { return _Persons; }
set
{
if (_Persons == value)
return;
_Persons = value;
RaisePropertyChanged("Persons");
}
}
#endregion
public void Initialize()
{
}
public MainWindowViewModel()
{
Persons = new ObservableCollection<Person> {
new Person { No = 1, Name = "吉田茂", InaugurationDay
= new DateTime(1948, 10, 15),
RetiringDay = new DateTime(1954, 12, 10) },
new Person { No = 2, Name = "鳩山一郎", InaugurationDay
= new DateTime(1954, 12, 10),
RetiringDay = new DateTime(1956, 12, 23) },
new Person { No = 3, Name = "石橋湛山", InaugurationDay
= new DateTime(1956, 12, 23),
RetiringDay = new DateTime(1957, 02, 25) },
};
this.InsertDataCommand = new ViewModelCommand(ExecuteInsertDataCommand,
CanExecuteInsertDataCommand);
}
public ViewModelCommand InsertDataCommand { private set; get; }
private void ExecuteInsertDataCommand()
{
string strSTR = txtNo;
txtNo = strSTR;
//DoSomething(this.Text);
string str = txtInaugurationDay.Replace("/", "");
string strYear = str.Substring(0, 4);
string strMounth = str.Substring(4, 2);
string strDay = str.Substring(6, 2);
//
string str2 = txtRetiringDay.Replace("/", "");
string strYear2 = str.Substring(0, 4);
string strMounth2 = str.Substring(4, 2);
string strDay2 = str.Substring(6, 2);
Persons.Add(new Person
{
No = Convert.ToInt32(txtNo),
Name = txtName,
InaugurationDay = new DateTime(Convert.ToInt32(strYear),
Convert.ToInt32(strMounth),
Convert.ToInt32(strDay)),
RetiringDay = new DateTime(Convert.ToInt32(strYear2),
Convert.ToInt32(strMounth2),
Convert.ToInt32(strDay2))
});
}
private bool CanExecuteInsertDataCommand()
{
return !String.IsNullOrEmpty(this.txtNo);
}
}
}
モデルPerson.csは下記です。
Model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LivetWPFDataGrid.Models
{
//classにpublicをつけないとアクセシビリティに一貫性がありませんのエラーになる。
public class Person
{
public int No { get; set; }
public string Name { get; set; }
public DateTime InaugurationDay { get; set; }
public DateTime RetiringDay { get; set; }
}
}
Livetでは、RaiseCanExecuteChanged を呼び出すとNullReferenceException が発生しますので、その対策として、App.zaml.cs(App.zamlのコードビハインド)に背景色を付けた分を自分で追加します。
App
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Windows;
using Livet;
namespace LivetWPFDataGrid
{
/// <summary>
/// App.xaml の相互作用ロジック
/// </summary>
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
DispatcherHelper.UIDispatcher = Dispatcher;
}
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Livet.DispatcherHelper.UIDispatcher = this.Dispatcher;
}
}
}
View
<Button Command="{Binding CopyCommand}" Content="Commands" Margin="37,120,264,192" />
<Button Content="Event Trigger Commands" Margin="37,168,264,139"> <i:Interaction.Triggers>
<i:EventTrigger EventName="Click"> <i:InvokeCommandAction Command="{Binding
CopyCommand}"/> </i:EventTrigger> </i:Interaction.Triggers>
</Button>
ViewModel
#region 変更通知プロパティ
//-----------------------------------------------
private string _txtState;
{
get { return _txtState; }
set
{
if (_txtState != value)
{
_txtState = value;
RaisePropertyChanged("txtState");
}
}
}
//
private string _txtMessage;
public string txtMessage
{
get { return _txtMessage; }
set
{
if (_txtMessage != value)
{
_txtMessage = value;
RaisePropertyChanged("txtMessage");
}
}
}
//-----------------------------------------------
#endregion
public MainWindowViewModel()
{
txtState = "対応中";
}
#region CopyCommand1
//----------------------------------
private ViewModelCommand _copyCommand;
public ViewModelCommand CopyCommand
{
get
{
if (this._copyCommand == null)
{
this._copyCommand = new ViewModelCommand(CopyExecute, CanCopyExecute);
}
return this._copyCommand;
}
}
private void CopyExecute()
{
txtMessage = txtState;
}
private bool CanCopyExecute()
{
}
//----------------------------------
#endregion