Introduction
If you’ve ever tried to use WindowStyle=”None”
to create a window with a custom chrome, you’ve no doubt noticed that you lose standard Windows functionality. This article shows how to use the WPF Shell Integration Library to solve most of these problems, and builds on this foundation by adding caption buttons (minimize, maximize/restore, and close) and a window icon control that displays the system menu on click and closes on double-click.
Background
The WPF Shell Integration Library provides an easy way to address the following features that are lost when WindowStyle
is set to "None
":
- Click-and-drag to move the window
- Click-and-drag borders to resize
- Double-click to maximize and restore
- Right-click to display system menu
- Drag to top to maximize, drag away to unmaximize
- When maximized, leave the Windows taskbar visible
However, it doesn't provide much help for the following additional features:
- Caption buttons (minimize, maximize/restore, close)
- Caption button hover glow (not addressed in this article)
- Border shadow (not addressed in this article)
- Window title abbreviated with ellipsis for smaller window width (not addressed in this article)
In attempting to create a fully-functional window with custom chrome, I was unable to find code for existing buttons similar to the standard Windows min/max/close buttons, and I wanted a convenient way to incorporate this capability into future projects, so I created this new library and named it "WPF Custom Chrome Library".
From Standard Chrome to Custom Chrome
Standard Chrome
Here's the standard Windows chrome (in Windows 7) we're all familiar with:
And here's the basic XAML snippet for the Window with standard chrome:
<Window x:Class="CustomChromeSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomChromeSample"
Title="Window 1 - Standard Chrome"
Height="350" Width="525">
When you set WindowStyle
to "None
", you lose the title bar and caption buttons, but you still have the resize border:
Set ResizeMode
to "NoResize
" in order to remove the resize border:
We now have the basic custom look that we're after, but we've lost standard window functionality like the caption buttons, dragging, double-click to maximize, and the system menu. Here's the XAML for the Window declaration:
<Window x:Class="CustomChromeSample.Window3"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomChromeSample"
WindowStyle="None"
ResizeMode="NoResize"
Title="Window 3 - No Chrome"
Height="350" Width="525">
Restoring Standard Behavior
The WPF Shell Integration Library restores the basic functionality.
Here's the XAML for the Window declaration as well as the WindowChrome
from the Shell Integration Library:
<Window x:Class="CustomChromeSample.Window4"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:shell="http://schemas.microsoft.com/winfx/2006/xaml/presentation/shell"
xmlns:ccl="clr-namespace:CustomChromeLibrary;assembly=CustomChromeLibrary"
xmlns:local="clr-namespace:CustomChromeSample"
Title="Custom Chrome Sample"
Height="350" Width="525">
<shell:WindowChrome.WindowChrome>
<shell:WindowChrome
ResizeBorderThickness="6"
CaptionHeight="43"
CornerRadius="25,25,10,10"
GlassFrameThickness="0">
</shell:WindowChrome>
</shell:WindowChrome.WindowChrome>
The WindowChrome
class restores the basic window functionality that we lost when we set WindowStyle
to "None
". The full capabilities of this class and how to customize it require an article unto itself. Some important points to note:
ResizeBorderThickness
is set to 6
to provide an adequate area for the user to click to resize the window. This does not affect the window appearance in any way.
CaptionHeight
specifies how much of the top of the window is occupied by the title bar and should respond to click-and-drag, double-click to maximize, and right-click for system menu.
CornerRadius
specifies the amount that the window corners are rounded. According to the documentation, CornerRadius
only has an effect when the standard window glass frame is disabled (because Aero is not enabled or because GlassFrameThickness
is set to 0
).
GlassFrameThickness
is set to 0
in order to disable the standard window glass.
Note that in general these WindowChrome
settings do not have any visual effect; they only control behavior (the exception is GlassFrameThickness
, which if set to a non-zero value will cause the windows glass to be displayed). It is up to us to match the WindowChrome
settings to the visual dimensions of our custom chrome window.
Also note that when the WindowChrome
class is applied to our window, it automatically sets WindowStyle="None"
and ResizeMode
to "NoResize
", so we don't have to.
For more information about the WindowChrome
class, refer to the MSDN documentation.
Fully Functional Custom Chrome Window
The final steps to completing our custom chrome window are:
- Add the caption buttons using the pre-defined
CaptionButtons
user control, or individual CaptionButton
controls if we want to customize the buttons.
- Use the
WindowIcon
control to display the system menu when the icon is clicked, and close the window when the icon is double-clicked.
- Derive the window from our own custom window base class that provides a property that correctly calculates the caption button margin when the window is maximized.
First the window and its XAML are presented, and after that the underlying code and supporting library are explained.
<ccl:CustomChromeWindow x:Class="CustomChromeSample.Window5"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:shell="http://schemas.microsoft.com/winfx/2006/xaml/presentation/shell"
xmlns:ccl="clr-namespace:CustomChromeLibrary;assembly=CustomChromeLibrary"
xmlns:local="clr-namespace:CustomChromeSample"
Title="Custom Chrome Sample"
Height="350" Width="525">
<shell:WindowChrome.WindowChrome>
<shell:WindowChrome
ResizeBorderThickness="6"
CaptionHeight="43"
CornerRadius="25,25,10,10"
GlassFrameThickness="0">
</shell:WindowChrome>
</shell:WindowChrome.WindowChrome>
<Window.Resources>
<ResourceDictionary>
<local:CaptionButtonRectToMarginConverter
x:Key="CaptionButtonMarginConverter"/>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary
Source="Resources\GlassButton.xaml"/>
<ResourceDictionary
Source="Resources\GlassIcon.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
-->
<Border CornerRadius="10,10,5,5" Grid.RowSpan="2"
BorderThickness="3" BorderBrush="LightSteelBlue">
<Border.Background>
<LinearGradientBrush StartPoint="0.5,0"
EndPoint="0.5,1">
<GradientStop Color="#99bbbbff" Offset="0" />
<GradientStop Color="#ff7777bb" Offset="1" />
</LinearGradientBrush>
</Border.Background>
</Border>
-->
<Border CornerRadius="10,10,0,0" BorderThickness="3,3,3,1"
BorderBrush="LightSteelBlue"
Margin="{Binding Path=CaptionButtonMargin}">
<Border.Background>
<LinearGradientBrush StartPoint="0.5,0"
EndPoint="0.5,1">
<GradientStop Color="#ffbbbbff" Offset="0" />
<GradientStop Color="#ff7777bb" Offset="1" />
</LinearGradientBrush>
</Border.Background>
-->
<StackPanel Orientation="Horizontal" Margin="0"
VerticalAlignment="Top">
<ccl:WindowIcon Width="35" Height="35"
Background="#ff0000bb"
Margin="7,3,5,5"
Style="{StaticResource GlassIcon}" />
<TextBlock Text="Window 5 - Caption Buttons"
FontFamily="Calibri" FontWeight="Bold" FontSize="26"
Foreground="#FF000044" VerticalAlignment="Center"/>
</StackPanel>
</Border>
-->
<ccl:CaptionButtons/>
-->
<Grid Grid.Row="1">
<TextBlock Grid.Row="1" Margin="10" FontFamily="Verdana"
FontSize="14">
Complete custom chrome with caption buttons.
</TextBlock>
</Grid>
</Grid>
</ccl:CustomChromeWindow>
CustomChromeLibrary
The key to providing complete window functionality in a window with custom chrome is the CustomChromeLibrary
. This library provides the following:
CaptionButton
- Extends Button
to provide dependency properties CornerRadius
, HoverBackground
, and PressedBackground
. Serves as a base class for the minimize, maximize, close, and other caption buttons.
MinimizeButton
MaximizeButton
CloseButton
CaptionButtons
- Combines MinimizeButton
, MaximizeButton
, and CloseButton
into a standard grouping that can be conveniently included in custom chrome windows.
WindowIcon
- Provides a wrapper for the window icon that displays the system menu when clicked, and closes the window when double-clicked.
CustomChromeWindow
- Extends Window by adding CaptionButtonMargin
, which provides adjusted margins for the caption buttons and window icon when the window is maximized.
Part 2 of this article will explain these classes and how to use them in greater detail.