WPFでMVVMを実装

WPF の基本と MVVM の実装方法を学ぶために、シンプルなプログラムを

書いてみました。下記のプログラムは「MainWindow.xaml」と「MainWindow.xaml.cs」が View 、「MainWindowViewModel.cs」が ViewModel、

「PersonModel.cs」が Model になっています。

 イメージ

f:id:azumal:20140413105656j:plain

 

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 {
        男性,
        女性
    }
}