programing

MVVM에서 ViewModelBase를 쓰는 방법

showcode 2023. 4. 20. 23:25
반응형

MVVM에서 ViewModelBase를 쓰는 방법

저는 WPF 프로그래밍 환경에 대해 꽤 초보입니다.MVVM 디자인 패턴을 사용하여 프로그램을 작성하려고 합니다.

연구도 하고 관련 기사도 읽었는데 이런 걸 많이 접하게 됐어요

View Model Base(보기 모델 베이스)

뭔지 알아..하지만 View Model Base를 작성하려면 구체적으로 어디서부터 시작해야 하는지 알 수 있을까요?예를 들면...너무 복잡해지지 않고 무슨 일이 일어나고 있는지 이해하는 것.감사합니다:)

내부 상황을 모르면 MVVM 프레임워크를 사용할 필요가 없습니다.

이제 차근차근 View Model Base 클래스를 구축해 보겠습니다.

  1. ViewModelBase는 모든 뷰모델에 공통 클래스입니다.일반적인 논리를 모두 이 반으로 옮기자.

  2. 은 View Model을 구현해야 .INotifyPropertyChanged(그냥?)

     public abstract class ViewModelBase : INotifyPropertyChanged
     {
         public event PropertyChangedEventHandler PropertyChanged;
    
         protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
         {
             PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
         }
     }
    

    [CallerMemberName]Attribute 、 at음음 、 음음음 at at at at at at at at at at at at at at at 。OnPropertyChanged();OnPropertyChanged("SomeProperty");코드 내의 문자열 상수를 회피할 수 있습니다.§:

     public string FirstName
     {
         set
         {
             _firstName = value;
             OnPropertyChanged(); //instead of OnPropertyChanged("FirstName") or OnPropertyChanged(nameof(FirstName))
         }
         get{ return _firstName;}
     }
    

    해 주세요, 그 세주주주 please please please please please please please please 。OnPropertyChanged(() => SomeProperty)권장하지 않습니다. 에게는 더상권권 is is is is is is is is is is is is is is is is is is 。nameofC# 6 의 c 。

  3. Property Changed를 호출하는 속성은 다음과 같이 구현하는 것이 일반적입니다.

     public string FirstName
     {
         get { return _firstName; }
         set { SetProperty(ref _firstName, value); }
     }
    

    뷰 모델 베이스에서 SetProperty를 정의합니다.

     protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = "")
     {
         if (EqualityComparer<T>.Default.Equals(storage, value))
             return false;
         storage = value;
         this.OnPropertyChanged(propertyName);
         return true;
     }
    

    할 수 있다.PropertyChanged사실값이 변경되지 않고 false가 반환되는 경우 이벤트가 실행되지 않습니다.은 '아주머니'라는 것이다SetProperty으로, 보다 을 트리거하는 경우)로 수 있습니다. method를 하여 검증할 수도 있습니다.PropertyChanging

이거 예쁘다.이 단계에서 View Model Base에 포함되는 모든 내용입니다.나머지는 당신의 프로젝트에 달려 있습니다.예를 들어, 앱이 페이지 기반 탐색을 사용하고 ViewModel에서 탐색을 처리하기 위해 자체 탐색 서비스를 작성했습니다.따라서 Navigation Service 속성을 ViewModelBase 클래스에 추가할 수 있습니다.그러면 원하는 경우 모든 뷰 모델에서 액세스할 수 있습니다.

더 많은 재사용성을 얻고 SRP를 유지하기 위해 BindableBase라는 클래스가 있는데, 이것은 거의 INotify를 구현한 것입니다.여기서와 같이 속성이 변경되었습니다.모든 WPF/UWP/Silverligt/Windows에서 이 클래스를 재사용합니다.보편적이기 때문에 전화 솔루션.

그런 다음 각 프로젝트에서 BindableBase에서 파생된 커스텀 ViewModelBase 클래스를 만듭니다.

public abstract ViewModelBase : BindableBase
{
    //project specific logic for all viewmodels. 
    //E.g in this project I want to use EventAggregator heavily:
    public virtual IEventAggregator () => ServiceLocator.GetInstance<IEventAggregator>()   
}

페이지 기반 네비게이션을 사용하는 앱이 있는 경우 페이지 뷰 모델의 기본 클래스도 지정합니다.

public abstract PageViewModelBase : ViewModelBase
{
    //for example all my pages have title:
    public string Title {get; private set;}
}

대화 상자에 대한 다른 클래스를 가질 수 있습니다.

