WPF-UI 튜토리얼 #3

guru chun (haechul chun)·2025년 2월 3일
0

WPF 개발

목록 보기
4/10

살짝 고민이 된다. 지난번에 확인하지 못했던 내용을 마저 확인하고 이해한 후에 진도를 나갈지, 우선 전체를 훑어보고 다시 돌아와서 이전에 넘겼던 내용을 제대로 볼 것인지...
앞에서 이해하지 못한 내용이 뒤에서 발목을 잡을 가능성도 있기에 마냥 무시하기도 어렵다.
그러니, 진도를 나가기 전에 가볍게라도 살펴보고 넘어가는걸로 하자.

의존성 주입

Host를 생성할 때 서비스를 등록하는 부분에서 services.AddHostedService<MyHostService>() 같은 코드를 보았다. 이 코드를 이해하기 위해 찾아보면 의존성 주입이라는 말이 나온다. 이게 말은 어려워보이지만 별거 없다.
라이브러리가 참조하는(의존하는) 객체를 런타임에 라이브러리 밖에서 만들어 넘겨주겠다는(주입하는) 것이다. 그것이 가능한 이유는 라이브러리는 객체가 아닌 인터페이스를 참조해서 구현하면 되기 때문이다. 나중에 더 자세히 살펴볼 것이니, 이 정도만 이해하고 넘어가자.

App

App.xaml은 아래와 같이 정의되어 있다.

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ui:ThemesDictionary Theme="Light" />
            <ui:ControlsDictionary />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>
  • Application.Resources Application이 사용할 리소스를 정의한다. 아이콘 이미지, 컨트롤의 색상, 모양 같은 것들이 리소스로 정의될 수 있다.
  • ResourceDictionary 런타임에 접근하는 동적 리소스를 정의한다.
  • ResourceDictionary.MergedDictionaries 외부에 정의한 ResourceDictionary를 불러와 병합한다.
  • ui:ThemesDictionary 패키지 WPF-UI에서 정의한 Light/Dark 테마용 ResourceDictionary를 병합한다.
  • ui:ControlsDictionary 패키지 WPF-UI에서 정의한 UI-Control용 ResourceDictionary를 불러와 병합한다.
  • Application.Resources에 정의된 ResourceDictionary는 모든 화면에서 공통으로 참조할 수 있다.

WPF-UI 예제와 문서

이 프로젝트는 WPF-UI Control을 사용할 것이므로 'WPF UI Gallery' 프로그램을 실행해서 실제 화면에 어떻게 보여지는지 미리 확인하고, 문서(https://wpfui.lepo.co)를 참조해 코딩하는 것이 좋다.
wpf-ui-gallery
wpf-ui-site

MainWindow(View)

MainWindow.xaml

  • Window: 메뉴바, 툴바, 상태바 등을 갖는 Application의 메인창이다. WPF-UI의FluentWindow를 사용한다.
  • Window는 TitleBar영역과 NavigationView 영역으로 나눠진다.
    • TitleBar에는 제목(Title)과 아이콘(TitleBar.Icon)이 설정된다.
    • TitleBar.Title은 ViewModel.ApplicationTitle에 바인딩된다.
    • Window.Title은 화면에 표시되지 않지만 OS에 의해 사용되므로 동일하게 바인딩한다.
  • NavigationView는 다음과 같은 레이아웃을 가진다.
    • NavigationView의 이름은 RootNavigation이다.
    • 좌측의 메뉴는 ViewModel.MenuItems에 바인딩된다.
    • 메뉴의 맨 하단은 ViewModel.FooterMenuItems에 바인딩된다.
    • NavigationView Header에는 BreadscrumBar가 위치한다.
    • NavigationView Header 아래가 Content 영역이다.
    • Content영역에 Snackbar를 Overlay할 수 있도록 SnackbarPresenter가 위치한다.
    • Content영역에는 ContentPresenter가 위치한다. 이름은 RootContentDialog이다.

여기서 Binding이 어떻게 동작하는지 궁금해질 수 있는데, 일단은 ModelView에서 제공하는 데이터를 컨트롤에 표시해준다는 정도로만 이해하고 넘어가자.

MainWindow.xaml.cs

MainWindow는 WPF-UI의 FluentWindow이고 INavigationWindow 인터페이스를 구현한다. INavigationWindow 인터페이스는 WPF-UI MVVM 모델과 MainWindow의 NavigationView를 연결한다.

MainWindow의 생성자는 MainWindowViewModel, IPageService, INavigationService를 인자로 받는다.

  • MVVM 모델에서 View와 ViewModel은 View.BindingCotext를 통해 연결되는데 이런 관계가 보이지 않는다. WPF-UI 패키지 안에서 이루어지는것 같다.
  • navigationService.SetNavigationControl(RootNavigation)
    NavigationService에 NavigationView를 지정한다. 이렇게 하면 NavigationService와 View가 한몸으로 동작할 것 같다.
  • RootNavigation.SetPageService(pageService) NavigationView에 PageService를 지정한다. 이렇게하면 NavigationView가 표시할 Page Content를 PageService를 통해 접근할 수 있을 것 같다.

MainWindow는 INavigationWindow를 구현한다.

  • GetNavigation() MainWindow의 NavigationView를 참조한다.
  • Navigate(Type pageType) MainWindow의 NavigationView를 통해서 Page를 이동한다.
  • ShowWindow() MainWindow를 보이게 한다.
  • CloseWindow() MainWindow를 닫는다.

INavigationWindow가 어떻게 사용되는지는 이전에 살펴보았던 ApplicationHostService의 아래 코드를 참조해볼 수 있다.

private async Task HandleActivationAsync()
{
	...
	_navigationWindow = (
		_serviceProvider.GetService(typeof(INavigationWindow)) as INavigationWindow
	)!;
	_navigationWindow!.ShowWindow();
	_navigationWindow.Navigate(typeof(Views.Pages.DashboardPage));
	...
}
  • _serviceProvider는 위에 의존성 주입에 대해 설명할 때 언급되었던 ServiceCollection이다. AddSingleton()으로 추가했던 서비스와 Window, ViewModel에서 INavigationWindow 인스턴스를 찾아 _navigationWindow에 담는다.
  • _navigationWindow가 가리키는 Window가 MainWindow이고, INavigationWindow를 통해서 MainWindow의 ShowWindow()와 Navigate()를 호출하고 있다.

MainWindow(ViewModel)

MainWindowViewModel은 다음과 같은 속성을 제공한다.

  • ApplicationTitle
  • MenuItems
  • FooterMenuItems
  • TrayMenuItems

ViewModel은 ObservableObject를 상속받는다. ViewModel에서 PropertyChanged 이벤트와 Command를 쉽게 구현하기 위한 것인데, 나중에 좀 더 알아보기로 하고 넘어가자.
ViewModel이 ObservableObject를 상속받으면 위 속성은 [ObservableProperty] Annotation을 통해 Property와 Notification을 쉽게 구현할 수 있다.(컴파일하면 관련 코드가 자동으로 생성된다.)

public partial class MainWindowViewModel : ObservableObject
{
    [ObservableProperty]
    private string _applicationTitle = "WPF UI - UiDesktopApp1";
    ...
}
profile
오늘도, 내일도 코딩을 즐기자

0개의 댓글