MVVM – ViewModel 연결

이 장에서는 ViewModel을 연결하는 방법을 다룰 것입니다. View first 구성에 대해 논의한 마지막 장의 연속입니다. 자, 첫 번째 건설의 다음 형태는meta-pattern 로 알려진 ViewModelLocator. 의사 패턴이며 MVVM 패턴 위에 계층화됩니다.

  • MVVM에서 각 View는 ViewModel에 연결되어야합니다.

  • ViewModelLocator는 코드를 중앙 집중화하고 뷰를 더 분리하는 간단한 접근 방식입니다.

  • 이는 ViewModel 유형과 구성 방법에 대해 명시 적으로 알 필요가 없음을 의미합니다.

  • ViewModelLocator를 사용하는 방법에는 여러 가지가 있지만 여기서는 PRISM 프레임 워크의 일부인 것과 가장 유사한 방법을 사용합니다.

ViewModelLocator는 ViewModel을 View에 연결하는 프로세스를 자동화하는 View First 구성을 수행하는 표준적이고 일관 적이며 선언적이며 느슨하게 결합 된 방법을 제공합니다. 다음 그림은 ViewModelLocator의 상위 수준 프로세스를 나타냅니다.

Step 1 − 어떤 뷰 유형이 구성되고 있는지 파악합니다.

Step 2 − 특정 뷰 유형에 대한 ViewModel을 식별합니다.

Step 3 − 해당 ViewModel을 생성합니다.

Step 4 − Views DataContext를 ViewModel로 설정합니다.

기본 개념을 이해하기 위해 지난 장의 동일한 예제를 계속하여 ViewModelLocator의 간단한 예제를 살펴 보겠습니다. StudentView.xaml 파일을 보면 ViewModel이 정적으로 연결되어 있음을 알 수 있습니다.

이제 다음 프로그램에 표시된대로 이러한 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" 
   mc:Ignorable = "d" d:DesignHeight = "300" d:DesignWidth = "300">
	
   <!--<UserControl.DataContext> 
      <viewModel:StudentViewModel/> 
   </UserControl.DataContext>-->
	
   <Grid> 
      <StackPanel HorizontalAlignment = "Left"> 
         <ItemsControl ItemsSource = "{Binding Path = Students}">
            <ItemsControl.ItemTemplate> 
               <DataTemplate> 
					
                  <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> 
            </ItemsControl.ItemTemplate> 
         </ItemsControl> 
      </StackPanel> 
   </Grid>
	
</UserControl>

이제 새 폴더 VML을 만들고 다음 코드와 같이 연결된 단일 속성 (종속성 속성) AutoHookedUpViewModel을 포함 할 새 공용 클래스 ViewModelLocator를 추가하겠습니다.

public static bool GetAutoHookedUpViewModel(DependencyObject obj) { 
   return (bool)obj.GetValue(AutoHookedUpViewModelProperty); 
}

public static void SetAutoHookedUpViewModel(DependencyObject obj, bool value) { 
   obj.SetValue(AutoHookedUpViewModelProperty, value); 
}

// Using a DependencyProperty as the backing store for AutoHookedUpViewModel. 
//This enables animation, styling, binding, etc...
 
public static readonly DependencyProperty AutoHookedUpViewModelProperty =
   DependencyProperty.RegisterAttached("AutoHookedUpViewModel",
   typeof(bool), typeof(ViewModelLocator), new PropertyMetadata(false,
   AutoHookedUpViewModelChanged));

이제 기본 연결 속성 정의를 볼 수 있습니다. 속성에 동작을 추가하려면 View에 대한 ViewModel을 연결하는 자동 프로세스를 포함하는이 속성에 대한 변경된 이벤트 핸들러를 추가해야합니다. 이를 수행하는 코드는 다음과 같습니다.