public abstract DialogViewModelBase : ViewModelBase
{
    private bool? _dialogResult;

    public event EventHandler Closing;

    public string Title {get; private set;}
    public ObservableCollection<DialogButton> DialogButtons { get; }

    public bool? DialogResult
    {
        get { return _dialogResult; }
        set { SetProperty(ref _dialogResult, value); }
    }

    public void Close()
    {
        Closing?.Invoke(this, EventArgs.Empty);
    }
}

다음 클래스는 WPF 프로젝트에서 ViewModelBase로 사용할 수 있습니다.

public abstract class ViewModelBase : INotifyPropertyChanged
{
    /// <summary>
    /// Multicast event for property change notifications.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Checks if a property already matches the desired value.  Sets the property and
    /// notifies listeners only when necessary.
    /// </summary>
    /// <typeparam name="T">Type of the property.</typeparam>
    /// <param name="storage">Reference to a property with both getter and setter.</param>
    /// <param name="value">Desired value for the property.</param>
    /// <param name="propertyName">Name of the property used to notify listeners.This
    /// value is optional and can be provided automatically when invoked from compilers that
    /// support CallerMemberName.</param>
    /// <returns>True if the value was changed, false if the existing value matched the
    /// desired value.</returns>
    protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (object.Equals(storage, value)) return false;
        storage = value;
        // Log.DebugFormat("{0}.{1} = {2}", this.GetType().Name, propertyName, storage);
        this.OnPropertyChanged(propertyName);
        return true;
    }

    /// <summary>
    /// Notifies listeners that a property value has changed.
    /// </summary>
    /// <param name="propertyName">Name of the property used to notify listeners.  This
    /// value is optional and can be provided automatically when invoked from compilers
    /// that support <see cref="CallerMemberNameAttribute"/>.</param>
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var eventHandler = this.PropertyChanged;
        if (eventHandler != null)
            eventHandler(this, new PropertyChangedEventArgs(propertyName));
    }
}

View Model 클래스의 예는 다음과 같습니다.

public class MyViewModel : ViewModelBase
{
    private int myProperty;
    public int MyProperty
    {
        get { return myProperty; }
        set { SetProperty(ref myProperty, value); }
    }
}

쓰기 쉽도록 다음 스니펫을 사용할 수 있습니다.

<?xml version="1.0" encoding="utf-8"?>  
<CodeSnippets  
    xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">  
    <CodeSnippet Format="1.0.0">  
        <Header>  
            <Title>OnPropertyChanged</Title>  
        </Header>      
      <Snippet>
          <SnippetTypes>
            <SnippetType>SurroundsWith</SnippetType>
            <SnippetType>Expansion</SnippetType>
          </SnippetTypes>
          <Declarations>
            <Literal>
              <ID>TYPE</ID>
              <ToolTip>Property type</ToolTip>
              <Default>int</Default>
            </Literal>
            <Literal>
              <ID>NAME1</ID>
              <ToolTip>Property name</ToolTip>
              <Default>MyProperty</Default>
            </Literal>
          </Declarations>
            <Code Language="CSharp">  
                <![CDATA[private $TYPE$ _$NAME1$;
public $TYPE$ $NAME1$
{
    get => _$NAME1$; 
    set => SetProperty(ref _$NAME1$, value);    
}]]>  
            </Code>  
        </Snippet>  
    </CodeSnippet>  
</CodeSnippets>  

여기서 전체 코드를 다운로드할 수 있습니다.

MVVM을 구현하기 위한 몇 가지 nuget 패키지가 있습니다.

  1. MVVM 라이트
  2. MVVM 크로스
  3. 프리즘

코드 샘플을 제공하고 있기 때문에 초보자에게는 MVVM 라이트입니다.

따라서 이 nuget 패키지를 설치하고 생성된 코드를 살펴보고 필요한 경우 자세한 설명을 위해 당사로 돌아오는 것이 좋습니다.

대부분의 MVVM 프레임워크에서 기본 View Model 클래스에는 실제로 매우 적은 코드가 포함되어 있습니다.보통 INotify 구현일 뿐입니다.Property Changed 및 일부 도우미 기능.

MVVM Light의 ViewModelBaseObservableObject 클래스의 소스 코드를 살펴봅니다.Observable Object는 대부분 INotify입니다.PropertyChanged 구현 - 속성 이름에 "매직 문자열" 대신 람다 식을 사용합니다.ViewModelBase는 ObservableObject를 확장하며 Visual Studio 디자이너 내에서 실행 중인지 여부를 확인하는 유틸리티 방법입니다.

