티스토리 뷰

반응형

 

2022.11.30 - [WPF .NET] - Prism Library를 사용하는 개발자를 위한 안내 Part6 - TabControl Region Navigation

2022.11.25 - [WPF .NET] - Prism Library를 사용하는 개발자를 위한 안내 Part5 - Region & ContentControl Region Navigation

2022.11.18 - [WPF .NET] - Prism Library를 사용하는 개발자를 위한 안내 Part4 - Register Types

2022.11.15 - [WPF .NET] - Prism Library를 사용하는 개발자를 위한 안내 Part3 - DelegateCommand

2022.10.28 - [WPF .NET] - Prism Library를 사용하는 개발자를 위한 안내 Part2 - 프로젝트 구성 살펴 보기

2022.10.27 - [WPF .NET] - Prism Library를 사용하는 개발자를 위한 안내 Part1

 

이번 포스트에서는 Prism에서 제공하는 여러가지의 커맨드를 사용하는 방법을 알아 보겠습니다.

커맨드의 대한 일반적인 사항은 여기를 참고하시면 됩니다.

1. DelegateCommand

Prism library에서 제공하는 ICommand 인터페이스를 구현한 클래스 이며, 일반적은 사용 방법은 다른 커맨드 클래스들과 비슷합니다. 

아래 셈플은 MainWindowViewModel에 NavigateCommand를 추가한 모습입니다.

뷰모델의 커맨드 프로퍼티의 Type을 인터페이스로 사용했는데, 예전부터 프로퍼티들은 사용이 가능하면 인터페이스 type을 사용하는 편입니다. 그래서, List<string> 보다는 IList<string>을 사용하고, 여기서도 DelegateCommand를 사용하지 않고, ICommand를 사용합니다.
  • NavigateCommand = new DelegateCommand<string>(OnNavigate, CanNavigate)
    • DelegateCommand뒤에 <string>이 붙어 있기 때문에 이 커맨드를 이용할 때는 CommandParameter에 string 데이터를 넣어서 실행시켜야 합니다.
    • OnNavigate는 NavigateCommand가 Execute되면 실행되는 메서드 입니다.
    • CanNavige는 NavigateCommand 사용 가능 여부를 반환합니다. 현재는 return true;를 반환하고 있기 때문에 무조건 실행 가능 상태입니다. 또한, CanNavigate를 만들지 않는 경우에도 무조건 사용 가능입니다.
  • 이동할 뷰 이름을 List에 입력하고 선택할 예정입니다.

MainWindowViewModel.cs

/// <summary>
/// MainWindowViewModel
/// </summary>
public class MainWindowViewModel : BindableBase
{
    /// <summary>
    /// ContainerProvider
    /// </summary>
    private readonly IContainerProvider _containerProvider;

    private IList<string> _navigationNames;
    /// <summary>
    /// Navigation Names
    /// </summary>
    public IList<string> NavigationNames
    {
        get { return _navigationNames; }
        set { SetProperty(ref _navigationNames, value); }
    }

    private string _title = "Prism Application";
    public string Title
    {
        get { return _title; }
        set { SetProperty(ref _title, value); }
    }
    /// <summary>
    /// NavigateCommand
    /// </summary>
    public ICommand NavigateCommand { get; set; }
    /// <summary>
    /// 기본 생성자
    /// </summary>
    public MainWindowViewModel()
    {
    }
    /// <summary>
    /// 런타임 생성자
    /// </summary>
    /// <param name="containerProvider"></param>
    public MainWindowViewModel(IContainerProvider containerProvider)
    {
        //프리즘에서 사용하고 있는 IoC Container를 생성자 주입 후 로컬 변수로 등록하고 사용합니다.
        _containerProvider = containerProvider;
        //NavigateCommand 생성 - 꼭 생성자에서 만들 필요는 없지만 클래스가 만들어진 후 빠르게
        //생성하는 것이 좋습니다.
        NavigateCommand = new DelegateCommand<string>(OnNavigate, CanNavigate);
        //네비게이션 할 View 이름들
        NavigationNames = new List<string>
        {
            "Sample1View",
            "Sample2View"
        };
    }
    /// <summary>
    /// NavigateCommand를 View에서 호출했을 때 실행될 메서드
    /// </summary>
    /// <param name="obj"></param>
    private void OnNavigate(string obj)
    {
        Debug.WriteLine($"OnNavigate : {obj}");
    }
    /// <summary>
    /// NavigateCommand를 사용할 수 있는지 여부반환
    /// </summary>
    /// <param name="arg"></param>
    /// <returns></returns>
    private bool CanNavigate(string arg)
    {
        //지금은 무조건 true를 반환하고 있습니다.
        return true;
    }
}

