Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPF Custom Chrome Library

0.00/5 (No votes)
3 Dec 2010 1  
Create fully functional windows with custom chrome and caption buttons in WPF

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:

Window1.png

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:

Window2.png

Set ResizeMode to "NoResize" in order to remove the resize border:

Window3.png

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.

Window4.png

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.

Window5.png

<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>

		<!--provide the background for the entire form.
		In practice, this appears as the window's resize border,
		because the title and window content obscure the rest-->
		<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>

		<!--title bar-->
		<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>

			<!--Window Icon and Title-->
			<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>

		<!--min/max/close buttons-->
		<ccl:CaptionButtons/>

		<!--Content-->
		<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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here