private static void AutoHookedUpViewModelChanged(DependencyObject d, 
   DependencyPropertyChangedEventArgs e) { 
   if (DesignerProperties.GetIsInDesignMode(d)) return; 
   var viewType = d.GetType(); 
   string str = viewType.FullName; 
   str = str.Replace(".Views.", ".ViewModel."); 
	
   var viewTypeName = str; 
   var viewModelTypeName = viewTypeName + "Model"; 
   var viewModelType = Type.GetType(viewModelTypeName); 
   var viewModel = Activator.CreateInstance(viewModelType);
   ((FrameworkElement)d).DataContext = viewModel; 
}

다음은 ViewModelLocator 클래스의 완전한 구현입니다.

using System; 
using System.ComponentModel; 
using System.Windows;

namespace MVVMDemo.VML { 

   public static class ViewModelLocator { 
	
      public static bool GetAutoHookedUpViewModel(DependencyObject obj) {
         return (bool)obj.GetValue(AutoHookedUpViewModelProperty); 
      }
		
      public static void SetAutoHookedUpViewModel(DependencyObject obj, bool value) { 
         obj.SetValue(AutoHookedUpViewModelProperty, value); 
      }
		
      // Using a DependencyProperty as the backing store for AutoHookedUpViewModel. 
		
      //This enables animation, styling, binding, etc...
      public static readonly DependencyProperty AutoHookedUpViewModelProperty =
         DependencyProperty.RegisterAttached("AutoHookedUpViewModel", 
         typeof(bool), typeof(ViewModelLocator), new
         PropertyMetadata(false, AutoHookedUpViewModelChanged));
		
      private static void AutoHookedUpViewModelChanged(DependencyObject d,
         DependencyPropertyChangedEventArgs e) { 
         if (DesignerProperties.GetIsInDesignMode(d)) return; 
         var viewType = d.GetType(); 
			
         string str = viewType.FullName; 
         str = str.Replace(".Views.", ".ViewModel."); 
			
         var viewTypeName = str; 
         var viewModelTypeName = viewTypeName + "Model";
         var viewModelType = Type.GetType(viewModelTypeName); 
         var viewModel = Activator.CreateInstance(viewModelType);
			
        ((FrameworkElement)d).DataContext = viewModel; 
      } 
   } 
}

가장 먼저 할 일은 프로젝트의 루트에있는 ViewModelLocator 유형에 도달 할 수 있도록 네임 스페이스를 추가하는 것입니다. 그런 다음 뷰 유형 인 경로 요소에서 AutoHookedUpViewModel 속성을 추가하고 true로 설정합니다.

xmlns:vml = "clr-namespace:MVVMDemo.VML"
vml:ViewModelLocator.AutoHookedUpViewModel = "True"

다음은 StudentView.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:vml = "clr-namespace:MVVMDemo.VML" 
   vml:ViewModelLocator.AutoHookedUpViewModel = "True" 
   mc:Ignorable = "d" d:DesignHeight = "300" d:DesignWidth = "300">
   
   <!--<UserControl.DataContext> 
      <viewModel:StudentViewModel/> 
   </UserControl.DataContext>-->

   <Grid> 
      <StackPanel HorizontalAlignment = "Left"> 
         <ItemsControl ItemsSource = "{Binding Path = Students}"> 
            <ItemsControl.ItemTemplate> 
               <DataTemplate> 
					
                  <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> 
            </ItemsControl.ItemTemplate> 
         </ItemsControl> 
      </StackPanel>
   </Grid> 
	
</UserControl>

위의 코드가 컴파일되고 실행되면 ViewModelLocator가 해당 특정 뷰에 대한 ViewModel을 연결하는 것을 볼 수 있습니다.

이것에 대해 주목해야 할 중요한 점은 뷰가 더 이상 ViewModel의 유형 또는 구성 방법과 결합되지 않는다는 것입니다. 그게 모두 ViewModelLocator 내부의 중앙 위치로 옮겨졌습니다.