MainWindow.xaml

  • 콤보박스와 버튼을 화면에 배치하기 위해, Grid를 2개의 Row로 분리합니다.
  • <RowDefinition Height="Auto" /> 는 첫번째 Row의 높이를 자동으로 설정하기 위해 사용합니다.
  • ComboBox에 Width="100"은 StackPanel이 Orientation="Horizontal"로 되어 있기 때문에 너무 작게 표시되는 것을 방지 합니다.
  • ComboBox에 x:Name="NavigationNameComboBox"는 Element Binding을 사용하기 위해서 추가했습니다.
  • Button Command에서 NavigateCommand를 바인딩했습니다. 위에서 이야기한데로 CommandParameter를 사용해야 하기 때문에, ComboBox에서 SelectedItem을 입력합니다.
<Window
    x:Class="PrismStep2.Views.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:prism="http://prismlibrary.com/"
    Title="{Binding Title}"
    Width="525"
    Height="350"
    prism:ViewModelLocator.AutoWireViewModel="True">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
            <ComboBox
                x:Name="NavigationNameComboBox"
                Width="100"
                Margin="5"
                ItemsSource="{Binding NavigationNames}" />
            <Button
                Margin="5"
                Command="{Binding NavigateCommand}"
                CommandParameter="{Binding ElementName=NavigationNameComboBox, Path=SelectedItem}"
                Content="Region Navigate" />
        </StackPanel>
        <ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion" />
    </Grid>
</Window>

실행

콤보박스에 선택된 아이템이 없어도 Region Navigate 버튼이 활성화되어 있습니다. 클릭하면 

OnNavigate :

라는 문구가 Debug 창에 출력됩니다.

콤보박스에 아이템을 선택하고 Region Naviate 버튼을 클릭하면

OnNavigate : SampleView

라는 문구가 출력됩니다.

2. DelegateCommand의 사용 가능 여부 변경하기

선택된 아이템이 없을 때는 버튼을 비활성화 시키고, 선택이 된 경우에만 버튼을 클릭하면 좋을 것 같습니다.

그렇게 하기 위해서는 아이템이 선택되었는지 아닌지를 뷰모델에서 알 수 있어야 합니다.

  • 첫번째 방법은 ComboBox의 SelectedItem 프로퍼티를 뷰모델에 바인딩하는 방법이 있습니다.
  • 두번째 방법은 ComboBox의 SelectionChanged 이벤트를 InvokeActionCommand와 연결해서 뷰모델의 커맨드를 실행시키는 방법이 있습니다.
  • 여기서는 간단한 첫번째 방법을 사용합니다. 이렇게 선택된 아이템을 뷰모델에서 미리 알 수 있다면, NavigateCommand에서 string 값을 CommandParameter로 받을 필요도 없습니다.
  • DelegateCommand의 사용 가능여부를 다시 판단하기 위해서 사용하는 전통적인 방법은 RaiseCanExecuteChanged()를 실행하는 것입니다.
  • DelegateCommand는 위의 방법 외에도 ObservesProperty(), ObservesCanExecute()를 이용해서 커맨드의 사용 여부를 확인 할 수 있습니다.

2-1. ObservesProperty

특정 프로퍼티의 값이 변경될 때 마다, 커맨드의 실행 가능 여부를 계속 확인을 해서 결과를 반환하는 방법입니다. 즉, .ObservesProperty(() => SelectedNavigationName) 이렇게 입력을 한다면, SelectedNavigationName 프로퍼티가 변경될 때마다, RaiseCanExecuteChanged()를 실행해서 사용 여부를 확인 할수 있다는 것입니다.

2-2. ObservesCanExecute

