diff --git a/components/TitleBar/OpenSolution.bat b/components/TitleBar/OpenSolution.bat
new file mode 100644
index 000000000..814a56d4b
--- /dev/null
+++ b/components/TitleBar/OpenSolution.bat
@@ -0,0 +1,3 @@
+@ECHO OFF
+
+powershell ..\..\tooling\ProjectHeads\GenerateSingleSampleHeads.ps1 -componentPath %CD% %*
\ No newline at end of file
diff --git a/components/TitleBar/samples/Assets/AppTitleBarIcon.png b/components/TitleBar/samples/Assets/AppTitleBarIcon.png
new file mode 100644
index 000000000..9b7414291
Binary files /dev/null and b/components/TitleBar/samples/Assets/AppTitleBarIcon.png differ
diff --git a/components/TitleBar/samples/Assets/Avatar.png b/components/TitleBar/samples/Assets/Avatar.png
new file mode 100644
index 000000000..5fd88928d
Binary files /dev/null and b/components/TitleBar/samples/Assets/Avatar.png differ
diff --git a/components/TitleBar/samples/Assets/icon.png b/components/TitleBar/samples/Assets/icon.png
new file mode 100644
index 000000000..5f574ceca
Binary files /dev/null and b/components/TitleBar/samples/Assets/icon.png differ
diff --git a/components/TitleBar/samples/Dependencies.props b/components/TitleBar/samples/Dependencies.props
new file mode 100644
index 000000000..e622e1df4
--- /dev/null
+++ b/components/TitleBar/samples/Dependencies.props
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/TitleBar/samples/SamplePages/FirstPage.xaml b/components/TitleBar/samples/SamplePages/FirstPage.xaml
new file mode 100644
index 000000000..135a5b9bd
--- /dev/null
+++ b/components/TitleBar/samples/SamplePages/FirstPage.xaml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/components/TitleBar/samples/SamplePages/FirstPage.xaml.cs b/components/TitleBar/samples/SamplePages/FirstPage.xaml.cs
new file mode 100644
index 000000000..c2fb661be
--- /dev/null
+++ b/components/TitleBar/samples/SamplePages/FirstPage.xaml.cs
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace TitleBarExperiment.Samples;
+///
+/// An empty page that can be used on its own or navigated to within a Frame.
+///
+public sealed partial class FirstPage : Page
+{
+ public FirstPage()
+ {
+ this.InitializeComponent();
+ }
+}
diff --git a/components/TitleBar/samples/SamplePages/SecondPage.xaml b/components/TitleBar/samples/SamplePages/SecondPage.xaml
new file mode 100644
index 000000000..5be84e0cf
--- /dev/null
+++ b/components/TitleBar/samples/SamplePages/SecondPage.xaml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/components/TitleBar/samples/SamplePages/SecondPage.xaml.cs b/components/TitleBar/samples/SamplePages/SecondPage.xaml.cs
new file mode 100644
index 000000000..917c00f9b
--- /dev/null
+++ b/components/TitleBar/samples/SamplePages/SecondPage.xaml.cs
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace TitleBarExperiment.Samples;
+///
+/// An empty page that can be used on its own or navigated to within a Frame.
+///
+public sealed partial class SecondPage : Page
+{
+ public SecondPage()
+ {
+ this.InitializeComponent();
+ }
+}
diff --git a/components/TitleBar/samples/SamplePages/ShellPage.xaml b/components/TitleBar/samples/SamplePages/ShellPage.xaml
new file mode 100644
index 000000000..4ce0d4bb3
--- /dev/null
+++ b/components/TitleBar/samples/SamplePages/ShellPage.xaml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/TitleBar/samples/SamplePages/ShellPage.xaml.cs b/components/TitleBar/samples/SamplePages/ShellPage.xaml.cs
new file mode 100644
index 000000000..56d609414
--- /dev/null
+++ b/components/TitleBar/samples/SamplePages/ShellPage.xaml.cs
@@ -0,0 +1,56 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace TitleBarExperiment.Samples;
+///
+/// An empty page that can be used on its own or navigated to within a Frame.
+///
+public sealed partial class ShellPage : Page
+{
+#if WINAPPSDK
+ public ShellPage(Window window)
+ {
+ this.InitializeComponent();
+ appTitleBar.Window = window;
+ }
+#else
+ public ShellPage()
+ {
+ this.InitializeComponent();
+ Microsoft.UI.Xaml.Controls.BackdropMaterial.SetApplyToRootOrPageBackground(this, true);
+ }
+#endif
+ private void appTitleBar_BackButtonClick(object sender, RoutedEventArgs e)
+ {
+ if (NavFrame.CanGoBack)
+ {
+ NavFrame.GoBack();
+ }
+ }
+
+ private void appTitleBar_PaneButtonClick(object sender, RoutedEventArgs e)
+ {
+ NavView.IsPaneOpen = !NavView.IsPaneOpen;
+ }
+
+ private void NavView_SelectionChanged(MUXC.NavigationView sender, MUXC.NavigationViewSelectionChangedEventArgs args)
+ {
+ switch ((string)((MUXC.NavigationViewItem)NavView.SelectedItem).Tag)
+ {
+ case "1": NavFrame.Navigate(typeof(FirstPage)); break;
+ case "2": NavFrame.Navigate(typeof(SecondPage)); break;
+ }
+
+ }
+
+ private void NavView_Loaded(object sender, RoutedEventArgs e)
+ {
+ NavView.SelectedItem = NavView.MenuItems[0];
+ }
+
+ private void NavFrame_Navigated(object sender, NavigationEventArgs e)
+ {
+ appTitleBar.IsBackButtonVisible = NavFrame.CanGoBack;
+ }
+}
diff --git a/components/TitleBar/samples/TitleBar.Samples.csproj b/components/TitleBar/samples/TitleBar.Samples.csproj
new file mode 100644
index 000000000..6d58d58db
--- /dev/null
+++ b/components/TitleBar/samples/TitleBar.Samples.csproj
@@ -0,0 +1,25 @@
+
+
+ TitleBar
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
diff --git a/components/TitleBar/samples/TitleBar.md b/components/TitleBar/samples/TitleBar.md
new file mode 100644
index 000000000..b7ef40dd6
--- /dev/null
+++ b/components/TitleBar/samples/TitleBar.md
@@ -0,0 +1,28 @@
+---
+title: TitleBar
+author: niels9001
+description: A control that provides a modern TitleBar
+keywords: TitleBar, Control, Layout
+dev_langs:
+ - csharp
+category: Controls
+subcategory: Layout
+discussion-id: 0
+issue-id: 0
+icon: assets/icon.png
+---
+
+The `TitleBar` provides an easy way to display a modern titlebar experience. The control handles all the required APIs to extend content into the titlebar area and set custom drag regions. The control is set up in a way that it handles the correct design guidelines while being flexible in what content to show.
+
+> [!Sample TitleBarConfigSample]
+
+> [!Sample TitleBarFullSample]
+
+## Using TitleBar in WASDK apps
+If `AutoConfigureCustomTitleBar` is enabled, the `TitleBar` will make the required code-behind changes to make your content extend into the titlebar area and setting the correct caption background brushes. However, launching the app might briefly show the standard titlebar. To overcome this, make sure you set the following code in the `OnLaunched` method (in `App.xaml.cs`) or wherever you create your window:
+
+```CS
+currentWindow.AppWindow.TitleBar.ExtendsContentIntoTitleBar = true;
+currentWindow.AppWindow.TitleBar.ButtonBackgroundColor = Microsoft.UI.Colors.Transparent;
+`
+
diff --git a/components/TitleBar/samples/TitleBarConfigSample.xaml b/components/TitleBar/samples/TitleBarConfigSample.xaml
new file mode 100644
index 000000000..49736cb03
--- /dev/null
+++ b/components/TitleBar/samples/TitleBarConfigSample.xaml
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/TitleBar/samples/TitleBarConfigSample.xaml.cs b/components/TitleBar/samples/TitleBarConfigSample.xaml.cs
new file mode 100644
index 000000000..645efe4eb
--- /dev/null
+++ b/components/TitleBar/samples/TitleBarConfigSample.xaml.cs
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using CommunityToolkit.WinUI.Controls;
+
+namespace TitleBarExperiment.Samples;
+
+///
+/// An example sample page of a custom control inheriting from Panel.
+///
+[ToolkitSampleTextOption("TitleText", "Contoso", Title = "Title")]
+[ToolkitSampleTextOption("SubtitleText", "Preview", Title = "Subtitle")]
+[ToolkitSampleBoolOption("ShowBackButtonSetting", false, Title = "ShowBackButton")]
+[ToolkitSampleBoolOption("ShowPaneButtonSetting", false, Title = "ShowPaneButton")]
+
+[ToolkitSample(id: nameof(TitleBarConfigSample), "Full titlebar sample", description: $"A sample for showing how to create and use a {nameof(TitleBar)} in a window.")]
+public sealed partial class TitleBarConfigSample : Page
+{
+ public TitleBarConfigSample()
+ {
+ this.InitializeComponent();
+ }
+
+
+}
diff --git a/components/TitleBar/samples/TitleBarFullSample.xaml b/components/TitleBar/samples/TitleBarFullSample.xaml
new file mode 100644
index 000000000..bec6c3a0e
--- /dev/null
+++ b/components/TitleBar/samples/TitleBarFullSample.xaml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
diff --git a/components/TitleBar/samples/TitleBarFullSample.xaml.cs b/components/TitleBar/samples/TitleBarFullSample.xaml.cs
new file mode 100644
index 000000000..8bede4933
--- /dev/null
+++ b/components/TitleBar/samples/TitleBarFullSample.xaml.cs
@@ -0,0 +1,51 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using CommunityToolkit.WinUI.Controls;
+#if WINDOWS_UWP
+using Windows.ApplicationModel.Core;
+using Windows.UI.Core;
+using Windows.UI.ViewManagement;
+#endif
+namespace TitleBarExperiment.Samples;
+
+[ToolkitSample(id: nameof(TitleBarFullSample), "Full titlebar sample", description: $"A sample for showing how to create and use a {nameof(TitleBar)} in a window.")]
+public sealed partial class TitleBarFullSample : Page
+{
+ public TitleBarFullSample()
+ {
+ this.InitializeComponent();
+ }
+
+
+#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
+ private async void LaunchSample_Click(object sender, RoutedEventArgs e)
+#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
+ {
+#if WINDOWS_UWP
+ CoreApplicationView newView = CoreApplication.CreateNewView();
+ int newViewId = 0;
+ await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
+ {
+ Frame frame = new Frame();
+ frame.Navigate(typeof(ShellPage), null);
+ Window.Current.Content = frame;
+ // You have to activate the window in order to show it later.
+ Window.Current.Activate();
+
+ newViewId = ApplicationView.GetForCurrentView().Id;
+ });
+
+ bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newViewId);
+#endif
+#if WINAPPSDK
+ Window newWindow = new Window
+ {
+ SystemBackdrop = new MicaBackdrop()
+ };
+ newWindow.Content = new ShellPage(newWindow);
+ newWindow.Activate();
+#endif
+ }
+}
diff --git a/components/TitleBar/src/AdditionalAssemblyInfo.cs b/components/TitleBar/src/AdditionalAssemblyInfo.cs
new file mode 100644
index 000000000..8f864b0fd
--- /dev/null
+++ b/components/TitleBar/src/AdditionalAssemblyInfo.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.CompilerServices;
+
+// These `InternalsVisibleTo` calls are intended to make it easier for
+// for any internal code to be testable in all the different test projects
+// used with the Labs infrastructure.
+[assembly: InternalsVisibleTo("TitleBar.Tests.Uwp")]
+[assembly: InternalsVisibleTo("TitleBar.Tests.WinAppSdk")]
+[assembly: InternalsVisibleTo("CommunityToolkit.Tests.Uwp")]
+[assembly: InternalsVisibleTo("CommunityToolkit.Tests.WinAppSdk")]
diff --git a/components/TitleBar/src/CommunityToolkit.WinUI.Controls.TitleBar.csproj b/components/TitleBar/src/CommunityToolkit.WinUI.Controls.TitleBar.csproj
new file mode 100644
index 000000000..7bf974a32
--- /dev/null
+++ b/components/TitleBar/src/CommunityToolkit.WinUI.Controls.TitleBar.csproj
@@ -0,0 +1,13 @@
+
+
+ TitleBar
+ This package contains TitleBar.
+ 0.0.1
+
+
+ CommunityToolkit.WinUI.Controls.TitleBarRns
+
+
+
+
+
diff --git a/components/TitleBar/src/Dependencies.props b/components/TitleBar/src/Dependencies.props
new file mode 100644
index 000000000..e622e1df4
--- /dev/null
+++ b/components/TitleBar/src/Dependencies.props
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/TitleBar/src/InfoHelper.cs b/components/TitleBar/src/InfoHelper.cs
new file mode 100644
index 000000000..6dc80ca8b
--- /dev/null
+++ b/components/TitleBar/src/InfoHelper.cs
@@ -0,0 +1,36 @@
+#if WINAPPSDK
+using Windows.ApplicationModel;
+using Windows.Storage;
+using Windows.System.Profile;
+
+namespace CommunityToolkit.WinUI.Controls;
+internal static class InfoHelper
+{
+ public static Version AppVersion { get; } = new Version(
+ Package.Current.Id.Version.Major,
+ Package.Current.Id.Version.Minor,
+ Package.Current.Id.Version.Build,
+ Package.Current.Id.Version.Revision
+ );
+
+ public static Version SystemVersion { get; }
+
+ public static SystemDataPaths SystemDataPath { get; } = SystemDataPaths.GetDefault();
+
+ public static UserDataPaths UserDataPath { get; } = UserDataPaths.GetDefault();
+
+ public static string AppInstalledLocation { get; } = Package.Current.InstalledLocation.Path;
+
+ static InfoHelper()
+ {
+ string systemVersion = AnalyticsInfo.VersionInfo.DeviceFamilyVersion;
+ ulong version = ulong.Parse(systemVersion);
+ SystemVersion = new Version(
+ (int)((version & 0xFFFF000000000000L) >> 48),
+ (int)((version & 0x0000FFFF00000000L) >> 32),
+ (int)((version & 0x00000000FFFF0000L) >> 16),
+ (int)(version & 0x000000000000FFFFL)
+ );
+ }
+}
+#endif
\ No newline at end of file
diff --git a/components/TitleBar/src/MultiTarget.props b/components/TitleBar/src/MultiTarget.props
new file mode 100644
index 000000000..6a4600344
--- /dev/null
+++ b/components/TitleBar/src/MultiTarget.props
@@ -0,0 +1,9 @@
+
+
+
+ uwp;wasdk;
+
+
\ No newline at end of file
diff --git a/components/TitleBar/src/NativeMethods.cs b/components/TitleBar/src/NativeMethods.cs
new file mode 100644
index 000000000..6306ae87a
--- /dev/null
+++ b/components/TitleBar/src/NativeMethods.cs
@@ -0,0 +1,86 @@
+#if WINAPPSDK
+using System.Runtime.InteropServices;
+
+namespace CommunityToolkit.WinUI.Controls;
+internal static class NativeMethods
+{
+ public enum WindowMessage : int
+ {
+ WM_NCLBUTTONDOWN = 0x00A1,
+ WM_NCRBUTTONDOWN = 0x00A4,
+ WM_SYSCOMMAND = 0x0112,
+ WM_SYSMENU = 0x0313,
+ WM_GETMINMAXINFO = 0x0024
+ }
+ [Flags]
+ public enum WindowStyle : uint
+ {
+ WS_SYSMENU = 0x80000
+ }
+
+ [Flags]
+ public enum WindowLongIndexFlags : int
+ {
+ GWL_WNDPROC = -4,
+ GWL_STYLE = -16
+ }
+
+ [Flags]
+ public enum SetWindowPosFlags : uint
+ {
+ ///
+ /// Retains the current position (ignores X and Y parameters).
+ ///
+ SWP_NOMOVE = 0x0002
+ }
+
+ public enum SystemCommand
+ {
+ SC_MOUSEMENU = 0xF090,
+ SC_KEYMENU = 0xF100
+ }
+
+ [DllImport("user32.dll", EntryPoint = "GetWindowLongW", SetLastError = false)]
+ public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
+
+ [DllImport("user32.dll", EntryPoint = "GetWindowLongPtrW", SetLastError = false)]
+ public static extern int GetWindowLongPtr(IntPtr hWnd, int nIndex);
+
+ public static int GetWindowLongAuto(IntPtr hWnd, int nIndex)
+ {
+ if (IntPtr.Size is 8)
+ {
+ return GetWindowLongPtr(hWnd, nIndex);
+ }
+ else
+ {
+ return GetWindowLong(hWnd, nIndex);
+ }
+ }
+
+ [DllImport("user32.dll", EntryPoint = "FindWindowExW", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern IntPtr FindWindowEx(IntPtr hWndParent, IntPtr hWndChildAfter, string lpszClass, string lpszWindow);
+
+
+ [DllImport("user32.dll", EntryPoint = "SetWindowLongW", SetLastError = false)]
+ public static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
+
+ [DllImport("user32.dll", EntryPoint = "SetWindowLongPtrW", SetLastError = false)]
+ public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
+
+ public static IntPtr SetWindowLongAuto(IntPtr hWnd, int nIndex, IntPtr dwNewLong)
+ {
+ if (IntPtr.Size is 8)
+ {
+ return SetWindowLongPtr(hWnd, nIndex, dwNewLong);
+ }
+ else
+ {
+ return SetWindowLong(hWnd, nIndex, dwNewLong);
+ }
+ }
+
+ [DllImport("user32.dll")]
+ public static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, WindowMessage Msg, IntPtr wParam, IntPtr lParam);
+}
+#endif
\ No newline at end of file
diff --git a/components/TitleBar/src/Themes/Generic.xaml b/components/TitleBar/src/Themes/Generic.xaml
new file mode 100644
index 000000000..07014e9bd
--- /dev/null
+++ b/components/TitleBar/src/Themes/Generic.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/components/TitleBar/src/TitleBar.Properties.cs b/components/TitleBar/src/TitleBar.Properties.cs
new file mode 100644
index 000000000..17237dacb
--- /dev/null
+++ b/components/TitleBar/src/TitleBar.Properties.cs
@@ -0,0 +1,225 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace CommunityToolkit.WinUI.Controls;
+
+public partial class TitleBar : Control
+{
+ ///
+ /// The backing for the property.
+ ///
+ public static readonly DependencyProperty IconProperty = DependencyProperty.Register(nameof(Icon), typeof(IconElement), typeof(TitleBar), new PropertyMetadata(null, IconChanged));
+
+ ///
+ /// The backing for the property.
+ ///
+ public static readonly DependencyProperty TitleProperty = DependencyProperty.Register(nameof(Title), typeof(string), typeof(TitleBar), new PropertyMetadata(default(string)));
+
+ ///
+ /// The backing for the property.
+ ///
+ public static readonly DependencyProperty SubtitleProperty = DependencyProperty.Register(nameof(Subtitle), typeof(string), typeof(TitleBar), new PropertyMetadata(default(string)));
+
+ ///
+ /// The backing for the property.
+ ///
+ public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(nameof(Content), typeof(object), typeof(TitleBar), new PropertyMetadata(null, ContentChanged));
+
+ ///
+ /// The backing for the property.
+ ///
+ public static readonly DependencyProperty FooterProperty = DependencyProperty.Register(nameof(Footer), typeof(object), typeof(TitleBar), new PropertyMetadata(null, FooterChanged));
+
+ ///
+ /// The backing for the property.
+ ///
+ public static readonly DependencyProperty IsBackButtonVisibleProperty = DependencyProperty.Register(nameof(IsBackButtonVisible), typeof(bool), typeof(TitleBar), new PropertyMetadata(false, IsBackButtonVisibleChanged));
+
+ ///
+ /// The backing for the property.
+ ///
+ public static readonly DependencyProperty IsPaneButtonVisibleProperty = DependencyProperty.Register(nameof(IsPaneButtonVisible), typeof(bool), typeof(TitleBar), new PropertyMetadata(false, IsPaneButtonVisibleChanged));
+
+ ///
+ /// The backing for the property.
+ ///
+ public static readonly DependencyProperty DisplayModeProperty = DependencyProperty.Register(nameof(DisplayMode), typeof(DisplayMode), typeof(TitleBar), new PropertyMetadata(DisplayMode.Standard, DisplayModeChanged));
+
+ ///
+ /// The backing for the property.
+ ///
+ public static readonly DependencyProperty CompactStateBreakpointProperty = DependencyProperty.Register(nameof(CompactStateBreakpoint), typeof(int), typeof(TitleBar), new PropertyMetadata(850));
+
+ ///
+ /// The backing for the property.
+ ///
+ public static readonly DependencyProperty AutoConfigureCustomTitleBarProperty = DependencyProperty.Register(nameof(AutoConfigureCustomTitleBar), typeof(bool), typeof(TitleBar), new PropertyMetadata(true, AutoConfigureCustomTitleBarChanged));
+
+#if WINAPPSDK
+ ///
+ /// The backing for the property.
+ ///
+ public static readonly DependencyProperty WindowProperty = DependencyProperty.Register(nameof(Window), typeof(Window), typeof(TitleBar), new PropertyMetadata(null));
+#endif
+
+ ///
+ /// The event that gets fired when the back button is clicked
+ ///
+ public event EventHandler? BackButtonClick;
+
+ ///
+ /// The event that gets fired when the pane toggle button is clicked
+ ///
+ public event EventHandler? PaneButtonClick;
+
+ ///
+ /// Gets or sets the Icon
+ ///
+ public IconElement Icon
+ {
+ get => (IconElement)GetValue(IconProperty);
+ set => SetValue(IconProperty, value);
+ }
+
+ ///
+ /// Gets or sets the Title
+ ///
+ public string Title
+ {
+ get => (string)GetValue(TitleProperty);
+ set => SetValue(TitleProperty, value);
+ }
+
+ ///
+ /// Gets or sets the Subtitle
+ ///
+ public string Subtitle
+ {
+ get => (string)GetValue(SubtitleProperty);
+ set => SetValue(SubtitleProperty, value);
+ }
+
+ ///
+ /// Gets or sets the content shown at the center of the TitleBar. When setting this, using DisplayMode=Tall is recommended.
+ ///
+ public object Content
+ {
+ get => (object)GetValue(ContentProperty);
+ set => SetValue(ContentProperty, value);
+ }
+
+ ///
+ /// Gets or sets the content shown at the right of the TitleBar, next to the caption buttons. When setting this, using DisplayMode=Tall is recommended.
+ ///
+ public object Footer
+ {
+ get => (object)GetValue(FooterProperty);
+ set => SetValue(FooterProperty, value);
+ }
+
+ ///
+ /// Gets or sets DisplayMode. Compact is default (32px), Tall is recommended when setting the Content or Footer.
+ ///
+ public DisplayMode DisplayMode
+ {
+ get => (DisplayMode)GetValue(DisplayModeProperty);
+ set => SetValue(DisplayModeProperty, value);
+ }
+
+ ///
+ /// Gets or sets the visibility of the back button.
+ ///
+ public bool IsBackButtonVisible
+ {
+ get => (bool)GetValue(IsBackButtonVisibleProperty);
+ set => SetValue(IsBackButtonVisibleProperty, value);
+ }
+
+ ///
+ /// Gets or sets the visibility of the pane toggle button.
+ ///
+ public bool IsPaneButtonVisible
+ {
+ get => (bool)GetValue(IsPaneButtonVisibleProperty);
+ set => SetValue(IsPaneButtonVisibleProperty, value);
+ }
+
+ ///
+ /// Gets or sets the breakpoint of when the compact state is triggered.
+ ///
+ public int CompactStateBreakpoint
+ {
+ get => (int)GetValue(CompactStateBreakpointProperty);
+ set => SetValue(CompactStateBreakpointProperty, value);
+ }
+
+ ///
+ /// Gets or sets if the TitleBar should auto configure ExtendContentIntoTitleBar and CaptionButtion background colors.
+ ///
+ public bool AutoConfigureCustomTitleBar
+ {
+ get => (bool)GetValue(AutoConfigureCustomTitleBarProperty);
+ set => SetValue(AutoConfigureCustomTitleBarProperty, value);
+ }
+
+#if WINAPPSDK
+ ///
+ /// Gets or sets the window the TitleBar should configure (WASDK only).
+ ///
+ public Window Window
+ {
+ get => (Window)GetValue(WindowProperty);
+ set => SetValue(WindowProperty, value);
+ }
+#endif
+
+ private static void IsBackButtonVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ ((TitleBar)d).Update();
+ }
+
+ private static void IsPaneButtonVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ ((TitleBar)d).Update();
+ }
+
+ private static void DisplayModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ ((TitleBar)d).Update();
+ }
+
+ private static void ContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ ((TitleBar)d).Update();
+ }
+
+ private static void FooterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ ((TitleBar)d).Update();
+ }
+
+ private static void IconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ ((TitleBar)d).Update();
+ }
+
+ private static void AutoConfigureCustomTitleBarChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (((TitleBar)d).AutoConfigureCustomTitleBar)
+ {
+ ((TitleBar)d).Configure();
+ }
+ else
+ {
+ ((TitleBar)d).Reset();
+ }
+ }
+}
+
+public enum DisplayMode
+{
+ Standard,
+ Tall
+}
diff --git a/components/TitleBar/src/TitleBar.UWP.cs b/components/TitleBar/src/TitleBar.UWP.cs
new file mode 100644
index 000000000..cbe14bf71
--- /dev/null
+++ b/components/TitleBar/src/TitleBar.UWP.cs
@@ -0,0 +1,63 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#if WINDOWS_UWP && !HAS_UNO
+using Windows.ApplicationModel.Core;
+using Windows.UI.ViewManagement;
+using Windows.UI;
+
+namespace CommunityToolkit.WinUI.Controls;
+
+[TemplatePart(Name = nameof(PART_DragRegion), Type = typeof(Grid))]
+
+public partial class TitleBar : Control
+{
+ Grid? PART_DragRegion;
+
+ private void SetUWPTitleBar()
+ {
+ if (AutoConfigureCustomTitleBar)
+ {
+ CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = true;
+ CoreApplication.GetCurrentView().TitleBar.LayoutMetricsChanged -= this.TitleBar_LayoutMetricsChanged;
+ CoreApplication.GetCurrentView().TitleBar.LayoutMetricsChanged += this.TitleBar_LayoutMetricsChanged;
+ Window.Current.Activated -= this.Current_Activated;
+ Window.Current.Activated += this.Current_Activated;
+
+ ApplicationView.GetForCurrentView().TitleBar.ButtonBackgroundColor = Colors.Transparent;
+ ApplicationView.GetForCurrentView().TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
+
+ PART_DragRegion = GetTemplateChild(nameof(PART_DragRegion)) as Grid;
+ Window.Current.SetTitleBar(PART_DragRegion);
+ }
+ }
+
+ private void ResetUWPTitleBar()
+ {
+ CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = false;
+ Window.Current.Activated -= this.Current_Activated;
+ SizeChanged -= this.TitleBar_SizeChanged;
+ CoreApplication.GetCurrentView().TitleBar.LayoutMetricsChanged -= this.TitleBar_LayoutMetricsChanged;
+ Window.Current.SetTitleBar(null);
+ }
+
+ private void Current_Activated(object sender, Windows.UI.Core.WindowActivatedEventArgs e)
+ {
+ if (e.WindowActivationState == Windows.UI.Core.CoreWindowActivationState.Deactivated)
+ {
+ VisualStateManager.GoToState(this, WindowDeactivatedState, true);
+ }
+ else
+ {
+ VisualStateManager.GoToState(this, WindowActivatedState, true);
+ }
+ }
+
+ private void TitleBar_LayoutMetricsChanged(CoreApplicationViewTitleBar sender, object args)
+ {
+ PART_LeftPaddingColumn!.Width = new GridLength(CoreApplication.GetCurrentView().TitleBar.SystemOverlayLeftInset);
+ PART_RightPaddingColumn!.Width = new GridLength(CoreApplication.GetCurrentView().TitleBar.SystemOverlayRightInset);
+ }
+}
+#endif
diff --git a/components/TitleBar/src/TitleBar.WASDK.cs b/components/TitleBar/src/TitleBar.WASDK.cs
new file mode 100644
index 000000000..ea8bd946c
--- /dev/null
+++ b/components/TitleBar/src/TitleBar.WASDK.cs
@@ -0,0 +1,260 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#if WINDOWS_WINAPPSDK && !HAS_UNO
+using Windows.Graphics;
+using Microsoft.UI;
+using Microsoft.UI.Input;
+using Microsoft.UI.Windowing;
+using Microsoft.UI.Xaml.Media;
+
+namespace CommunityToolkit.WinUI.Controls;
+
+[TemplatePart(Name = nameof(PART_FooterPresenter), Type = typeof(ContentPresenter))]
+[TemplatePart(Name = nameof(PART_ContentPresenter), Type = typeof(ContentPresenter))]
+
+public partial class TitleBar : Control
+{
+ WndProcHelper? WndProcHelper;
+ MenuFlyout? MenuFlyout;
+ ContentPresenter? PART_ContentPresenter;
+ ContentPresenter? PART_FooterPresenter;
+
+ private void SetWASDKTitleBar()
+ {
+ if (this.Window == null)
+ {
+ return;
+ // TO DO: Throw exception that window has not been set?
+ }
+ if (AutoConfigureCustomTitleBar)
+ {
+ Window.AppWindow.TitleBar.ExtendsContentIntoTitleBar = true;
+
+ if (this.ContextFlyout != null && this.ContextFlyout is MenuFlyout menuFlyout)
+ {
+ this.MenuFlyout = menuFlyout;
+ WndProcHelper = new WndProcHelper(this.Window);
+ WndProcHelper.RegisterWndProc(WindowWndProc);
+ WndProcHelper.RegisterInputNonClientPointerSourceWndProc(InputNonClientPointerSourceWndProc);
+ }
+
+ this.Window.SizeChanged -= Window_SizeChanged;
+ this.Window.SizeChanged += Window_SizeChanged;
+ this.Window.Activated -= Window_Activated;
+ this.Window.Activated += Window_Activated;
+
+ if (Window.Content is FrameworkElement rootElement)
+ {
+ UpdateCaptionButtons(rootElement);
+ rootElement.ActualThemeChanged += (s, e) =>
+ {
+ UpdateCaptionButtons(rootElement);
+ };
+ }
+
+ PART_ContentPresenter = GetTemplateChild(nameof(PART_ContentPresenter)) as ContentPresenter;
+ PART_FooterPresenter = GetTemplateChild(nameof(PART_FooterPresenter)) as ContentPresenter;
+
+ // Get caption button occlusion information.
+ int CaptionButtonOcclusionWidthRight = Window.AppWindow.TitleBar.RightInset;
+ int CaptionButtonOcclusionWidthLeft = Window.AppWindow.TitleBar.LeftInset;
+ PART_LeftPaddingColumn!.Width = new GridLength(CaptionButtonOcclusionWidthLeft);
+ PART_RightPaddingColumn!.Width = new GridLength(CaptionButtonOcclusionWidthRight);
+
+ if (DisplayMode == DisplayMode.Tall)
+ {
+ // Choose a tall title bar to provide more room for interactive elements
+ // like search box or person picture controls.
+ Window.AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Tall;
+ }
+ else
+ {
+ Window.AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Standard;
+ }
+ // Recalculate the drag region for the custom title bar
+ // if you explicitly defined new draggable areas.
+ SetDragRegionForCustomTitleBar();
+ }
+ }
+
+ private void Window_SizeChanged(object sender, WindowSizeChangedEventArgs args)
+ {
+ UpdateVisualStateAndDragRegion(args.Size);
+ }
+
+ private void UpdateCaptionButtons(FrameworkElement rootElement)
+ {
+ Window.AppWindow.TitleBar.ButtonBackgroundColor = Colors.Transparent;
+ Window.AppWindow.TitleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
+ if (rootElement.ActualTheme == ElementTheme.Dark)
+ {
+ Window.AppWindow.TitleBar.ButtonForegroundColor = Colors.White;
+ Window.AppWindow.TitleBar.ButtonInactiveForegroundColor = Colors.DarkGray;
+ }
+ else
+ {
+ Window.AppWindow.TitleBar.ButtonForegroundColor = Colors.Black;
+ Window.AppWindow.TitleBar.ButtonInactiveForegroundColor = Colors.DarkGray;
+ }
+ }
+
+ private void ResetWASDKTitleBar()
+ {
+ if (this.Window == null)
+ {
+ return;
+ // TO DO: Throw exception that window has not been set?
+ }
+
+ Window.AppWindow.TitleBar.ExtendsContentIntoTitleBar = false;
+ this.Window.SizeChanged -= Window_SizeChanged;
+ this.Window.Activated -= Window_Activated;
+ SizeChanged -= this.TitleBar_SizeChanged;
+ Window.AppWindow.TitleBar.ResetToDefault();
+ }
+
+ private void Window_Activated(object sender, WindowActivatedEventArgs args)
+ {
+ if (args.WindowActivationState == WindowActivationState.Deactivated)
+ {
+ VisualStateManager.GoToState(this, WindowDeactivatedState, true);
+ }
+ else
+ {
+ VisualStateManager.GoToState(this, WindowActivatedState, true);
+ }
+ }
+
+ public void SetDragRegionForCustomTitleBar()
+ {
+ if (AutoConfigureCustomTitleBar && Window is not null)
+ {
+ ClearDragRegions(NonClientRegionKind.Passthrough);
+ var items = new FrameworkElement?[] { PART_ContentPresenter, PART_FooterPresenter, PART_ButtonHolder };
+ var validItems = items.Where(x => x is not null).Select(x => x!).ToArray(); // Prune null items
+
+ SetDragRegion(NonClientRegionKind.Passthrough, validItems);
+ }
+ }
+
+ public double GetRasterizationScaleForElement(UIElement element)
+ {
+ if (element.XamlRoot != null)
+ {
+ return element.XamlRoot.RasterizationScale;
+ }
+ return 0.0;
+ }
+
+ public void SetDragRegion(NonClientRegionKind nonClientRegionKind, params FrameworkElement[] frameworkElements)
+ {
+ var nonClientInputSrc = InputNonClientPointerSource.GetForWindowId(Window.AppWindow.Id);
+ List rects = new List();
+ var scale = GetRasterizationScaleForElement(this);
+
+ foreach (var frameworkElement in frameworkElements)
+ {
+ if (frameworkElement == null)
+ {
+ continue;
+ }
+ GeneralTransform transformElement = frameworkElement.TransformToVisual(null);
+ Windows.Foundation.Rect bounds = transformElement.TransformBounds(new Windows.Foundation.Rect(0, 0, frameworkElement.ActualWidth, frameworkElement.ActualHeight));
+ var transparentRect = new Windows.Graphics.RectInt32(
+ _X: (int)Math.Round(bounds.X * scale),
+ _Y: (int)Math.Round(bounds.Y * scale),
+ _Width: (int)Math.Round(bounds.Width * scale),
+ _Height: (int)Math.Round(bounds.Height * scale)
+ );
+ rects.Add(transparentRect);
+ }
+ if (rects.Count > 0)
+ {
+ nonClientInputSrc.SetRegionRects(nonClientRegionKind, rects.ToArray());
+ }
+ }
+
+ public void ClearDragRegions(NonClientRegionKind nonClientRegionKind)
+ {
+ var noninputsrc = InputNonClientPointerSource.GetForWindowId(Window.AppWindow.Id);
+ noninputsrc.ClearRegionRects(nonClientRegionKind);
+ }
+
+ private IntPtr InputNonClientPointerSourceWndProc(IntPtr hWnd, NativeMethods.WindowMessage Msg, IntPtr wParam, IntPtr lParam)
+ {
+ switch (Msg)
+ {
+ case NativeMethods.WindowMessage.WM_NCLBUTTONDOWN:
+ {
+ if (MenuFlyout?.IsOpen ?? false)
+ {
+ MenuFlyout.Hide();
+ }
+ break;
+ }
+ case NativeMethods.WindowMessage.WM_NCRBUTTONDOWN:
+ {
+ PointInt32 pt = new PointInt32(lParam.ToInt32() & 0xFFFF, lParam.ToInt32() >> 16);
+ FlyoutShowOptions options = new FlyoutShowOptions();
+ options.ShowMode = FlyoutShowMode.Standard;
+ options.Position = InfoHelper.SystemVersion.Build >= 22000 ?
+ new Windows.Foundation.Point((pt.X - this.Window.AppWindow.Position.X - 8) / XamlRoot.RasterizationScale, (pt.Y - this.Window.AppWindow.Position.Y) / XamlRoot.RasterizationScale) :
+ new Windows.Foundation.Point(pt.X - this.Window.AppWindow.Position.X - 8, pt.Y - this.Window.AppWindow.Position.Y);
+
+ MenuFlyout?.ShowAt(this, options);
+ return (IntPtr)0;
+ }
+ }
+
+ if (WndProcHelper is null)
+ {
+ throw new InvalidOperationException($"Internal error: {nameof(WndProcHelper)} is missing.");
+ }
+
+ return WndProcHelper.CallInputNonClientPointerSourceWindowProc(hWnd, Msg, wParam, lParam);
+ }
+
+ private IntPtr WindowWndProc(IntPtr hWnd, NativeMethods.WindowMessage Msg, IntPtr wParam, IntPtr lParam)
+ {
+ switch (Msg)
+ {
+ case NativeMethods.WindowMessage.WM_SYSMENU:
+ {
+ return (IntPtr)0;
+ }
+
+ case NativeMethods.WindowMessage.WM_SYSCOMMAND:
+ {
+ NativeMethods.SystemCommand sysCommand = (NativeMethods.SystemCommand)(wParam.ToInt32() & 0xFFF0);
+
+ if (sysCommand is NativeMethods.SystemCommand.SC_MOUSEMENU)
+ {
+ FlyoutShowOptions options = new FlyoutShowOptions();
+ options.Position = new Windows.Foundation.Point(0, 15);
+ options.ShowMode = FlyoutShowMode.Standard;
+ MenuFlyout?.ShowAt(null, options);
+ return (IntPtr)0;
+ }
+ else if (sysCommand is NativeMethods.SystemCommand.SC_KEYMENU)
+ {
+ FlyoutShowOptions options = new FlyoutShowOptions();
+ options.Position = new Windows.Foundation.Point(0, 45);
+ options.ShowMode = FlyoutShowMode.Standard;
+ MenuFlyout?.ShowAt(null, options);
+ return (IntPtr)0;
+ }
+ break;
+ }
+ }
+
+ if (WndProcHelper is null)
+ {
+ throw new InvalidOperationException($"Internal error: {nameof(WndProcHelper)} is missing.");
+ }
+
+ return WndProcHelper.CallWindowProc(hWnd, Msg, wParam, lParam);
+ }
+}
+#endif
diff --git a/components/TitleBar/src/TitleBar.cs b/components/TitleBar/src/TitleBar.cs
new file mode 100644
index 000000000..7b8fe5e5a
--- /dev/null
+++ b/components/TitleBar/src/TitleBar.cs
@@ -0,0 +1,222 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace CommunityToolkit.WinUI.Controls;
+
+[TemplateVisualState(Name = BackButtonVisibleState, GroupName = BackButtonStates)]
+[TemplateVisualState(Name = BackButtonCollapsedState, GroupName = BackButtonStates)]
+[TemplateVisualState(Name = PaneButtonVisibleState, GroupName = PaneButtonStates)]
+[TemplateVisualState(Name = PaneButtonCollapsedState, GroupName = PaneButtonStates)]
+[TemplateVisualState(Name = WindowActivatedState, GroupName = ActivationStates)]
+[TemplateVisualState(Name = WindowDeactivatedState, GroupName = ActivationStates)]
+[TemplateVisualState(Name = StandardState, GroupName = DisplayModeStates)]
+[TemplateVisualState(Name = TallState, GroupName = DisplayModeStates)]
+[TemplateVisualState(Name = IconVisibleState, GroupName = IconStates)]
+[TemplateVisualState(Name = IconCollapsedState, GroupName = IconStates)]
+[TemplateVisualState(Name = ContentVisibleState, GroupName = ContentStates)]
+[TemplateVisualState(Name = ContentCollapsedState, GroupName = ContentStates)]
+[TemplateVisualState(Name = FooterVisibleState, GroupName = FooterStates)]
+[TemplateVisualState(Name = FooterCollapsedState, GroupName = FooterStates)]
+[TemplateVisualState(Name = WideState, GroupName = ReflowStates)]
+[TemplateVisualState(Name = NarrowState, GroupName = ReflowStates)]
+[TemplatePart(Name = PartBackButton, Type = typeof(Button))]
+[TemplatePart(Name = PartPaneButton, Type = typeof(Button))]
+[TemplatePart(Name = nameof(PART_LeftPaddingColumn), Type = typeof(ColumnDefinition))]
+[TemplatePart(Name = nameof(PART_RightPaddingColumn), Type = typeof(ColumnDefinition))]
+[TemplatePart(Name = nameof(PART_ButtonHolder), Type = typeof(StackPanel))]
+
+public partial class TitleBar : Control
+{
+ private const string PartBackButton = "PART_BackButton";
+ private const string PartPaneButton = "PART_PaneButton";
+
+ private const string BackButtonVisibleState = "BackButtonVisible";
+ private const string BackButtonCollapsedState = "BackButtonCollapsed";
+ private const string BackButtonStates = "BackButtonStates";
+
+ private const string PaneButtonVisibleState = "PaneButtonVisible";
+ private const string PaneButtonCollapsedState = "PaneButtonCollapsed";
+ private const string PaneButtonStates = "PaneButtonStates";
+
+ private const string WindowActivatedState = "Activated";
+ private const string WindowDeactivatedState = "Deactivated";
+ private const string ActivationStates = "WindowActivationStates";
+
+ private const string IconVisibleState = "IconVisible";
+ private const string IconCollapsedState = "IconCollapsed";
+ private const string IconStates = "IconStates";
+
+ private const string StandardState = "Standard";
+ private const string TallState = "Tall";
+ private const string DisplayModeStates = "DisplayModeStates";
+
+ private const string ContentVisibleState = "ContentVisible";
+ private const string ContentCollapsedState = "ContentCollapsed";
+ private const string ContentStates = "ContentStates";
+
+ private const string FooterVisibleState = "FooterVisible";
+ private const string FooterCollapsedState = "FooterCollapsed";
+ private const string FooterStates = "FooterStates";
+
+ private const string WideState = "Wide";
+ private const string NarrowState = "Narrow";
+ private const string ReflowStates = "ReflowStates";
+
+ ColumnDefinition? PART_LeftPaddingColumn;
+ ColumnDefinition? PART_RightPaddingColumn;
+ StackPanel? PART_ButtonHolder;
+
+ public TitleBar()
+ {
+ this.DefaultStyleKey = typeof(TitleBar);
+ }
+
+ protected override void OnApplyTemplate()
+ {
+ PART_LeftPaddingColumn = GetTemplateChild(nameof(PART_LeftPaddingColumn)) as ColumnDefinition;
+ PART_RightPaddingColumn = GetTemplateChild(nameof(PART_RightPaddingColumn)) as ColumnDefinition;
+ ConfigureButtonHolder();
+ Configure();
+ if (GetTemplateChild(PartBackButton) is Button backButton)
+ {
+ backButton.Click -= BackButton_Click;
+ backButton.Click += BackButton_Click;
+ }
+
+ if (GetTemplateChild(PartPaneButton) is Button paneButton)
+ {
+ paneButton.Click -= PaneButton_Click;
+ paneButton.Click += PaneButton_Click;
+ }
+
+
+ SizeChanged -= this.TitleBar_SizeChanged;
+ SizeChanged += this.TitleBar_SizeChanged;
+
+ Update();
+ base.OnApplyTemplate();
+ }
+
+ private void TitleBar_SizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ UpdateVisualStateAndDragRegion(e.NewSize);
+ }
+
+ private void UpdateVisualStateAndDragRegion(Windows.Foundation.Size size)
+ {
+ if (size.Width <= CompactStateBreakpoint)
+ {
+ if (Content != null || Footer != null)
+ {
+ VisualStateManager.GoToState(this, NarrowState, true);
+ }
+ }
+ else
+ {
+ VisualStateManager.GoToState(this, WideState, true);
+ }
+
+#if WINAPPSDK
+ SetDragRegionForCustomTitleBar();
+#endif
+ }
+
+ private void BackButton_Click(object sender, RoutedEventArgs e)
+ {
+ BackButtonClick?.Invoke(this, new RoutedEventArgs());
+ }
+
+ private void PaneButton_Click(object sender, RoutedEventArgs e)
+ {
+ PaneButtonClick?.Invoke(this, new RoutedEventArgs());
+ }
+
+ private void ConfigureButtonHolder()
+ {
+ if (PART_ButtonHolder != null)
+ {
+ PART_ButtonHolder.SizeChanged -= PART_ButtonHolder_SizeChanged;
+ }
+
+ PART_ButtonHolder = GetTemplateChild(nameof(PART_ButtonHolder)) as StackPanel;
+
+ if(PART_ButtonHolder != null)
+ {
+ PART_ButtonHolder.SizeChanged += PART_ButtonHolder_SizeChanged;
+ }
+ }
+
+ private void PART_ButtonHolder_SizeChanged(object sender, SizeChangedEventArgs e)
+ {
+#if WINAPPSDK
+ SetDragRegionForCustomTitleBar();
+#endif
+ }
+
+ private void Configure()
+ {
+#if WINDOWS_UWP && !HAS_UNO
+ SetUWPTitleBar();
+#endif
+#if WINAPPSDK
+ SetWASDKTitleBar();
+#endif
+ }
+
+ public void Reset()
+ {
+#if WINDOWS_UWP && !HAS_UNO
+ ResetUWPTitleBar();
+#endif
+#if WINAPPSDK
+ ResetWASDKTitleBar();
+#endif
+ }
+
+ private void Update()
+ {
+ if (Icon != null)
+ {
+ VisualStateManager.GoToState(this, IconVisibleState, true);
+ }
+ else
+ {
+ VisualStateManager.GoToState(this, IconCollapsedState, true);
+ }
+
+ VisualStateManager.GoToState(this, IsBackButtonVisible ? BackButtonVisibleState : BackButtonCollapsedState, true);
+ VisualStateManager.GoToState(this, IsPaneButtonVisible ? PaneButtonVisibleState : PaneButtonCollapsedState, true);
+
+ if (DisplayMode == DisplayMode.Tall)
+ {
+ VisualStateManager.GoToState(this, TallState, true);
+ }
+ else
+ {
+ VisualStateManager.GoToState(this, StandardState, true);
+ }
+
+ if (Content != null)
+ {
+ VisualStateManager.GoToState(this, ContentVisibleState, true);
+ }
+ else
+ {
+ VisualStateManager.GoToState(this, ContentCollapsedState, true);
+ }
+
+ if (Footer != null)
+ {
+ VisualStateManager.GoToState(this, FooterVisibleState, true);
+ }
+ else
+ {
+ VisualStateManager.GoToState(this, FooterCollapsedState, true);
+ }
+
+#if WINAPPSDK
+ SetDragRegionForCustomTitleBar();
+#endif
+ }
+}
diff --git a/components/TitleBar/src/TitleBar.xaml b/components/TitleBar/src/TitleBar.xaml
new file mode 100644
index 000000000..f353bafbd
--- /dev/null
+++ b/components/TitleBar/src/TitleBar.xaml
@@ -0,0 +1,385 @@
+
+
+ 32
+ 48
+ 360
+
+
+
+
+
+
+
+
+
+
diff --git a/components/TitleBar/src/WndProcHelper.cs b/components/TitleBar/src/WndProcHelper.cs
new file mode 100644
index 000000000..8a7c11ac9
--- /dev/null
+++ b/components/TitleBar/src/WndProcHelper.cs
@@ -0,0 +1,51 @@
+#if WINAPPSDK
+using System.Runtime.InteropServices;
+using WinRT.Interop;
+
+namespace CommunityToolkit.WinUI.Controls;
+internal class WndProcHelper
+{
+ public delegate IntPtr WNDPROC(IntPtr hWnd, NativeMethods.WindowMessage Msg, IntPtr wParam, IntPtr lParam);
+
+ private IntPtr Handle { get; set; }
+ private WNDPROC? newMainWindowWndProc = null;
+ private IntPtr oldMainWindowWndProc = IntPtr.Zero;
+
+ private WNDPROC? newInputNonClientPointerSourceWndProc = null;
+ private IntPtr oldInputNonClientPointerSourceWndProc = IntPtr.Zero;
+
+ public WndProcHelper(Window window)
+ {
+ Handle = WindowNative.GetWindowHandle(window);
+ }
+
+ public IntPtr CallWindowProc(IntPtr hWnd, NativeMethods.WindowMessage Msg, IntPtr wParam, IntPtr lParam)
+ {
+ return NativeMethods.CallWindowProc(oldMainWindowWndProc, hWnd, Msg, wParam, lParam);
+ }
+
+ public IntPtr CallInputNonClientPointerSourceWindowProc(IntPtr hWnd, NativeMethods.WindowMessage Msg, IntPtr wParam, IntPtr lParam)
+ {
+ return NativeMethods.CallWindowProc(oldInputNonClientPointerSourceWndProc, hWnd, Msg, wParam, lParam);
+ }
+ public void RegisterWndProc(WNDPROC wndProc)
+ {
+ newMainWindowWndProc = wndProc;
+ oldMainWindowWndProc = NativeMethods.SetWindowLongAuto(Handle, (int)NativeMethods.WindowLongIndexFlags.GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(newMainWindowWndProc));
+ }
+
+ public void RegisterInputNonClientPointerSourceWndProc(WNDPROC wndProc)
+ {
+ IntPtr inputNonClientPointerSourceHandle = NativeMethods.FindWindowEx(Handle, IntPtr.Zero, "InputNonClientPointerSource", string.Empty);
+
+ if (inputNonClientPointerSourceHandle != IntPtr.Zero)
+ {
+ int style = NativeMethods.GetWindowLongAuto(Handle, (int)NativeMethods.WindowLongIndexFlags.GWL_STYLE);
+ NativeMethods.SetWindowLongAuto(Handle, (int)NativeMethods.WindowLongIndexFlags.GWL_STYLE, (IntPtr)(style & ~(int)NativeMethods.WindowStyle.WS_SYSMENU));
+
+ newInputNonClientPointerSourceWndProc = wndProc;
+ oldInputNonClientPointerSourceWndProc = NativeMethods.SetWindowLongAuto(inputNonClientPointerSourceHandle, (int)NativeMethods.WindowLongIndexFlags.GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(newInputNonClientPointerSourceWndProc));
+ }
+ }
+}
+#endif
diff --git a/components/TitleBar/tests/ExampleTitleBarTestClass.cs b/components/TitleBar/tests/ExampleTitleBarTestClass.cs
new file mode 100644
index 000000000..1245988a8
--- /dev/null
+++ b/components/TitleBar/tests/ExampleTitleBarTestClass.cs
@@ -0,0 +1,73 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using CommunityToolkit.Tooling.TestGen;
+using CommunityToolkit.Tests;
+using CommunityToolkit.WinUI.Controls;
+
+namespace TitleBarExperiment.Tests;
+
+[TestClass]
+public partial class ExampleTitleBarTestClass : VisualUITestBase
+{
+ // If you don't need access to UI objects directly or async code, use this pattern.
+ [TestMethod]
+ public void SimpleSynchronousExampleTest()
+ {
+ var assembly = typeof(TitleBar).Assembly;
+ var type = assembly.GetType(typeof(TitleBar).FullName ?? string.Empty);
+
+ Assert.IsNotNull(type, "Could not find TitleBar type.");
+ Assert.AreEqual(typeof(TitleBar), type, "Type of TitleBar does not match expected type.");
+ }
+
+ // If you don't need access to UI objects directly, use this pattern.
+ [TestMethod]
+ public async Task SimpleAsyncExampleTest()
+ {
+ await Task.Delay(250);
+
+ Assert.IsTrue(true);
+ }
+
+ // Example that shows how to check for exception throwing.
+ [TestMethod]
+ public void SimpleExceptionCheckTest()
+ {
+ // If you need to check exceptions occur for invalid inputs, etc...
+ // Use Assert.ThrowsException to limit the scope to where you expect the error to occur.
+ // Otherwise, using the ExpectedException attribute could swallow or
+ // catch other issues in setup code.
+ Assert.ThrowsException(() => throw new NotImplementedException());
+ }
+
+ // The UIThreadTestMethod automatically dispatches to the UI for us to work with UI objects.
+ [UIThreadTestMethod]
+ public void SimpleUIAttributeExampleTest()
+ {
+ var component = new TitleBar();
+ Assert.IsNotNull(component);
+ }
+
+ // The UIThreadTestMethod can also easily grab a XAML Page for us by passing its type as a parameter.
+ // This lets us actually test a control as it would behave within an actual application.
+ // The page will already be loaded by the time your test is called.
+ [UIThreadTestMethod]
+ public void SimpleUIExamplePageTest(ExampleTitleBarTestPage page)
+ {
+ // You can use the Toolkit Visual Tree helpers here to find the component by type or name:
+
+ }
+
+ // You can still do async work with a UIThreadTestMethod as well.
+ [UIThreadTestMethod]
+ public async Task SimpleAsyncUIExamplePageTest(ExampleTitleBarTestPage page)
+ {
+ // This helper can be used to wait for a rendering pass to complete.
+ // Note, this is already done by loading a Page with the [UIThreadTestMethod] helper.
+ await CompositionTargetHelper.ExecuteAfterCompositionRenderingAsync(() => { });
+
+
+ }
+}
diff --git a/components/TitleBar/tests/ExampleTitleBarTestPage.xaml b/components/TitleBar/tests/ExampleTitleBarTestPage.xaml
new file mode 100644
index 000000000..d1e4598d7
--- /dev/null
+++ b/components/TitleBar/tests/ExampleTitleBarTestPage.xaml
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/components/TitleBar/tests/ExampleTitleBarTestPage.xaml.cs b/components/TitleBar/tests/ExampleTitleBarTestPage.xaml.cs
new file mode 100644
index 000000000..a3a3ddc3e
--- /dev/null
+++ b/components/TitleBar/tests/ExampleTitleBarTestPage.xaml.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace TitleBarExperiment.Tests;
+
+///
+/// An empty page that can be used on its own or navigated to within a Frame.
+///
+public sealed partial class ExampleTitleBarTestPage : Page
+{
+ public ExampleTitleBarTestPage()
+ {
+ this.InitializeComponent();
+ }
+}
diff --git a/components/TitleBar/tests/TitleBar.Tests.projitems b/components/TitleBar/tests/TitleBar.Tests.projitems
new file mode 100644
index 000000000..41ae219e8
--- /dev/null
+++ b/components/TitleBar/tests/TitleBar.Tests.projitems
@@ -0,0 +1,23 @@
+
+
+
+ $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+ true
+ 3BA0AB2F-24A8-4E53-BEFE-2796E3E82421
+
+
+ TitleBarExperiment.Tests
+
+
+
+
+ ExampleTitleBarTestPage.xaml
+
+
+
+
+ Designer
+ MSBuild:Compile
+
+
+
\ No newline at end of file
diff --git a/components/TitleBar/tests/TitleBar.Tests.shproj b/components/TitleBar/tests/TitleBar.Tests.shproj
new file mode 100644
index 000000000..5c6e8c674
--- /dev/null
+++ b/components/TitleBar/tests/TitleBar.Tests.shproj
@@ -0,0 +1,13 @@
+
+
+
+ 3BA0AB2F-24A8-4E53-BEFE-2796E3E82421
+ 14.0
+
+
+
+
+
+
+
+