Skip to content

Commit da54c4e

Browse files
committed
Titlebar improvements
1 parent c33f01a commit da54c4e

File tree

10 files changed

+450
-211
lines changed

10 files changed

+450
-211
lines changed

CommunityToolkit.App.Shared/App.xaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,6 @@
2323

2424
<SolidColorBrush x:Key="BackgroundColorBrush"
2525
Color="{ThemeResource SolidBackgroundFillColorBase}" />
26-
<SolidColorBrush x:Key="WindowCaptionBackground"
27-
Color="Transparent" />
28-
<SolidColorBrush x:Key="WindowCaptionBackgroundDisabled"
29-
Color="Transparent" />
3026

3127
<!-- PathData for the GitHub icon -->
3228
<x:String x:Key="GithubIcon">M21.7999992370605,0L19.220495223999,0.26007080078125 16.81787109375,1.00595712661743 14.6436157226563,2.18616962432861 12.7492189407349,3.74921894073486 11.1861696243286,5.64361572265625 10.0059566497803,7.81787109375 9.26007080078125,10.2204961776733 9,12.8000001907349 9.65248012542725,16.8459720611572 11.4694375991821,20.3591785430908 14.2401514053345,23.1291217803955 17.7539005279541,24.9453010559082 18.4305686950684,24.8080005645752 18.6273498535156,24.3296756744385 18.6207065582275,23.4247951507568 18.609375,21.9468746185303 16.4340572357178,22.0373229980469 15.1187467575073,21.4822216033936 14.4708204269409,20.7821025848389 14.2976503372192,20.4375 13.8297338485718,19.5214366912842 13.3685493469238,18.947265625 12.8765497207642,18.5656261444092 12.3995819091797,18.1091804504395 12.4844465255737,17.87890625 12.7874250411987,17.7974605560303 12.9647998809814,17.7875003814697 13.8134965896606,18.0311241149902 14.4276065826416,18.4802703857422 14.8007507324219,18.9127178192139 14.926549911499,19.1062507629395 15.8880548477173,20.1437015533447 16.9443283081055,20.494140625 17.9229640960693,20.416259765625 18.6515502929688,20.1687507629395 18.9645938873291,19.1242198944092 19.4640502929688,18.4593753814697 17.3543262481689,18.0241260528564 15.4833002090454,17.014066696167 14.1450357437134,15.1450166702271 13.6336002349854,12.1328001022339 13.9853601455688,10.2268438339233 14.9500007629395,8.69764995574951 14.7027282714844,7.54188776016235 14.7441072463989,6.53565359115601 15.0765495300293,5.30859994888306 15.2825078964233,5.28076791763306 15.9191312789917,5.34375619888306 17.0145378112793,5.71729135513306 18.596851348877,6.62109994888306 21.799976348877,6.19062519073486 25.004674911499,6.62265014648438 26.5845413208008,5.71818733215332 27.6791000366211,5.34472513198853 28.315746307373,5.28210020065308 28.5218753814697,5.31015014648438 28.8556652069092,6.53784370422363 28.8976573944092,7.5438346862793 28.6499996185303,8.69764995574951 29.6154251098633,10.2268533706665 29.9656257629395,12.1328001022339 29.453296661377,15.1497011184692 28.1123065948486,17.0164012908936 26.2366523742676,18.020601272583 24.120325088501,18.4500007629395 24.7275562286377,19.3355484008789 24.9890747070313,20.8187503814697 24.9804744720459,23.0584030151367 24.9718742370605,24.3312511444092 25.1693305969238,24.8128852844238 25.8531246185303,24.9453010559082 29.3641395568848,23.1273632049561 32.1326217651367,20.3568344116211 33.948070526123,16.8442134857178 34.5999984741211,12.8000001907349 34.3399276733398,10.2204961776733 33.5940399169922,7.81787109375 32.4138298034668,5.64361572265625 30.8507804870605,3.74921894073486 28.9563827514648,2.18616962432861 26.7821273803711,1.00595712661743 24.3795032501221,0.26007080078125 21.7999992370605,0z</x:String>

CommunityToolkit.App.Shared/App.xaml.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ protected override void OnLaunched(LaunchActivatedEventArgs e)
5252
#endif
5353
rootFrame.Navigate(typeof(AppLoadingView), e.Arguments);
5454

