MVVM – View / ViewModel通信
この章では、MVVMアプリケーションに対話機能を追加する方法と、ロジックをクリーンに呼び出す方法を学習します。また、これらすべてが、MVVMパターンの中心である疎結合と適切な構造化を維持することによって行われていることがわかります。これらすべてを理解するために、まずコマンドについて学びましょう。
コマンドによるView / ViewModel通信
コマンドパターンは十分に文書化されており、数十年にわたってデザインパターンを頻繁に使用しています。このパターンには、呼び出し側と受信側の2つの主要なアクターがあります。
呼び出し元
呼び出し元は、いくつかの命令型ロジックを実行できるコードの一部です。
通常、これは、UIフレームワークのコンテキストでユーザーが操作するUI要素です。
これは、アプリケーションの他の場所にあるロジックコードの別のチャンクである可能性があります。
レシーバー
レシーバーは、呼び出し元が起動したときに実行することを目的としたロジックです。
MVVMのコンテキストでは、レシーバーは通常、呼び出す必要のあるViewModelのメソッドです。
これら2つの間に、障害物レイヤーがあります。これは、呼び出し側と受信側がお互いを明示的に知る必要がないことを意味します。これは通常、呼び出し側に公開されるインターフェイスの抽象化として表され、そのインターフェイスの具体的な実装は受信者を呼び出すことができます。
コマンドと、それらを使用してViewとViewModelの間で通信する方法を学習する簡単な例を見てみましょう。この章では、前の章と同じ例を続けます。
StudentView.xamlファイルには、ViewModelから学生データをフックするListBoxがあります。次に、リストボックスから生徒を削除するためのボタンを追加しましょう。
重要なことは、ボタンのコマンドはICommandに接続するためのコマンドプロパティを持っているため、非常に簡単に操作できることです。
したがって、次のコードに示すように、ICommandを持ち、ボタンのコマンドプロパティからそれにバインドするViewModelのプロパティを公開できます。
<Button Content = "Delete"
Command = "{Binding DeleteCommand}"
HorizontalAlignment = "Left"
VerticalAlignment = "Top"
Width = "75" />
プロジェクトに新しいクラスを追加してみましょう。これにより、ICommandインターフェイスが実装されます。以下は、ICommandインターフェイスの実装です。
using System;
using System.Windows.Input;
namespace MVVMDemo {
public class MyICommand : ICommand {
Action _TargetExecuteMethod;
Func<bool> _TargetCanExecuteMethod;
public MyICommand(Action executeMethod) {
_TargetExecuteMethod = executeMethod;
}
public MyICommand(Action executeMethod, Func<bool> canExecuteMethod){
_TargetExecuteMethod = executeMethod;
_TargetCanExecuteMethod = canExecuteMethod;
}
public void RaiseCanExecuteChanged() {
CanExecuteChanged(this, EventArgs.Empty);
}
bool ICommand.CanExecute(object parameter) {
if (_TargetCanExecuteMethod != null) {
return _TargetCanExecuteMethod();
}
if (_TargetExecuteMethod != null) {
return true;
}
return false;
}
// Beware - should use weak references if command instance lifetime
is longer than lifetime of UI objects that get hooked up to command
// Prism commands solve this in their implementation
public event EventHandler CanExecuteChanged = delegate { };
void ICommand.Execute(object parameter) {
if (_TargetExecuteMethod != null) {
_TargetExecuteMethod();
}
}
}
}
ご覧のとおり、これはICommandの単純な委任実装であり、executeMethod用とcanExecuteMethod用の2つの委任があり、構築時に渡すことができます。
上記の実装では、2つのオーバーロードされたコンストラクターがあります。1つはexecuteMethodのみ用で、もう1つはexecuteMethodとI canExecuteMethodの両方用です。
StudentViewModelクラスにMyICommandタイプのプロパティを追加しましょう。次に、StudentViewModelでインスタンスを作成する必要があります。2つのパラメーターを受け取るMyICommandのオーバーロードされたコンストラクターを使用します。
public MyICommand DeleteCommand { get; set;}
public StudentViewModel() {
LoadStudents();
DeleteCommand = new MyICommand(OnDelete, CanDelete);
}
次に、OnDeleteメソッドとCanDeleteメソッドの実装を追加します。
private void OnDelete() {
Students.Remove(SelectedStudent);
}
private bool CanDelete() {
return SelectedStudent != null;
}
また、ユーザーがリストボックスから選択したアイテムを削除できるように、新しいSelectedStudentを追加する必要があります。
private Student _selectedStudent;
public Student SelectedStudent {
get {
return _selectedStudent;
}
set {
_selectedStudent = value;
DeleteCommand.RaiseCanExecuteChanged();
}
}
以下は、ViewModelクラスの完全な実装です。
using MVVMDemo.Model;
using System.Collections.ObjectModel;
using System.Windows.Input;
using System;
namespace MVVMDemo.ViewModel {
public class StudentViewModel {
public MyICommand DeleteCommand { get; set;}
public StudentViewModel() {
LoadStudents();
DeleteCommand = new MyICommand(OnDelete, CanDelete);
}
public ObservableCollection<Student> Students {
get;
set;
}
public void LoadStudents() {
ObservableCollection<Student> students = new ObservableCollection<Student>();
students.Add(new Student { FirstName = "Mark", LastName = "Allain" });
students.Add(new Student { FirstName = "Allen", LastName = "Brown" });
students.Add(new Student { FirstName = "Linda", LastName = "Hamerski" });
Students = students;
}
private Student _selectedStudent;
public Student SelectedStudent {
get {
return _selectedStudent;
}
set {
_selectedStudent = value;
DeleteCommand.RaiseCanExecuteChanged();
}
}
private void OnDelete() {
Students.Remove(SelectedStudent);
}
private bool CanDelete() {
return SelectedStudent != null;
}
}
}
StudentView.xamlで、SelectStudentプロパティにバインドするSelectedItemプロパティをListBoxに追加する必要があります。
<ListBox ItemsSource = "{Binding Students}" SelectedItem = "{Binding SelectedStudent}"/>
以下は完全なxamlファイルです。
<UserControl x:Class = "MVVMDemo.Views.StudentView"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
xmlns:local = "clr-namespace:MVVMDemo.Views"
xmlns:viewModel = "clr-namespace:MVVMDemo.ViewModel"
xmlns:data = "clr-namespace:MVVMDemo.Model"
xmlns:vml = "clr-namespace:MVVMDemo.VML"
vml:ViewModelLocator.AutoHookedUpViewModel = "True"
mc:Ignorable = "d"
d:DesignHeight = "300" d:DesignWidth = "300">
<UserControl.Resources>
<DataTemplate DataType = "{x:Type data:Student}">
<StackPanel Orientation = "Horizontal">
<TextBox Text = "{Binding Path = FirstName, Mode = TwoWay}"
Width = "100" Margin = "3 5 3 5"/>
<TextBox Text = "{Binding Path = LastName, Mode = TwoWay}"
Width = "100" Margin = "0 5 3 5"/>
<TextBlock Text = "{Binding Path = FullName, Mode = OneWay}"
Margin = "0 5 3 5"/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid>
<StackPanel Orientation = "Horizontal">
<ListBox ItemsSource = "{Binding Students}"
SelectedItem = "{Binding SelectedStudent}"/>
<Button Content = "Delete"
Command = "{Binding DeleteCommand}"
HorizontalAlignment = "Left"
VerticalAlignment = "Top"
Width = "75" />
</StackPanel>
</Grid>
</UserControl>
上記のコードをコンパイルして実行すると、次のウィンドウが表示されます。
削除ボタンが無効になっていることがわかります。いずれかの項目を選択すると有効になります。
アイテムを選択して削除を押したとき。選択したアイテムリストが削除され、削除ボタンが再び無効になっていることがわかります。
理解を深めるために、上記の例を段階的に実行することをお勧めします。