Skip to main content

Command Palette

Search for a command to run...

[WinUI 3] 타이틀바를 포함한 테마를 지원하는 WinUI 3 만들기

Published
3 min read

개요

WinUI 3은 윈도의 테마와 연계된 테마 시스템을 제공합니다. WinUI 3에서 제공하는 테마 기능과 함께 타이틀바도 테마에 맞게 색을 조정하는 방법을 알아봅시다.

개발환경

  • Visual Studio 2022 미리보기 4
  • .NET 6 RC1
  • Windows App SDK 미리보기 2

WinUI 3에서 테마 동작

WinUI 3에서는 테마의 기본 동작은 Default입니다. Default일 경우 윈도 테마를 그대로 적용한다는 의미인데요, 가령 윈도 테마 컬러가 어둡게일 경우 각종 Element의 스타일에 다크 테마가 적용되는 식입니다.

| 윈도 테마 색이 밝게일 경우

image.png

| 윈도 테마 색이 어둡게일 경우

image.png

그런데 타이틀바가 테마를 따라가주질 못하네요!

WinUI 3에서 테마 스타일 지정

윈도10에서는 Light 및 Dark 테마를 지원합니다. 윈도 어플리케이션에서는 이 테마에 맞게 자신의 테마를 조정하기를 원할 텐데요. 다음의 기능을 통해 가능합니다.

| FrameworkElement.cs

public ElementTheme ActualTheme { get; }
public ElementTheme RequestedTheme { get; set; }

WinUI 3은 FrameworkElement를 통해 구현된 컨트롤은 ActualTheme 속성을 이용해 현재 선택된 태마를 확인할 수 있습니다. 그리고 RequestedTheme 속성에 테마를 설정할 수 있는데요, 설정 가능한 테마는 다음과 같습니다.

| ElementTheme.cs

    public enum ElementTheme
    {
        // 기본(윈도 설정 테마)
        Default,
        // 밝게
        Light,
        // 어둡게
        Dark
    }

테마 설정은 일반적으로 Window.ContentRequestedTheme 속성을 설정하여 Content 이하의 모든 컨트롤에 적용합니다. 지역적으로 적용하려면 지역적으로 적용할 컨테이너의 RequestedTheme를 설정하는 것으로 가능합니다.

테마가 변경되었을 때 관련 작업을 하기 위해서 이벤트를 구독할 수 있습니다.

rootElement.ActualThemeChanged += (s, e) => { };

이 이벤트는 사용자가 테마를 변경하는 것 뿐만 아니라 시스템에서 테마가 변경된 것을 감지할 수 있습니다.

WinUI 3 타이틀바 테마 적용

위에서 확인한 것처럼 윈도 색 테마가 어둡게로 되어 있어도 어두운 테마가 타이틀바 까지는 적용되지 않는데요, 타이틀바는 WinUI 3의 구성이 아니기 때문입니다. 이를 개선하기 위해서는 ActualTheme 속성과 ActualThemeChanged 이벤트를 통해 현재 설정된 테마의 종류와 윈도 테마 색이 변경되었을 때 감지해서 타이틀 바의 색을 변경해야 합니다.

타이틀바를 변경하기 위해선 AppWindow의 도움을 받아야 하는데요, AppWindow는 다음처럼 획득할 수 있습니다.

    public AppWindow AppWindow
    {
        get
        {
            var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
            var winId = Win32Interop.GetWindowIdFromWindow(hWnd);
            return AppWindow.GetFromWindowId(winId);
        }
    }

그리고 타이틀바의 각종 색은 다음처럼 변경할 수 있습니다.

private void ModifyTitlebarTheme()
    {
        var content = (Content as FrameworkElement)!;
        var value = content.ActualTheme;

        var titleBar = AppWindow.TitleBar;
        if (value == ElementTheme.Light)
        {
            titleBar.ForegroundColor = Colors.Black;
            titleBar.BackgroundColor = Colors.White;
            titleBar.InactiveForegroundColor = Colors.Gray;
            titleBar.InactiveBackgroundColor = Colors.White;

            titleBar.ButtonForegroundColor = Colors.Black;
            titleBar.ButtonBackgroundColor = Colors.White;
            titleBar.ButtonInactiveForegroundColor = Colors.Gray;
            titleBar.ButtonInactiveBackgroundColor = Colors.White;

            titleBar.ButtonHoverForegroundColor = Colors.Black;
            titleBar.ButtonHoverBackgroundColor = Color.FromArgb(255, 245, 245, 245);
            titleBar.ButtonPressedForegroundColor = Colors.Black;
            titleBar.ButtonPressedBackgroundColor = Colors.White;
        }
        else if (value == ElementTheme.Dark)
        {
            titleBar.ForegroundColor = Colors.White;
            titleBar.BackgroundColor = Color.FromArgb(255, 31, 31, 31);
            titleBar.InactiveForegroundColor = Colors.Gray;
            titleBar.InactiveBackgroundColor = Color.FromArgb(255, 31, 31, 31);

            titleBar.ButtonForegroundColor = Colors.White;
            titleBar.ButtonBackgroundColor = Color.FromArgb(255, 31, 31, 31);
            titleBar.ButtonInactiveForegroundColor = Colors.Gray;
            titleBar.ButtonInactiveBackgroundColor = Color.FromArgb(255, 31, 31, 31);

            titleBar.ButtonHoverForegroundColor = Colors.White;
            titleBar.ButtonHoverBackgroundColor = Color.FromArgb(255, 51, 51, 51);
            titleBar.ButtonPressedForegroundColor = Colors.White;
            titleBar.ButtonPressedBackgroundColor = Colors.Gray;
        }

        // 아이콘 배경 색이 적용 안되는 버그 수정
        titleBar.IconShowOptions = IconShowOptions.HideIconAndSystemMenu;
        titleBar.IconShowOptions = IconShowOptions.ShowIconAndSystemMenu;
    }

