MVVM – View / ViewModel 통신
이 장에서는 MVVM 응용 프로그램에 상호 작용을 추가하는 방법과 논리를 깔끔하게 호출하는 방법을 배웁니다. 또한 MVVM 패턴의 핵심 인 느슨한 결합과 좋은 구조화를 유지함으로써이 모든 것이 수행된다는 것을 알 수 있습니다. 이 모든 것을 이해하기 위해 먼저 명령에 대해 알아 보겠습니다.
명령을 통한 View / ViewModel 통신
명령 패턴은 잘 문서화되어 있으며 수십 년 동안 자주 디자인 패턴을 사용합니다. 이 패턴에는 두 가지 주요 행위자 인 호출자와 수신자가 있습니다.
호출자
호출자는 명령 적 논리를 실행할 수있는 코드 조각입니다.
일반적으로 UI 프레임 워크 컨텍스트에서 사용자가 상호 작용하는 UI 요소입니다.
응용 프로그램의 다른 곳에 논리 코드의 또 다른 청크 일 수 있습니다.
리시버
수신자는 호출자가 실행될 때 실행되도록 의도 된 논리입니다.
MVVM의 컨텍스트에서 수신자는 일반적으로 호출해야하는 ViewModel의 메서드입니다.
이 둘 사이에는 방해 레이어가 있으며 이는 호출자와 수신자가 서로에 대해 명시 적으로 알 필요가 없음을 의미합니다. 이것은 일반적으로 호출자에게 노출 된 인터페이스 추상화로 표현되며 해당 인터페이스의 구체적인 구현은 수신자를 호출 할 수 있습니다.
명령을 배우고 명령을 사용하여 View와 ViewModel간에 통신하는 방법을 배우는 간단한 예제를 살펴 보겠습니다. 이 장에서 우리는 지난 장의 동일한 예를 계속할 것입니다.
StudentView.xaml 파일에는 ViewModel에서 학생 데이터를 연결하는 ListBox가 있습니다. 이제 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에 대한 하나의 델리게이트가 있습니다.
위의 구현에는 두 개의 오버로드 된 생성자가 있습니다. 하나는 executeMethod 전용이고 다른 하나는 executeMethod 및 I canExecuteMethod입니다.
StudentView Model 클래스에 MyICommand 유형의 속성을 추가해 보겠습니다. 이제 StudentViewModel에서 인스턴스를 생성해야합니다. 두 개의 매개 변수를 사용하는 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;
}
또한 사용자가 ListBox에서 선택한 항목을 삭제할 수 있도록 새 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 속성에 바인딩 할 ListBox에 SelectedItem 속성을 추가해야합니다.
<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>
위의 코드가 컴파일되고 실행되면 다음과 같은 창이 나타납니다.
삭제 버튼이 비활성화 된 것을 볼 수 있습니다. 항목을 선택하면 활성화됩니다.
항목을 선택하고 삭제를 누르면. 선택한 항목 목록이 삭제되고 삭제 버튼이 다시 비활성화됩니다.
더 나은 이해를 위해 위의 예를 단계별로 실행하는 것이 좋습니다.