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