그리고 윈도의 테마 색이 변경될 때를 감지하기 위해 이벤트 구독을 합니다.

    public MainWindow()
    {
        this.InitializeComponent();

        var content = (Content as FrameworkElement)!;
        content.ActualThemeChanged += (s, e) =>
        {
            ModifyTitlebarTheme();
        };
        ModifyTitlebarTheme();
    }

동작 화면

| 윈도 테마 색이 밝게일 경우

image.png

| 윈도 테마 색이 어둡게일 경우

image.png

정리

WinUI 3에서 타이틀바에 테마를 적용하는 방법을 알아보았습니다.

WinUI 3이 어느덧 버젼 1.0 미리보기 2 단계입니다. 그 사이 UWP 미지원 및 UWP와 WPF에 비해서 성 능이 현저히 떨어지는 등의 이슈가 있었지만 조금씩 안정화 되어가고 있는 모습입니다.

우리가 Windows라는 운영체제를 사용하는 이상 여전히 윈도 어플리케이션 개발을 위해 새로운 환경에 익숙해질 필요가 있습니다. 물론 여전히 윈폼이나 WPF로도 거뜬히 윈도 어플리케이션을 만들 수는 있지만 새로운 윈도의 지원을 활용하기엔 제약이 따르죠. WPF개발자 이면서 UWP의 폐쇄적인 여러 정책 때문에 UWP에 접근하지 못했던 분들은 이번에 WinUI 3를 체험해 볼 만합니다. WinUI 3도 XAML 기반이므로 WPF에서 사용하고 있는 여럿 경험을 공유하거든요. 자! 자신의 앱을 WinUI 3으로 개발 해봅시다!

More from this blog

개발, 테스트, 운영에서의 도커 활용

핵심 원칙: "한 번 빌드하고, 어디서든 실행한다 (Build once, run anywhere)" 도커의 가장 큰 장점은 환경 일관성입니다. 동일한 도커 이미지를 사용하여 개발, 테스트, 운영 환경을 구성함으로써 "제 PC에서는 됐는데..." 하는 문제를 최소화할 수 있습니다. 1. 개발 단계 (Development) 목표: 빠른 코드 변경 반영, 쉬운 디버깅, 실제 운영 환경과 유사한 환경 구성. Docker 사용 방안: Dockerf...

May 9, 20256 min read15

[EF Core] 데이터 삭제 시 소프트 삭제 적용

DB에서 데이터를 삭제하면 일반적으로 복구할 수 없습니다. 또한 관계에 따라 영구 삭제 자체가 어려울 수도 있습니다. 그래서 데이터를 영구 삭제하는 대신 IsDeleted 속성을 true로 주고 IsDeleted 속성을 필터링해서 조회하는 방법을 사용하기도 합니다. 이를 소프트 삭제라고 합니다. 그런데 EF에서 알아서 데이터 삭제 시 소프트 삭제를 하고 쿼리시 IsDeleted 속성을 체크해서 삭제한 데이터를 제외한 데이터만 쿼리하게 하는 ...

Mar 18, 20243 min read19

[EF Core] ValueConverter를 이용해서 엔터티 속성의 도메인 관리

EF Core를 사용하면서 문자열 길이 등의 특성을 일일이 지정하는 것은 번거롭습니다. ... [MaxLength(32)] public string? 제목 { get; set; } 엔터티가 한 개일 때는 상관이 없으나 제목 유형이 여러 엔터티에 사용될 경우 유형을 지정하기 번거롭습니다. 속성 유형을 도메인으로 관리하면 참 편할텐데요, ValueConverter를 이용할 수 있습니다. 그런데 이것을 인터페이스 정적 추상를 사용해서 다음처럼 ...

Mar 16, 20242 min read8

디모이 블로그

154 posts

.NET 관련 기술을 선호하고 새로운 언어를 배우는데 관심이 있습니다.