특정 프로퍼티의 값이 bool형일 때만 사용하며, CanExecute 대리자도 사용할 필요 없이, 프로퍼티의 값이 변경되면 사용 여부를 반환하게 됩니다.

 

최종적으로 아래와 같이 수정했습니다.

MainWindowViewModel.cs

    /// <summary>
    /// MainWindowViewModel
    /// </summary>
    public class MainWindowViewModel : BindableBase
    {
        /// <summary>
        /// ContainerProvider
        /// </summary>
        private readonly IContainerProvider _containerProvider;

        private IList<string> _navigationNames;
        /// <summary>
        /// Navigation Names
        /// </summary>
        public IList<string> NavigationNames
        {
            get { return _navigationNames; }
            set { SetProperty(ref _navigationNames, value); }
        }

        private string _title = "Prism Application";
        public string Title
        {
            get { return _title; }
            set { SetProperty(ref _title, value); }
        }
        private string _selectedNavigationName;
        /// <summary>
        /// 선택된 네비게이션 할 뷰이름
        /// </summary>
        public string SelectedNavigationName
        {
            get { return _selectedNavigationName; }
            set { SetProperty(ref _selectedNavigationName, value); }
        }
        /// <summary>
        /// NavigateCommand
        /// </summary>
        public ICommand NavigateCommand { get; set; }
        /// <summary>
        /// 기본 생성자
        /// </summary>
        public MainWindowViewModel()
        {
        }
        /// <summary>
        /// 런타임 생성자
        /// </summary>
        /// <param name="containerProvider"></param>
        public MainWindowViewModel(IContainerProvider containerProvider)
        {
            //프리즘에서 사용하고 있는 IoC Container를 생성자 주입 후 로컬 변수로 등록하고 사용합니다.
            _containerProvider = containerProvider;
            //NavigateCommand 생성 - 꼭 생성자에서 만들 필요는 없지만 클래스가 만들어진 후 빠르게
            //생성하는 것이 좋습니다.
            NavigateCommand = new DelegateCommand(OnNavigate, CanNavigate)
                                .ObservesProperty(() => SelectedNavigationName);
            //네비게이션 할 View 이름들
            NavigationNames = new List<string>
            {
                "Sample1View",
                "Sample2View"
            };
        }
        /// <summary>
        /// NavigateCommand를 View에서 호출했을 때 실행될 메서드
        /// </summary>
        /// <param name="obj"></param>
        private void OnNavigate()
        {
            Debug.WriteLine($"OnNavigate : {SelectedNavigationName}");
        }
        /// <summary>
        /// NavigateCommand를 사용할 수 있는지 여부반환
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        private bool CanNavigate()
        {
            //SelectedNavigationName가 빈값이 아닌 경우에만 사용 가능
            return string.IsNullOrEmpty(SelectedNavigationName) == false;
        }
    }

MainWindow.xaml

SelectedItem을 뷰모델의 SelectedNavigationName과 바인딩 할 때는 TwoWay로 해야합니다.

<Window
    x:Class="PrismStep2.Views.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:prism="http://prismlibrary.com/"
    Title="{Binding Title}"
    Width="525"
    Height="350"
    prism:ViewModelLocator.AutoWireViewModel="True">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
            <ComboBox
                x:Name="NavigationNameComboBox"
                Width="100"
                Margin="5"
                ItemsSource="{Binding NavigationNames}"
                SelectedItem="{Binding SelectedNavigationName, Mode=TwoWay}" />
            <Button
                Margin="5"
                Command="{Binding NavigateCommand}"
                Content="Region Navigate" />
        </StackPanel>
        <ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion" />
    </Grid>
</Window>

실행

버튼이 비활성화 되어 있습니다.

아이템을 선택하면 버튼이 활성화됩니다.

프리즘 라이브러리의 DelegateCommand에 대해서 더 자세한 사항은 여기를 참고하시기 바랍니다.

3. 소스

WpfTest/PrismStep2 at master · kaki104/WpfTest (github.com)

 

GitHub - kaki104/WpfTest

Contribute to kaki104/WpfTest development by creating an account on GitHub.

github.com

DelegateCommand에 대해서 궁금하신 사항은 리플로 남겨주시면 알려드리겠습니다.

반응형
댓글