WPFでMVVMを実装
WPF の基本と MVVM の実装方法を学ぶために、シンプルなプログラムを
書いてみました。下記のプログラムは「MainWindow.xaml」と「MainWindow.xaml.cs」が View 、「MainWindowViewModel.cs」が ViewModel、
「PersonModel.cs」が Model になっています。
イメージ
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:e="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="450" Width="915">
<Window.Resources>
<Style TargetType="{x:Type Label}">
<Setter Property="Width" Value="100"/>
<Setter Property="Margin" Value="5"/>
</Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Width" Value="150"/>
<Setter Property="Height" Value="30"/>
</Style>
<Style TargetType="{x:Type Calendar}">
<Setter Property="Width" Value="250"/>
<Setter Property="Height" Value="200"/>
</Style>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="Width" Value="150"/>
</Style>
<Style TargetType="{x:Type Button}" x:Key="ButtonStyle">
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="30"/>
<Setter Property="Margin" Value="10 0 10 0"/>
</Style>
<ObjectDataProvider x:Key="EnumSex" MethodName="GetValues" ObjectType="{x:Type e:Enum}">
<ObjectDataProvider.MethodParameters>
<x:TypeExtension TypeName="local:Sexes"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="450"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Padding="10">
<StackPanel>
<DockPanel>
<Label x:Name="IdLabel" Content="ID"/>
<TextBox x:Name="IdTextBox" >
<TextBox.Text>
<Binding Path="RegisteredPerson.PersonID">
<Binding.ValidationRules>
<DataErrorValidationRule></DataErrorValidationRule>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DockPanel>
<DockPanel>
<Label x:Name="FirstNameLabel" Content="性"/>
<TextBox x:Name="FirstNameTextBox" Text="{Binding RegisteredPerson.FirstName}"/>
</DockPanel>
<DockPanel>
<Label x:Name="LastNameLabel" Content="名"/>
<TextBox x:Name="LastNameTextBox" Text="{Binding RegisteredPerson.LastName}"/>
</DockPanel>
<DockPanel>
<Label x:Name="RoleLabel" Content="性別"/>
<ComboBox ItemsSource="{Binding Source={StaticResource EnumSex}}" SelectedValue="{Binding RegisteredPerson.Sex}" Margin="5"/>
</DockPanel>
<DockPanel>
<Label x:Name="HireDateLabel" Content="誕生日"/>
<Calendar x:Name="HireDateCalender" SelectedDate="{Binding RegisteredPerson.HireDate}"/>
</DockPanel>
<DockPanel HorizontalAlignment="Right">
<Button x:Name="RegisterButton" Style="{StaticResource ButtonStyle}" Content="登録"
Command="{Binding RegisterPersonCommand}"
CommandParameter="{Binding RegisteredPerson}"/>
<Button x:Name="CancelButton" Style="{StaticResource ButtonStyle}" Content="キャンセル"
Command="{Binding CancelCommand}"
CommandParameter="{Binding RegisteredPerson}"/>
</DockPanel>
</StackPanel>
</Border>
<ListView Grid.Column="1" ItemsSource="{Binding People}">
<ListView.View>
<GridView>
<GridViewColumn Header="ID" DisplayMemberBinding="{Binding PersonID}" Width="50"/>
<GridViewColumn Header="性" DisplayMemberBinding="{Binding FirstName}" Width="100"/>
<GridViewColumn Header="名" DisplayMemberBinding="{Binding LastName}" Width="100"/>
<GridViewColumn Header="性別" DisplayMemberBinding="{Binding Sex}" Width="50"/>
<GridViewColumn Header="誕生日" DisplayMemberBinding="{Binding BirthDay, StringFormat=yyyy/MM/dd}" Width="120"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Windows;
namespace WpfApplication1 {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
}
}
MainWindowViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections.ObjectModel;
using System.Windows.Input;
namespace WpfApplication1 {
public class MainWindowViewModel {
public Person RegisteredPerson { get; set; }
public ObservableCollection<Person> People { get; set; }
public ICommand RegisterPersonCommand { get; set; }
public ICommand CancelCommand { get; set; }
public List<Sexes> Roles { get; set; }
public MainWindowViewModel() {
People = new ObservableCollection<Person>();
RegisteredPerson = new Person();
RegisterPersonCommand = new RegisterPerson(this);
CancelCommand = new Cancel(this);
}
}
public class RegisterPerson : ICommand {
private ObservableCollection<Person> _people;
private Person _person;
public RegisterPerson(MainWindowViewModel view) {
_people = view.People;
_person = view.RegisteredPerson;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) {
return true;
}
public void Execute(object parameter) {
var p = (Person)parameter;
_people.Add(new Person(
p.PersonID, p.FirstName, p.LastName, p.Sex, p.BirthDay));
_person.PersonID++;
_person.FirstName = string.Empty;
_person.LastName = string.Empty;
_person.Sex = Sexes.男性;
_person.BirthDay = DateTime.Today;
}
}
public class Cancel : ICommand {
private ObservableCollection<Person> _people;
public Cancel(MainWindowViewModel view) {
_people = view.People;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) {
return true;
}
public void Execute(object parameter) {
var p = (Person)parameter;
p.PersonID = _people.Max<Person>(u => u.PersonID) + 1;
p.FirstName = string.Empty;
p.LastName = string.Empty;
p.Sex = Sexes.男性;
p.BirthDay = DateTime.Today;
}
}
}
PersonModel.cs
using System;
using System.ComponentModel;
namespace WpfApplication1 {
public class Person : INotifyPropertyChanged, IDataErrorInfo {
public Person() {
PersonID = 1;
Sex = Sexes.男性;
BirthDay = DateTime.Today;
}
public Person(int id, string firstName,
string lastName, Sexes sex, DateTime birthDay) {
PersonID = id;
FirstName = firstName;
LastName = lastName;
Sex = sex;
BirthDay = birthDay;
}
private int _personID;
public int PersonID {
get {
return _personID;
}
set {
if (_personID == value)
return;
_personID = value;
RaisePropertyChanged("PersonID");
}
}
private string _firstName;
public string FirstName {
get {
return _firstName;
}
set {
if (_firstName == value)
return;
_firstName = value;
RaisePropertyChanged("FirstName");
}
}
private string _lastName;
public string LastName {
get {
return _lastName;
}
set {
if (_lastName == value)
return;
_lastName = value;
RaisePropertyChanged("LastName");
}
}
private Sexes _sex;
public Sexes Sex {
get {
return _sex;
}
set {
if (_sex == value)
return;
_sex = value;
RaisePropertyChanged("Sex");
}
}
private DateTime _birthDay;
public DateTime BirthDay {
get {
return _birthDay;
}
set {
if (_birthDay == value)
return;
_birthDay = value;
RaisePropertyChanged("BirthDay");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName) {
if (PropertyChanged != null)
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
public string this[string propertyName] {
get {
if (propertyName == "PersonID") {
if (PersonID < 1)
return "IDは1以上の数値を入力してください。";
}
return null;
}
}
public string Error {
get {
return null;
}
}
}
public enum Sexes {
男性,
女性
}
}