55-
SetTitleBar();
5655
// Ensure the current window is active
5756
currentWindow.Activate();
5857
}
@@ -66,13 +65,4 @@ void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
6665
{
6766
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
6867
}
69-
70-
private void SetTitleBar()
71-
{
72-
#if WINDOWS_UWP
73-
var viewTitleBar = Windows.UI.ViewManagement.ApplicationView.GetForCurrentView().TitleBar;
74-
viewTitleBar.ButtonBackgroundColor = Windows.UI.Colors.Transparent;
75-
viewTitleBar.ButtonInactiveBackgroundColor = Windows.UI.Colors.Transparent;
76-
#endif
77-
}
7868
}

CommunityToolkit.App.Shared/CommunityToolkit.App.Shared.projitems

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
<Compile Include="$(MSBuildThisFileDirectory)Attributes\PackageProjectUrlAttribute.cs" />
2525
<Compile Include="$(MSBuildThisFileDirectory)Behaviors\NavigateToUriAction.cs" />
2626
<Compile Include="$(MSBuildThisFileDirectory)Controls\TitleBar\TitleBar.cs" />
27+
<Compile Include="$(MSBuildThisFileDirectory)Controls\TitleBar\TitleBar.UWP.cs" />
28+
<Compile Include="$(MSBuildThisFileDirectory)Controls\TitleBar\TitleBar.WASDK.cs" />
2729
<Compile Include="$(MSBuildThisFileDirectory)Controls\TitleBar\TitleBar.Properties.cs" />
2830
<Compile Include="$(MSBuildThisFileDirectory)Converters\StringToUriConverter.cs" />
2931
<Compile Include="$(MSBuildThisFileDirectory)DocOrSampleTemplateSelector.cs" />

CommunityToolkit.App.Shared/Controls/TitleBar/TitleBar.Properties.cs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,13 @@ public partial class TitleBar : Control
2020

2121
public static readonly DependencyProperty IsPaneButtonVisibleProperty = DependencyProperty.Register(nameof(IsPaneButtonVisible), typeof(bool), typeof(TitleBar), new PropertyMetadata(false, IsPaneButtonVisibleChanged));
2222

23-
23+
public static readonly DependencyProperty ConfigureTitleBarProperty = DependencyProperty.Register(nameof(ConfigureTitleBar), typeof(bool), typeof(TitleBar), new PropertyMetadata(true, ConfigureTitleBarChanged));
24+
25+
public static readonly DependencyProperty DisplayModeProperty = DependencyProperty.Register(nameof(DisplayMode), typeof(DisplayMode), typeof(TitleBar), new PropertyMetadata(DisplayMode.Standard, DisplayModeChanged));
26+
27+
#if WINAPPSDK
28+
public static readonly DependencyProperty WindowProperty = DependencyProperty.Register(nameof(Window), typeof(Window), typeof(TitleBar), new PropertyMetadata(null));
29+
#endif
2430

2531
public ImageSource Icon
2632
{
@@ -52,6 +58,12 @@ public object Footer
5258
set => SetValue(FooterProperty, value);
5359
}
5460

61+
public DisplayMode DisplayMode
62+
{
63+
get => (DisplayMode)GetValue(DisplayModeProperty);
64+
set => SetValue(DisplayModeProperty, value);
65+
}
66+
5567
public bool IsBackButtonVisible
5668
{
5769
get => (bool)GetValue(IsBackButtonVisibleProperty);
@@ -64,12 +76,43 @@ public bool IsPaneButtonVisible
6476
set => SetValue(IsPaneButtonVisibleProperty, value);
6577
}
6678