저는 BaseVewModel을 좋아합니다.이것은 당신의 뷰 모델에 멋지고 깨끗한 스타일을 줍니다.다양한 '전'과 '후' 비교를 확인하십시오.물론 필수는 아닙니다.BaseViewModel이 제공하는 기능이 마음에 들지 않으면 사용하지 마십시오.또는 소스 코드가 있으므로 수정하십시오.특히 변경 알림을 사용하여 속성을 구현하는 방법에는 세 가지가 있습니다. 즉, 이해하거나 쉽게 이해할 수 있는 정교함의 수준을 선택하십시오.

오늘 이 답변을 다시 살펴보기 위해 Visual Studio용 MVVM 코드를 작성할 때 추가적인 생산성 향상을 제안하고자 합니다.

으로 Visual Studio Intellissense를 할 수 있습니다.SetProperty수행하려면 참조「XAML」의 「View Model」의 「XAML」의 「View Model」의 「XAML」의 「View Model」의 「XAML」의 「View Model」의 「View Model」의 「XAML」의 「XAML」 제가 {Binding Path=NewProperty}, 우클릭 후Quick Actions and Refactoring...(또는 경유로)Ctrl .)의 경우SetProperty메서드는 생성되지 않으며 ViewModel 클래스 내에서 자동으로 생성됩니다.또한 바인딩에 필요한 속성 및 필드를 생성합니다.

<Window x:Class="My.Project.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:My.Project"
        mc:Ignorable="d"
        xmlns:viewmodel="clr-namespace:My.Project.ViewModels" d:DataContext="{d:DesignInstance Type=viewmodel:MainWindowViewModel}"

        Title="MainWindow" Height="360" Width="1000">
...
</Window>

단, 이 방법에는 단점이 있습니다.

  1. INotifyPropertyChanged실장되어 있지 않고,OnPropertyChanged메서드가 구현되지 않았습니다(필요한 경우).
  2. 이 작업은 모든 View Model에서 수행해야 합니다.
  3. 이것은 Visual Studio 전용입니다.

이점:

  1. 일단은SetProperty메서드는 프로젝트 내에서 정의되며,Quick Actions and Refactoring...옵션을 지정하면 필요한 속성 및 필드만 생성됩니다.이 기능은, 에서 상속하는 경우에도 유효합니다.ViewModelBase.

여기 있습니다SetPropertyVisual Studio에서 생성된 메서드입니다.

protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
    if (object.Equals(storage, value)) return false;
    storage = value;
    // Log.DebugFormat("{0}.{1} = {2}", this.GetType().Name, propertyName, storage);
    this.OnPropertyChanged(propertyName);
    return true;
}
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using System.Runtime.CompilerServices;
using System.ComponentModel;
namespace CollectionViewMVVM.ViewModel
{
public class BaseViewModel : INotifyPropertyChanged
{
    /*Referencia: https://www.youtube.com/watch?v=PXEt1esjnZ0&ab_channel=Codigo369*/
    public INavigation Navigation;
    public event PropertyChangedEventHandler PropertyChanged;
    public virtual void OnpropertyChanged([CallerMemberName] string nombre = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nombre));
    }

    private ImageSource foto;
    public ImageSource Foto
    {
        get { return foto; }
        set
        {
            foto = value;
            OnpropertyChanged();
        }
    }
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    public async Task DisplayAlert(string title, string message, string cancel)
    {
        await Application.Current.MainPage.DisplayAlert(title, message, cancel);
    }

    public async Task<bool> DisplayAlert(string title, string message, string accept, string cancel)
    {
        return await Application.Current.MainPage.DisplayAlert(title, message, accept, cancel);
    }

    protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
    {
        if(EqualityComparer<T>.Default.Equals(field, value))
        {
            return false;
        }
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }
    /*Ejemplo para declarar entrys*/
    private string _title;
    public string Title
    {
        get { return _title; }
        set
        {
            SetProperty(ref _title, value);
        }
    }
    /*Para tipos bool*/
    private bool _isBusy;
    public bool IsBusy
    {
        get { return _isBusy; }
        set
        {

            SetProperty(ref _isBusy, value);
        }
    }

    protected void SetValue<T>(ref T backingFielded, T value, [CallerMemberName] string propertyName = null)
    {
        if(EqualityComparer<T>.Default.Equals(backingFielded, value))
        {
            return;
        }
        backingFielded = value;
        OnPropertyChanged(propertyName);
    }
}

}

언급URL : https://stackoverflow.com/questions/36149863/how-to-write-a-viewmodelbase-in-mvvm

반응형