How can I create a basic custom window chrome for a WPF window, that doesn't include the close button and still a moveable and resizeable window?
-
1http://blogs.msdn.com/b/wpfsdk/archive/2008/09/08/custom-window-chrome-in-wpf.aspx – BoltClock Jul 22 '11 at 15:27
-
I checked that out...but was looking for more of a tutorial... – Carl Weis Jul 22 '11 at 15:32
-
An oldish, but still relevant question here: [Creating Bordless Windows with custom visual](http://stackoverflow.com/questions/2969521/creating-bordless-windows-with-custom-visual/2969652#2969652). – DK. Jul 22 '11 at 21:46
5 Answers
You set your Window's WindowStyle="None"
, then build your own window interface. You need to build in your own Min/Max/Close/Drag event handlers, but Resizing is still maintained.
For example:
<Window
WindowState="Maximized"
WindowStyle="None"
WindowStartupLocation="CenterScreen"
MaxWidth="{Binding Source={x:Static SystemParameters.WorkArea}, Path=Width}"
MaxHeight="{Binding Source={x:Static SystemParameters.WorkArea}, Path=Height}"
>
<DockPanel x:Name="RootWindow">
<DockPanel x:Name="TitleBar" DockPanel.Dock="Top">
<Button x:Name="CloseButton" Content="X"
Click="CloseButton_Click"
DockPanel.Dock="Right" />
<Button x:Name="MaxButton" Content="Restore"
Click="MaximizeButton_Click"
DockPanel.Dock="Right" />
<Button x:Name="MinButton" Content="Min"
Click="MinimizeButton_Click"
DockPanel.Dock="Right" />
<TextBlock HorizontalAlignment="Center">Application Name</TextBlock>
</DockPanel>
<ContentControl Content="{Binding CurrentPage}" />
</DockPanel>
</Window>
And here's some example code-behind for common window functionality
/// <summary>
/// TitleBar_MouseDown - Drag if single-click, resize if double-click
/// </summary>
private void TitleBar_MouseDown(object sender, MouseButtonEventArgs e)
{
if(e.ChangedButton == MouseButton.Left)
if (e.ClickCount == 2)
{
AdjustWindowSize();
}
else
{
Application.Current.MainWindow.DragMove();
}
}
/// <summary>
/// CloseButton_Clicked
/// </summary>
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
Application.Current.Shutdown();
}
/// <summary>
/// MaximizedButton_Clicked
/// </summary>
private void MaximizeButton_Click(object sender, RoutedEventArgs e)
{
AdjustWindowSize();
}
/// <summary>
/// Minimized Button_Clicked
/// </summary>
private void MinimizeButton_Click(object sender, RoutedEventArgs e)
{
this.WindowState = WindowState.Minimized;
}
/// <summary>
/// Adjusts the WindowSize to correct parameters when Maximize button is clicked
/// </summary>
private void AdjustWindowSize()
{
if (this.WindowState == WindowState.Maximized)
{
this.WindowState = WindowState.Normal;
MaxButton.Content = "1";
}
else
{
this.WindowState = WindowState.Maximized;
MaxButton.Content = "2";
}
}
-
2Actually, use of this -vs- main window -vs- application is a bit hectic (as in error-prone). Alex had more solid [implementation](http://web.archive.org/web/20081006142447/http://blog.opennetcf.com/ayakhnin/PermaLink,guid,23a7be17-d833-4e45-b498-34ef04d98cb2.aspx), though it also misses few minor functional pieces. – DK. Jul 23 '11 at 04:12
-
2You probably also want `DockPanel.Dock="Top"` on the `Titlebar` dockpanel. – Aaron Jul 09 '16 at 14:15
-
4Just use `Shell:WindowChrome.CaptionHeight` and you dont need any of that titlebar click drag nonsense. No reason to re-invent the wheel. Also keeps other default windows behaviors like double click to maximize, shake to solo. – Wobbles May 05 '17 at 14:27
-
Just be aware that setting `CaptionHeight` is going to block clicks to any custom buttons you put up there. – RandomEngy Dec 05 '18 at 08:14
-
2The best is to use `WindowChrome.CaptionHeight` to have all the titlebar features. @RandomEngy the combination with `WindowChrome.IsHitTestVisibleInChrome="True"` at the buttons will have the result that they are clickable in the titlebar area. – Coden Dec 16 '19 at 20:38
-
1To be clear, the WindowChrome.IsHitTestVisibleInChrome attached property goes on the individual buttons. Doesn't work when you put it on the window itself. – RandomEngy Dec 28 '19 at 18:12
.NET 4.5 added a new class that greatly simplifies this.
The WindowChrome class enables you to extend Windows Presentation Foundation (WPF) content into the non-client area of a window that is typically reserved for the operating system’s window manager.
You can find a tutorial here.
And here's a short example usage.

- 6,804
- 2
- 34
- 64
-
4The tutorial is full of dead links and deprecated information, and the short example includes an assembly reference that no longer works. – Josh Noe Dec 16 '16 at 20:30
-
-
5I know it's a little bit late, but I found a great example here : https://engy.us/blog/2020/01/01/implementing-a-custom-window-title-bar-in-wpf/ – SomeCode.NET Oct 31 '20 at 17:03
-
1
I've just used the example below for .net 4.5 and it works very well. Interestingly, it uses a code behind for a resource dictionary for the click events. All you have to do is reference the resource dictionary in your app.xaml
file and then assign the Window the Style CustomWindowStyle
. This was shamelessly stolen from http://www.eidias.com/blog/2014/1/27/restyle-your-window.
<ResourceDictionary x:Class="WpfApp7.WindowStyle"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="CustomWindowStyle" TargetType="{x:Type Window}">
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome CaptionHeight="30"
CornerRadius="4"
GlassFrameThickness="0"
NonClientFrameEdges="None"
ResizeBorderThickness="5"
UseAeroCaptionButtons="False" />
</Setter.Value>
</Setter>
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="Background" Value="Gray" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Grid>
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="5,30,5,5">
<AdornerDecorator>
<ContentPresenter />
</AdornerDecorator>
</Border>
<Grid Height="30"
VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" Margin="5,0">
<Button Content="A" Margin="0,0,5,0" VerticalAlignment="Center" Click="Button_Click" WindowChrome.IsHitTestVisibleInChrome="True"/>
<Button Content="B" Margin="0,0,5,0" VerticalAlignment="Center" Click="Button_Click" WindowChrome.IsHitTestVisibleInChrome="True"/>
<Button Content="C" Margin="0,0,5,0" VerticalAlignment="Center" Click="Button_Click" WindowChrome.IsHitTestVisibleInChrome="True"/>
<Button Content="D" Margin="0,0,5,0" VerticalAlignment="Center" Click="Button_Click" WindowChrome.IsHitTestVisibleInChrome="True"/>
</StackPanel>
<TextBlock Margin="5,0,0,0"
VerticalAlignment="Center"
HorizontalAlignment="Center"
FontSize="16"
Foreground="White"
Text="{TemplateBinding Title}"
Grid.Column="1"/>
<StackPanel Orientation="Horizontal"
Grid.Column="2">
<Button x:Name="btnClose"
Width="15"
Margin="5"
Click="CloseClick"
Content="X"
WindowChrome.IsHitTestVisibleInChrome="True" />
<Button x:Name="btnRestore"
Width="15"
Margin="5"
Click="MaximizeRestoreClick"
Content="#"
WindowChrome.IsHitTestVisibleInChrome="True" />
<Button x:Name="btnMinimize"
Width="15"
Margin="5"
VerticalContentAlignment="Bottom"
Click="MinimizeClick"
Content="_"
WindowChrome.IsHitTestVisibleInChrome="True" />
</StackPanel>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
And for the code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace WpfApp7
{
public partial class WindowStyle : ResourceDictionary
{
public WindowStyle()
{
InitializeComponent();
}
private void CloseClick(object sender, RoutedEventArgs e)
{
var window = (Window)((FrameworkElement)sender).TemplatedParent;
window.Close();
}
private void MaximizeRestoreClick(object sender, RoutedEventArgs e)
{
var window = (Window)((FrameworkElement)sender).TemplatedParent;
if (window.WindowState == System.Windows.WindowState.Normal)
{
window.WindowState = System.Windows.WindowState.Maximized;
}
else
{
window.WindowState = System.Windows.WindowState.Normal;
}
}
private void MinimizeClick(object sender, RoutedEventArgs e)
{
var window = (Window)((FrameworkElement)sender).TemplatedParent;
window.WindowState = System.Windows.WindowState.Minimized;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Hello!");
}
}
}

- 353
- 3
- 10
Here is an easy solution which looks very similar to the default Windows 10 buttons, it simply uses the same Font for the symbols:
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" WindowChrome.IsHitTestVisibleInChrome="True">
<Button Click="Minimize_Click" Content="" FontFamily="Segoe MDL2 Assets" FontSize="10" Padding="15,15,15,5" Background="Transparent" BorderBrush="Transparent" />
<Button Click="Maximize_Click" FontFamily="Segoe MDL2 Assets" FontSize="10" Padding="15,10" Background="Transparent" BorderBrush="Transparent">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Button.Content" Value="" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}, Path=WindowState}" Value="Maximized">
<Setter Property="Button.Content" Value="" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button Click="Close_Click" Content="" FontFamily="Segoe MDL2 Assets" FontSize="10" Padding="15,10" Background="Transparent" BorderBrush="Transparent" />
</StackPanel>
If you want Support for older Windows Versions (7 and 8) have a look here: https://stackoverflow.com/a/27911618/9758687

- 2,579
- 1
- 18
- 25
Here's an overview of the approach you'll need to take:
- Set
WindowStyle="None"
to do your own UI. - Use
WindowChrome.CaptionHeight
to get standard title bar drag/double click/shake behavior and setWindowChrome.IsHitTestVisibleInChrome="True"
on your buttons to make them clickable. - Implement click handlers for your buttons.
- Hook into the Window.StateChanged event to handle maximize/restore changes and update your UI. You can't assume everyone is using your title bar buttons to maximize and restore. This can happen via keyboard shortcuts (Win+Up/Win+Down) or double-clicking the title bar.
- 7px of your window gets cut off from all sides when you maximize. To fix we need to hook into WM_GETMINMAXINFO to supply the correct maximized position.
- You'll need to re-implement a window border to provide contrast over different backgrounds.
- Use <Path> to render the title bar icons so they look good at different DPIs.
- Change the look of the title bar depending on whether the window is active, to give the user an indication of which window has focus.
The full writeup is a little long; I go over them in detail with code examples in this blog post.

- 14,931
- 5
- 70
- 113
-
Definitely what I needed to get started with the fairly non-obvious way to accomplish an attractive and functional custom title bar. Now I have actual dark mode, as well as my menu and search bar integrated into the title bar, similar to how VS2022 does it. Thank you for your excellent blog post on this topic! – Timothy Lee Russell Jul 19 '23 at 04:58