MVVM패턴으로 개발을 하면 주요 로직은 ViewModel 또는 Model에서 동작하고 View에서는 ViewModel에 Binding 된 값을 화면에 보여주기만 합니다. 그런데 이때 문제가 되는 부분이 새로운 Window, 또는 MessageBox를 Popup 할 때입니다. MVVM패턴을 유지하면서 새로운 View을 생성하고자 할 때 IDialogService를 사용합니다.
✅ IDialogService
좀 더 자세히 얘기하면 로직이 수행되는 도중에 특정 상황이 발생하면 새로운 화면을 Popup 해야 할 경우,
수행 중인 로직이 ViewModel, 또는 Model에 있다면 View를 제어할 수가 없습니다. MVVM 패턴에서 Model, ViewModel은 View를 알 수 없기 때문이죠.
이때 interface를 통해 View와 ViewModel에 징검다리를 만들고 App.xaml.cs에서 연결해 주면 ViewModel에서 MessageBox 등을 생성할 수 있게 됩니다.
IDialogService는 MS에서 제공하는 interface는 아니고 RelayCommand와 같이 사용자 편의에 의해 만들어 사용하는 keyword입니다.
View와 ViewModel에서 같이 사용해야하므로 Solution 내 적절한 위치에 interface를 생성합니다.
소스 코드
IDialogService.cs
public interface IDialogService
{
void ShowMessage(string message);
bool ShowConfirmation(string message);
}
예제 프로젝트에서는 Message를 보여주는 MessageBox와 Yes or No를 결정하는 MessageBox, 두 가지를 만듭니다.
DialogService.cs
public class DialogService : IDialogService
{
public void ShowMessage(string message)
{
MessageBox.Show(message);
}
public bool ShowConfirmation(string message)
{
return MessageBox.Show(message, "Confirmation", MessageBoxButton.YesNo) == MessageBoxResult.Yes;
}
}
실제 View를 생성하는 클래스입니다.
MessageBox 외에도 새로운 View를 생성하고자 할 경우 이 클래스에서 구현하면 됩니다.
App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
IDialogService dialogService = new DialogService();
MainViewModel viewModel = new MainViewModel(dialogService);
MainWindow view = new MainWindow();
view.DataContext = viewModel;
view.Show();
}
}
App.xaml.cs 에 OnStartup를 override 합니다.
DialogService를 생성하고 ViewModel에 인자로 넘겨줘서 View와 ViewModel 간 DialogService를 공유합니다.
View의 DataContext에 ViewModel을 등록하기 위해 View를 생성합니다.
view.Show() 코드로 View가 Popup되는데 이 상태에서 실행하면 View가 두 개가 생성됩니다.
App.xaml
<Application x:Class="DialogService_Test.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DialogService_Test"
StartupUri="MainWindow.xaml" <-- 삭제
>
<Application.Resources>
</Application.Resources>
</Application>
StartupUri="MainWindow.xaml" 를 삭제합니다.
MainWindow.xaml
<Grid>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding MessageText}" Width="300" Height="20" VerticalAlignment="Center" HorizontalAlignment="Center" Background="SkyBlue"
/>
<Rectangle Height="50"/>
<Button Content="Popup ShowMessage" Command="{Binding CmdShowMessage}"/>
<Button Content="Popup ConfirmMessage" Command="{Binding CmdConfirmMessage}"/>
</StackPanel>
</Grid>
TextBlock 한 개와 Button이 두 개 있는 View를 만듭니다.
TextBlock의 Text와 각 버튼의 Command는 ViewModel과 Binding 되어있습니다.
MainViewModel.cs
public class MainViewModel : BaseViewModel
{
private readonly IDialogService _dialogService;
private string _message;
public string MessageText
{
get => _message;
set
{
_message = value;
OnPropertyChanged(nameof(MessageText));
}
}
private ICommand _cmdShowMessage;
public RelayCommand<object> CmdShowMessage
{
get
{
_cmdShowMessage ??= new RelayCommand<object>(ShowMessage);
return (RelayCommand<object>)_cmdShowMessage;
}
}
private ICommand _cmdConfirmMessage;
public RelayCommand<object> CmdConfirmMessage
{
get
{
_cmdConfirmMessage ??= new RelayCommand<object>(ShowConfirmation);
return (RelayCommand<object>)_cmdConfirmMessage;
}
}
public MainViewModel(IDialogService dialogService)
{
_dialogService = dialogService;
MessageText = "TEST";
}
public void ShowMessage(object obj)
{
_dialogService.ShowMessage("Hello, world!");
MessageText = "Hello, world!";
}
public void ShowConfirmation(object obj)
{
bool result = _dialogService.ShowConfirmation("Are you sure?");
if (result)
{
MessageText = "Yes";
}
else
{
MessageText = "No";
}
}
}
ShowMessage 버튼, ConfirmMessage 버튼을 클릭하면 MessageBox가 Popup 되며 TextBlock에 이벤트 결과가 나타납니다.
결과
✅ IDialogService - 끝