웹을 해보다가 wpf를 하다보니 헷갈려서 질문 드립니다
제가 wpf에서 특정 화면에서 다른 화면으로 전환될 때 그 화면을 페이지 태그로 구현하는 방법과 usercontrol 태그로 화면을 구현하는 방법이 있는 것 같은데 어떤 방식이 더 많이 사용되고 범용적인가요?
웹으로 할때는 버튼 마다 html페이지를 연결하는 식으로 하면 됐던 것 같은데 그 방식으로는 wpf에 적용되기가 어려운 것 같아서요
웹을 해보다가 wpf를 하다보니 헷갈려서 질문 드립니다
제가 wpf에서 특정 화면에서 다른 화면으로 전환될 때 그 화면을 페이지 태그로 구현하는 방법과 usercontrol 태그로 화면을 구현하는 방법이 있는 것 같은데 어떤 방식이 더 많이 사용되고 범용적인가요?
웹으로 할때는 버튼 마다 html페이지를 연결하는 식으로 하면 됐던 것 같은데 그 방식으로는 wpf에 적용되기가 어려운 것 같아서요
코파일럿 답변인데 참고가 될 수 있을것 같아 공유해 봅니다.
Copilot + Smart 사용
WPF에서 화면 전환을 구현할 때 Page와 UserControl은 비슷해 보이지만, 실제로는 용도와 철학이 조금 다릅니다. 웹 개발 경험이 있으셔서 더 헷갈리실 수 있는데, 차이를 정리해 드릴게요.
| 구분 | Page | UserControl |
|---|---|---|
| 주요 용도 | 내비게이션 기반 앱 (Frame, NavigationWindow 안에서 사용) | 재사용 가능한 화면 단위 (하나의 Window 안에서 교체/표시) |
| 특징 | - 브라우저처럼 앞/뒤 이동 기록 지원 - Frame.Navigate()로 전환- XBAP 같은 웹 호스팅 시 유용 |
- 독립적인 UI 조각 - 하나의 Window 안에서 ContentControl, TabControl, Grid 영역 등에 교체 삽입 - MVVM 패턴과 잘 어울림 |
| 장점 | - 내비게이션 히스토리 자동 관리 - URL 기반 탐색 가능 |
- 가볍고 단순 - 재사용성 높음 - 대부분의 데스크톱 앱에서 표준처럼 사용 |
| 단점 | - 데스크톱 앱에서는 오히려 과한 경우 많음 - Frame/NavigationWindow를 써야 함 |
- 내비게이션 히스토리는 직접 구현해야 함 (예: Stack으로 관리) |
ContentControl이나 Grid 영역에 현재 보여줄 UserControl을 교체하는 방식으로 구현합니다.<!-- MainWindow.xaml -->
<Window ...>
<ContentControl Content="{Binding CurrentView}" />
</Window>
// MainViewModel.cs
public class MainViewModel {
public object CurrentView { get; set; }
public MainViewModel() {
CurrentView = new HomeViewModel(); // 시작 화면
}
public void NavigateToProducts() {
CurrentView = new ProductsViewModel();
}
}
<!-- App.xaml -->
<Application.Resources>
<DataTemplate DataType="{x:Type vm:HomeViewModel}">
<views:HomeView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:ProductsViewModel}">
<views:ProductsView />
</DataTemplate>
</Application.Resources>
이렇게 하면 버튼 클릭 시 ViewModel만 바꿔주면 자동으로 UserControl이 교체됩니다.
Page + Frame 구조 (하지만 흔치 않음)UserControl + ContentControl 구조 (가장 많이 쓰임, MVVM과 찰떡궁합)Page + Frame 사용하게 되면 Frame 기본 동작이 뷰를 변경할 때마다 다시 만들어주는 것 같던데
그렇게 되면 DI를 이용할 수가 없고 뷰 재사용하고 싶어도 어렵더라구요 (하려면 따로 캐싱하고 그래야 하는 것 같던데) DataContext가 분리되는 문제도 있고
디모이님 말씀처럼 UserControl + ContentControl 방법이 제일 많이 쓰이는 것 같아요
저도 그렇게 사용중이고
답변 감사합니다
더 다양한 의견 있으시면 말씀해주세요
예전에 답변했던 내용을 링크합니다. (무조건 UserControl입니다ㅎ)
저는 개인적으로 WPF에서는 UserControl과 ContentControl, DataTemplate의 조합을 자주 사용하고, WinUI 3에서는 Page와 NavigationView, Frame의 조합을 자주 사용합니다.
WPF는 Frame.Navigate가 Uri를 주축으로 되어 있기 때문에 사용이 불편했습니다.
대신 DataTemplate을 Key를 지정하지 않고도 정의할 수 있기 때문에 리소스 딕셔너리를 만들어서 DataTemplate으로 ViewModel과 View를 묶어놓고, app.xaml에 merge하는 방법을 즐겨 사용합니다.
이렇게 하면 Window ViewModel에서 그리고 싶은 Content에 대한 ViewModel을 속성으로 노출하고, Window에서 해당 속성을 ContentControl에 바인딩하면 DataTemplate에서 매핑해둔 View가 Content Area에 그려집니다.
MVVM 친화적으로 화면을 그리는 방법은 쉬우므로, Content로 그릴 ViewModel을 사용자가 선택하게 하는 기능만 잘 구현하면 됩니다.
DataTemplate을 이용한 View 로딩은 이 아티클을 읽어보시면 많은 도움이 될 것 같습니다.
<!-- DataTemplate으로 ViewModel과 View를 매핑 -->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewmodels="clr-namespace:WpfApp1.ViewModels"
xmlns:views="clr-namespace:WpfApp1.Views">
<DataTemplate DataType="{x:Type viewmodels:HomeViewModel}">
<views:HomeView />
</DataTemplate>
</ResourceDictionary>
<Window>
<!-- Menu, Button 등으로 ViewModel을 선택하는 UI를 추가하면 됨 -->
<ContentControl Content="{Binding Content}"/>
</Window>
public partial class MainWindowViewModel : ObservableObject
{
[ObservableProperty]
private object? _content; // 이 Content만 원하는 ViewModel로 바꿔주면 됨
public MainWindowViewModel()
{
_content = Ioc.Default.GetService<HomeViewModel>();
}
}
WinUI 3에서는 DataTemplate에 Key가 필수이고, WPF처럼 ViewModel 대충 넘겨줄 테니까 알잘딱으로 View 찾아서 그려줘 가 안됩니다. DataTemplateSelector라는 걸 구현해서 ViewModel에 따라 적절한 DataTemplate을 찾는 로직을 구현해줘야 합니다.
대신 내비게이션을 돕는 NavigationView라는 컨트롤이 있고, Frame.Navigate가 대상 페이지의 Type을 받아 동작하며, 페이지 전환 애니메이션 같은 게 자체적으로 구현되어 있습니다. (윈도우 설정 앱이나 MS스토어에서의 페이지 전환을 생각하시면 됩니다.) 이런 걸 버리고 ContentControl로 넘어가긴 좀 아쉽죠.
그래서 Page와 Frame, NavigationView를 사용하고, MVVM과 DI 측면에서 부족한 로직을 보완해주는 서비스 계층을 곁들이는 방식을 자주 사용합니다.
WinUI 3쪽 내비게이션 서비스는 TemplateStudio 확장에서 생성되는 코드가 꽤 잘 구현합니다.