프로그래밍/WPF

[WPF] MVVM에서 IDialogService 사용

흔한티벳여우 2021. 5. 10. 17:12
반응형

mvvm을 사용할 때, Dialog용 Service를 만들어서 사용하는 방법을 기술하겠다.

DialogServer용 폴더 구조

위의 구조대로 만들 예정이다. 만약 새로운 화면이 필요하다면 커스텀 DialogView를 이용하여 처리하면 된다. 

 

IDialogWindow.cs

namespace CounterMonitor.Dialogs.Service
{
    public interface IDialogWindow
    {
        bool? DialogResult { get; set; }
        object DataContext { get; set; }

        bool? ShowDialog();
    }
}

창을 띄울 화면 생성

DialogWindow.xaml

<Window x:Class="CounterMonitor.Dialogs.Service.DialogWindow"
        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:CounterMonitor.Dialogs.Service"
        mc:Ignorable="d"
        Title="{Binding Title}"
        SizeToContent="WidthAndHeight"
        WindowStartupLocation="CenterScreen"
        Height="300" Width="300">
    <ContentControl x:Name="ContentPresenter" Content="{Binding}"/>
</Window>

 

해당 위의 인터페이스를 상속하자

DialogWindow.xaml.cs

using System.Windows;

namespace CounterMonitor.Dialogs.Service
{
    /// <summary>
    /// DialogWindow.xaml에 대한 상호 작용 논리
    /// </summary>
    public partial class DialogWindow : Window, IDialogWindow
    {
        public DialogWindow()
        {
            InitializeComponent();
        }
    }
}

 

DialogWindow에 바인딩할 기초 ViewModel 생성

DialogViewModelBase.cs

namespace CounterMonitor.Dialogs.Service
{
    public abstract class DialogViewModelBase<T>
    {
        public string Title { get; set; }
        public string Message { get; set; }
        public T DialogResult { get; set; }

        public DialogViewModelBase() : this(string.Empty, string.Empty) { }
        public DialogViewModelBase(string title) : this(title, string.Empty) { }
        protected DialogViewModelBase(string title, string message)
        {
            Title = title;
            Message = message;
        }

        public void CloseDialogWithResult(IDialogWindow dialog, T result)
        {
            DialogResult = result;
            if (dialog != null)
            {
                dialog.DialogResult = true;
            }
        }
    }
}

 

호출할 ViewModel에서 사용할 Service Interface 선언

IDialogService.cs

namespace CounterMonitor.Dialogs.Service
{
    public interface IDialogService
    {
        T OpenDialog<T>(DialogViewModelBase<T> viewModel);
    }
}

실제 객체 선언

DialogService.cs

namespace CounterMonitor.Dialogs.Service
{
    public class DialogService : IDialogService
    {
        public T OpenDialog<T>(DialogViewModelBase<T> viewModel)
        {
            IDialogWindow window = new DialogWindow();
            window.DataContext = viewModel;
            window.ShowDialog();
            return viewModel.DialogResult;
        }
    }
}

 

이제 기본 골격 완성되었다. 간단한 Alert 다이어로그를 만들어보자.

Dialog 결과를 받을 enum 선언

DialogResults.cs

namespace CounterMonitor.Dialogs
{
    public enum DialogResults
    {
        Undefined,
        Yes,
        No
    }
}

DialogWindow.xaml에 띄울 usercontrol 생성

AlertDialogView.xaml

<UserControl x:Class="CounterMonitor.Dialogs.Alert.AlertDialogView"
             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:CounterMonitor.Dialogs.Alert"
             mc:Ignorable="d" 
             Height="100" Width="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0"
                   Text="{Binding Message}"
                   HorizontalAlignment="Center" VerticalAlignment="Center" TextWrapping="Wrap"/>
        <Button Grid.Row="1"
                Content="OK"
                Command="{Binding OKCommand}"
                CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"/>
    </Grid>
</UserControl>

AlertDialogView에 바인딩할 ViewModel 생성

AlertDialogViewModel.cs

using CounterMonitor.Dialogs.Service;
using Prism.Commands;

namespace CounterMonitor.Dialogs.Alert
{
    public class AlertDialogViewModel : DialogViewModelBase<DialogResults>
    {
        public DelegateCommand<IDialogWindow> OKCommand { get; private set; }
        public AlertDialogViewModel(string title, string message) : base(title, message)
        {
            OKCommand = new DelegateCommand<IDialogWindow>(OnOKCommand);
        }

        private void OnOKCommand(IDialogWindow window)
        {
            CloseDialogWithResult(window, DialogResults.Undefined);
        }
    }
}

ViewModel과 view 링크처리

App.xaml

<Application x:Class="CounterMonitor.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:CounterMonitor"
             xmlns:alert="clr-namespace:CounterMonitor.Dialogs.Alert"
             StartupUri="View/MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! -->
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
                <!-- Theme setting -->
                <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" />
            </ResourceDictionary.MergedDictionaries>
            
            <DataTemplate DataType="{x:Type alert:AlertDialogViewModel}">
                <alert:AlertDialogView/>
            </DataTemplate>
        </ResourceDictionary>
    </Application.Resources>
</Application>

 

실제 사용할 ViewModel에서 호출

MainWindowViewModel.cs

namespace CounterMonitor.ViewModel
{
    class MainWindowViewModel : BindableBase
    {
        private IDialogService _dialogService;

        public DelegateCommand TestCommand { get; private set; }

        public MainWindowViewModel()
        {
            _dialogService = new DialogService();

            TestCommand = new DelegateCommand(OnTesetCommnad);
        }


        private void OnTesetCommnad()
        {
            var dialog = new AlertDialogViewModel("Attention", "this is an alert!");
            var result = _dialogService.OpenDialog(dialog);
        }
    }
}

 

끝!!

반응형