참조해주신 답변 링크의 경우 두 개의 뷰가 하나의 쌍으로 동작하는 특수한 상황에 대한 답변으로 보통은 View와 ViewModel은 1:1로 매핑됩니다.
일반적인 MVVM에서 새 창을 띄우는 케이스가 아니라면 View와 ViewModel은 DataTemplate
을 통해 연결할 수 있기 때문에 ViewModel이 View를 직접 참조하지 않아도 MVVM을 위반하지 않고 View를 표시 할 수 있습니다.
<DataTemplate DataType="{x:Type local:SomeViewModel}">
<local:SomeView/>
</DataTemplate>
하지만 새로운 창을 띄워야 하는 상황이라면, 부모 ViewModel에서 아래와 같은 서비스를 가지고 있다 사용하는 방법을 고려해 볼 수 있습니다.
public class WindowResolveService
{
private readonly Dictionary<Type, Type> _map = new Dictionary<Type, Type>();
/// <summary>
/// ViewModel, View(Window) 타입 등록
/// </summary>
public void Register<TViewModel, TView>()
where TViewModel : ViewModelBase
where TView : Window
{
_map[typeof(TViewModel)] = typeof(TView);
}
public void Show(ViewModelBase viewModel)
{
var window = CreateWindow(viewModel);
window.Show();
}
public bool? ShowDialog(ViewModelBase viewModel)
{
var window = CreateWindow(viewModel);
return window.ShowDialog();
}
private Window CreateWindow(ViewModelBase viewModel)
{
var vmType = viewModel.GetType();
if (!_map.TryGetValue(vmType, out var viewType))
throw new InvalidOperationException($"등록되지 않은 ViewModel 타입입니다: {vmType.Name}");
var window = (Window)Activator.CreateInstance(viewType)!;
window.DataContext = viewModel;
return window;
}
}
의존성 주입을 사용하고 있지 않다면 ViewModel 형식에 따른 View 형식의 등록은 App
클래스에서 전역적으로 설정하시거나, View 계열 클래스에서 필요한 것만 등록해서 사용하시면 됩니다.
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var windowService = new WindowResolveService();
// ViewModel ↔ Window 매핑 등록
windowService.Register<DetailViewModel, DetailWindow>();
windowService.Register<SettingsViewModel, SettingsWindow>();
var mainViewModel = new MainViewModel { WindowService = windowService };
var mainWindow = new MainWindow { DataContext = mainViewModel };
mainWindow.Show();
}
}
호출은 아래와 같이
public class MainViewModel
{
public WindowResolveService WindowService { get; set; }
// public required WindowResolveService WindowService { get; init; }
...
private void OnExecuteShowSettingDialogCommand(object arg)
{
var settingsVM = new SettingsViewModel
{
...
};
var result = WindowService.ShowDialog(settingsVM);
if (result == true)
{
...
}
});
}
또한, 위와 같은 상황이 필요한 경우에 대해서 답변 드리자면,
엔터프라이즈 어플리케이션 급으로 깊이 들어가면 구조가 조금은 달라질 수 있지만 View(Window)를 생성 및 표시하는 책임을 갖는 별도의 서비스를 통하는 기본적인 접근 방법은 같습니다.
public class ABWindowService //: IABWindowService
{
public void Show(SharedViewModel viewModel)
{
var winA = new AWindow { DataContext = viewModel };
var winB = new BWindow { DataContext = viewModel };
winA.Show();
winB.Show();
}
}
최근 커뮤니티에 부쩍 MVVM에 대한 논의가 올라오고 있는데요,
복잡도가 크지않은 간단한 프로젝트라면 원칙적으로 접근하지 않고 오직 View 생성할 때만 ViewModel이 View의 형식 알고 있는것을 허용한다 라는 예외 규칙을 두고 WPF와 MVVM이 제공하는 편의성을 적절히 활용하는 절충안도 검토해 볼 만할 것 같습니다.
상황에 따라 유연한 접근을 취하는 것도 빠른 배움에 도움이 되지 않을까 합니다.
이상입니다.