79+
public bool ConfigureTitleBar
80+
{
81+
get => (bool)GetValue(ConfigureTitleBarProperty);
82+
set => SetValue(ConfigureTitleBarProperty, value);
83+
}
84+
85+
#if WINAPPSDK
86+
public Window Window
87+
{
88+
get => (Window)GetValue(WindowProperty);
89+
set => SetValue(WindowProperty, value);
90+
}
91+
#endif
92+
6793
private static void IsBackButtonVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
6894
{
6995
((TitleBar)d).Update();
7096
}
97+
7198
private static void IsPaneButtonVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
7299
{
73100
((TitleBar)d).Update();
74101
}
102+
103+
private static void ConfigureTitleBarChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
104+
{
105+
((TitleBar)d).SetTitleBar();
106+
}
107+
108+
private static void DisplayModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
109+
{
110+
((TitleBar)d).SetTitleBar();
111+
}
112+
}
113+
114+
public enum DisplayMode
115+
{
116+
Standard,
117+
Tall
75118
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#if WINDOWS_UWP && !HAS_UNO
6+
using Windows.ApplicationModel.Core;
7+
using Windows.UI.ViewManagement;
8+
using Windows.UI;
9+
10+
namespace CommunityToolkit.App.Shared.Controls;
11+
12+
public partial class TitleBar : Control
13+
{
14+
private void SetUWPTitleBar()
15+
{
16+
if (ConfigureTitleBar)
17+
{
18+
CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = true;
19+
CoreApplication.GetCurrentView().TitleBar.LayoutMetricsChanged -= this.TitleBar_LayoutMetricsChanged;
20+
CoreApplication.GetCurrentView().TitleBar.LayoutMetricsChanged += this.TitleBar_LayoutMetricsChanged;
21+
Window.Current.Activated -= this.Current_Activated;
22+
Window.Current.Activated += this.Current_Activated;
23+
Window.Current.SetTitleBar(_dragRegion);
24+
ApplicationView.GetForCurrentView().TitleBar.ButtonBackgroundColor = Colors.Transparent;
25+
ApplicationView.GetForCurrentView().TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
26+
}
27+
else
28+
{
29+
CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = false;
30+
Window.Current.Activated -= this.Current_Activated;
31+
CoreApplication.GetCurrentView().TitleBar.LayoutMetricsChanged -= this.TitleBar_LayoutMetricsChanged;
32+
Window.Current.SetTitleBar(null);
33+
}
34+
}
35+
36+
private void Current_Activated(object sender, Windows.UI.Core.WindowActivatedEventArgs e)
37+
{
38+
if (e.WindowActivationState == Windows.UI.Core.CoreWindowActivationState.Deactivated)
39+
{
40+
VisualStateManager.GoToState(this, WindowDeactivatedState, true);
41+
}
42+
else
43+
{
44+
VisualStateManager.GoToState(this, WindowActivatedState, true);
45+
}
46+
}
47+
48+
private void TitleBar_LayoutMetricsChanged(CoreApplicationViewTitleBar sender, object args)
49+
{
50+
if (_titleBar != null)
51+
{
52+
ColumnDefinition Left = (ColumnDefinition)_titleBar.GetTemplateChild(PartLeftPaddingColumn);
53+
ColumnDefinition Right = (ColumnDefinition)_titleBar.GetTemplateChild(PartRightPaddingColumn);
54+
Left.Width = new GridLength(CoreApplication.GetCurrentView().TitleBar.SystemOverlayLeftInset);
55+
Right.Width = new GridLength(CoreApplication.GetCurrentView().TitleBar.SystemOverlayRightInset);
56+
}
57+
}
58+
}
59+
#endif
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#if WINAPPSDK
6+
using Microsoft.UI;
7+
using Microsoft.UI.Windowing;
8+
using Microsoft.UI.Xaml;
9+
using Microsoft.UI.Xaml.Controls;
10+
using System;
11+
using System.Collections.Generic;
12+
using System.Runtime.InteropServices;
13+
using WinRT.Interop;
14+
15+
namespace CommunityToolkit.App.Shared.Controls;
16+
17+
public partial class TitleBar : Control
18+
{
19+
private AppWindow? appWindow;
20+
ColumnDefinition? LeftPaddingColumn;
21+
ColumnDefinition? BackButtonColumn;
22+
ColumnDefinition? MenuButtonColumn;
23+
ColumnDefinition? IconColumn;
24+
ColumnDefinition? TitleColumn;
25+
ColumnDefinition? SubtitleColumn;
26+
ColumnDefinition? LeftDragColumn;
27+
ColumnDefinition? ContentColumn;
28+
ColumnDefinition? FooterColumn;
29+
ColumnDefinition? RightDragColumn;
30+
ColumnDefinition? RightPaddingColumn;
31+
32+
private void SetWASDKTitleBar()
33+
{
34+
appWindow = GetAppWindowForCurrentWindow();
35+
if (ConfigureTitleBar)
36+
{
37+
appWindow.TitleBar.ExtendsContentIntoTitleBar = true;
38+
this.Loaded -= AppTitleBar_Loaded;
39+
this.SizeChanged -= AppTitleBar_SizeChanged;
40+
41+
this.Loaded += AppTitleBar_Loaded;
42+
this.SizeChanged += AppTitleBar_SizeChanged;
43+
44+
Window.Activated -= Window_Activated;
45+
Window.Activated += Window_Activated;
46+
47+
appWindow.TitleBar.ButtonBackgroundColor = Colors.Transparent;
48+
appWindow.TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
49+
50+
// Set the width of padding columns in the UI.
51+
LeftPaddingColumn = (ColumnDefinition)_titleBar!.GetTemplateChild(PartLeftPaddingColumn);
52+
53+
BackButtonColumn = (ColumnDefinition)_titleBar!.GetTemplateChild("BackButtonColumn");
54+
MenuButtonColumn = (ColumnDefinition)_titleBar!.GetTemplateChild("MenuButtonColumn");
55+
IconColumn = (ColumnDefinition)_titleBar!.GetTemplateChild("IconColumn");
56+
TitleColumn = (ColumnDefinition)_titleBar!.GetTemplateChild("TitleColumn");
57+
SubtitleColumn = (ColumnDefinition)_titleBar!.GetTemplateChild("SubtitleColumn");
58+
LeftDragColumn = (ColumnDefinition)_titleBar!.GetTemplateChild("LeftDragColumn");
59+
ContentColumn = (ColumnDefinition)_titleBar!.GetTemplateChild("ContentColumn");
60+
FooterColumn = (ColumnDefinition)_titleBar!.GetTemplateChild("FooterColumn");
61+
RightDragColumn = (ColumnDefinition)_titleBar!.GetTemplateChild("RightDragColumn");
62+
RightPaddingColumn = (ColumnDefinition)_titleBar!.GetTemplateChild(PartRightPaddingColumn);
63+
64+
// Get caption button occlusion information.
65+
int CaptionButtonOcclusionWidthRight = appWindow.TitleBar.RightInset;
66+
int CaptionButtonOcclusionWidthLeft = appWindow.TitleBar.LeftInset;
67+
LeftPaddingColumn.Width = new GridLength(CaptionButtonOcclusionWidthLeft);
68+
RightPaddingColumn.Width = new GridLength(CaptionButtonOcclusionWidthRight);
69+
70+
// A taller title bar is only supported when drawing a fully custom title bar
71+
if (AppWindowTitleBar.IsCustomizationSupported() && appWindow.TitleBar.ExtendsContentIntoTitleBar)
72+
{
73+
if (DisplayMode == DisplayMode.Tall)
74+
{
75+
// Choose a tall title bar to provide more room for interactive elements
76+
// like search box or person picture controls.
77+
appWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Tall;
78+
}
79+
else
80+
{
81+
appWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Standard;
82+
}
83+
// Recalculate the drag region for the custom title bar
84+
// if you explicitly defined new draggable areas.
85+
SetDragRegionForCustomTitleBar(appWindow);
86+
}
87+
}
88+
else
89+
{
90+
appWindow.TitleBar.ResetToDefault();
91+
}
92+
}
93+
94+
private void AppTitleBar_Loaded(object sender, RoutedEventArgs e)
95+
{
96+
// Check to see if customization is supported.
97+
// The method returns true on Windows 10 since Windows App SDK 1.2, and on all versions of
98+
// Windows App SDK on Windows 11.
99+
if (AppWindowTitleBar.IsCustomizationSupported())
100+
{
101+
SetDragRegionForCustomTitleBar(appWindow!);
102+
}
103+
}
104+
105+
private void AppTitleBar_SizeChanged(object sender, SizeChangedEventArgs e)
106+
{
107+
// Check to see if customization is supported.
108+
// The method returns true on Windows 10 since Windows App SDK 1.2, and on all versions of
109+
// Windows App SDK on Windows 11.
110+
if (AppWindowTitleBar.IsCustomizationSupported() && appWindow!.TitleBar.ExtendsContentIntoTitleBar)
111+
{
112+
// Update drag region if the size of the title bar changes.
113+
SetDragRegionForCustomTitleBar(appWindow);
114+
}
115+
}
116+
117+
private void Window_Activated(object sender, WindowActivatedEventArgs args)
118+
{
119+
if (args.WindowActivationState == WindowActivationState.Deactivated)
120+
{
121+
VisualStateManager.GoToState(this, WindowDeactivatedState, true);
122+
}
123+
else
124+
{
125+
VisualStateManager.GoToState(this, WindowActivatedState, true);
126+
}
127+
}
128+
129+
private void SetDragRegionForCustomTitleBar(AppWindow appWindow)
130+
{
131+
// Check to see if customization is supported.
132+
// The method returns true on Windows 10 since Windows App SDK 1.2, and on all versions of
133+
// Windows App SDK on Windows 11.
134+
if (AppWindowTitleBar.IsCustomizationSupported() && appWindow.TitleBar.ExtendsContentIntoTitleBar)
135+
{
136+
double scaleAdjustment = GetScaleAdjustment();
137+
138+
RightPaddingColumn!.Width = new GridLength(appWindow.TitleBar.RightInset / scaleAdjustment);
139+
LeftPaddingColumn!.Width = new GridLength(appWindow.TitleBar.LeftInset / scaleAdjustment);
140+
141+
List<Windows.Graphics.RectInt32> dragRectsList = new();
142+
143+
Windows.Graphics.RectInt32 dragRectL;
144+
dragRectL.X = (int)((LeftPaddingColumn.ActualWidth + BackButtonColumn!.ActualWidth + MenuButtonColumn!.ActualWidth) * scaleAdjustment);
145+
dragRectL.Y = 0;
146+
dragRectL.Height = (int)(this.ActualHeight * scaleAdjustment);
147+
dragRectL.Width = (int)((IconColumn!.ActualWidth
148+
+ TitleColumn!.ActualWidth
149+
+ SubtitleColumn!.ActualWidth
150+
+ LeftDragColumn!.ActualWidth) * scaleAdjustment);
151+
dragRectsList.Add(dragRectL);
152+
153+
Windows.Graphics.RectInt32 dragRectR;
154+
dragRectR.X = (int)((LeftPaddingColumn.ActualWidth + IconColumn.ActualWidth + BackButtonColumn!.ActualWidth + MenuButtonColumn!.ActualWidth
155+
+ ((TextBlock)_titleBar!.GetTemplateChild("PART_TitleText")).ActualWidth
156+
+ ((TextBlock)_titleBar!.GetTemplateChild("PART_SubTitleText")).ActualWidth
157+
+ LeftDragColumn.ActualWidth
158+
+ ContentColumn!.ActualWidth) * scaleAdjustment);
159+
dragRectR.Y = 0;
160+
dragRectR.Height = (int)(this.ActualHeight * scaleAdjustment);
161+
dragRectR.Width = (int)(RightDragColumn!.ActualWidth * scaleAdjustment);
162+
dragRectsList.Add(dragRectR);
163+
164+
Windows.Graphics.RectInt32[] dragRects = dragRectsList.ToArray();
165+
166+
appWindow.TitleBar.SetDragRectangles(dragRects);
167+
}
168+
}
169+
170+
171+
private AppWindow GetAppWindowForCurrentWindow()
172+
{
173+
IntPtr hWnd = WindowNative.GetWindowHandle(Window);
174+
WindowId wndId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hWnd);
175+
return AppWindow.GetFromWindowId(wndId);
176+
}
177+
178+
[DllImport("Shcore.dll", SetLastError = true)]
179+
internal static extern int GetDpiForMonitor(IntPtr hmonitor, Monitor_DPI_Type dpiType, out uint dpiX, out uint dpiY);
180+
181+
internal enum Monitor_DPI_Type : int
182+
{
183+
MDT_Effective_DPI = 0,
184+
MDT_Angular_DPI = 1,
185+
MDT_Raw_DPI = 2,
186+
MDT_Default = MDT_Effective_DPI
187+
}
188+
189+
private double GetScaleAdjustment()
190+
{
191+
IntPtr hWnd = WindowNative.GetWindowHandle(this.Window);
192+
WindowId wndId = Win32Interop.GetWindowIdFromWindow(hWnd);
193+
DisplayArea displayArea = DisplayArea.GetFromWindowId(wndId, DisplayAreaFallback.Primary);
194+
IntPtr hMonitor = Win32Interop.GetMonitorFromDisplayId(displayArea.DisplayId);
195+
196+
// Get DPI.
197+
int result = GetDpiForMonitor(hMonitor, Monitor_DPI_Type.MDT_Default, out uint dpiX, out uint _);
198+
if (result != 0)
199+
{
200+
throw new Exception("Could not get DPI for monitor.");
201+
}
202+
203+
uint scaleFactorPercent = (uint)(((long)dpiX * 100 + (96 >> 1)) / 96);
204+
return scaleFactorPercent / 100.0;
205+
}
206+
}
207+
#endif

0 commit comments

Comments
 (0)