C#/WPF

[SingletoneSean] WPF MVVM Tutorials(Commands / Navigation)

HJ0216 2024. 2. 10. 08:02

이 글은 [WPF MVVM Tutorials] 수강하며 정리한 글입니다.

 

👉 기본 환경

- Language: C#, xaml

- IDE: Visual Basic 2022


Command 유효성 검사

namespace WPF_MVVM_Tutorial.Commands
{
    public abstract class CommandBase : ICommand
    {
        public event EventHandler? CanExecuteChanged;

        public virtual bool CanExecute(object? parameter)
        {
            return true;
        }

        public abstract void Execute(object? parameter);

        protected void OnCanExecutedChanged()
        {
            CanExecuteChanged?.Invoke(this, new EventArgs());
        }
    }
}

 

namespace WPF_MVVM_Tutorial.Commands
{
    public class MakeReservationCommand : CommandBase
    {
        private readonly MakeReservationViewModel _makeReservationViewModel;

        public override bool CanExecute(object? parameter)
        {
            return !string.IsNullOrEmpty(_makeReservationViewModel.Username) && base.CanExecute(parameter);
        }
    }
}

 

CommnadBase를 상속받은 MakeReservationCommand에서

virtual 메서드로 선언하여 재정의할 수 있도록 만든 CanExecute 메서드에서 Validation 진행

Username이 공란일 경우, Submit Button에 연결된 MakeReservationCommand 비활성화

 

PropertyChanged 등록

public MakeReservationCommand(ViewModels.MakeReservationViewModel makeReservationViewModel, Hotel hotel)
{
    _makeReservationViewModel = makeReservationViewModel;
    _hotel = hotel;

    _makeReservationViewModel.PropertyChanged += OnViewModelPropertyChanged;
}

public override bool CanExecute(object? parameter)
{
    return !string.IsNullOrEmpty(_makeReservationViewModel.Username) 
        && _makeReservationViewModel.FloorNumber > 0
        && _makeReservationViewModel.RoomNumber > 0
        && base.CanExecute(parameter);
}

private void OnViewModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
    if(e.PropertyName == nameof(MakeReservationViewModel.Username)
        || (e.PropertyName == nameof(MakeReservationViewModel.FloorNumber))
        || (e.PropertyName == nameof(MakeReservationViewModel.RoomNumber)))
    {
        OnCanExecutedChanged();
    }
}

⭐ makeReservationViewModel. PropertyChanged += OnViewModelPropertyChanged;

    - makeReservationViewModel의 PropertyChanged 이벤트에 이벤트 핸들러인 OnViewModelPropertyChanged 메서드 추가

    - PropertyChanged 이벤트가 발생하면 OnViewModelPropertyChanged 메서드를 호출

      _makeReservationViewModel의 어떤 속성이 변하면, 그 변화를 감지하고 OnViewModelPropertyChanged 메서드를 실행하라는 지시

    - OnViewModelPropertyChanged

        - 이벤트 핸들러로, 이벤트가 발생했을 때 수행할 작업을 정의

        - e.PropertyName를 통해 특정 속성의 변경에 따라 UI를 업데이트하거나 다른 작업을 수행하도록 함

 

Store View Change Event 설정

namespace WPF_MVVM_Tutorial.Stores
{
    public class NavigationStore
    {
        private ViewModelBase _currentViewModel;
        public ViewModelBase CurrentViewModel
        {
            get => _currentViewModel;
            set
            {
                _currentViewModel = value;
                OnCurrentViewModelChanged();
            }
        }

        public event Action CurrentViewModelChanged;

        private void OnCurrentViewModelChanged()
        {
            CurrentViewModelChanged?.Invoke();
        }

    }
}

NavigationStore

    - CurrentViewModel 프로퍼티의 값이 변경될 때마다 CurrentViewModelChanged 이벤트가 발생
    - 애플리케이션의 네비게이션 로직을 중앙에서 관리하고, 현재 활성화된 뷰모델이 변경될 때마다 이를 구독하는 객체에 알림

 

ViewModel과 View 연결

<Grid MaxWidth="600" Margin="20 10">
    <Grid.Resources>
        <DataTemplate DataType="{x:Type vms:MakeReservationViewModel}">
            <views:MakeReservationView/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type vms:ReservationListingViewModel}">
            <views:ReservationListingView/>
        </DataTemplate>
    </Grid.Resources>
    <ContentControl Content="{Binding CurrentViewModel}"/>
</Grid>

ContentControl

    - DataContext에서 데이터를 가져와 표시하는 컨트롤

    - ViewModel을 바인딩 → ViewModel에 DataTemplate로 view 바인딩

 

CurrentViewModel Change

public class NavigationStore
{
    private ViewModelBase _currentViewModel;
    public ViewModelBase CurrentViewModel
    {
        get => _currentViewModel;
        set
        {
            _currentViewModel = value;
            OnCurrentViewModelChanged();
        }
    }

    public event Action CurrentViewModelChanged;

    private void OnCurrentViewModelChanged()
    {
        CurrentViewModelChanged?.Invoke();
    }
}

 

namespace WPF_MVVM_Tutorial.ViewModels
{
    public class MainViewModel : ViewModelBase
    {
        private readonly NavigationStore _navigationStore;

        public MainViewModel(NavigationStore navigationStore)
        {
            _navigationStore = navigationStore;

            _navigationStore.CurrentViewModelChanged += OnCurrentViewModelChanged;
        }

        private void OnCurrentViewModelChanged()
        {
            OnPropertyChanged(nameof(CurrentViewModel));
        }

    }
}

CurrentViewModel이 변경될 때마다 UI가 업데이트 되도록 CurrentViewModelChanged에 메서드 추가