WPF 自定义 Chrome 库
在 WPF 中创建具有自定义 Chrome 和标题栏按钮的完整功能窗口
引言
如果您曾尝试使用 WindowStyle=”None”
来创建带有自定义边框的窗口,您肯定已经注意到您会丢失标准的 Windows 功能。 本文展示了如何使用 WPF Shell Integration Library 来解决这些问题,并在其基础上添加了标题按钮(最小化、最大化/还原和关闭)以及一个在单击时显示系统菜单并在双击时关闭的窗口图标控件。
背景
WPF Shell Integration Library 提供了一种简单的方法来解决当 WindowStyle
设置为 "None
" 时丢失的以下功能:
- 单击并拖动以移动窗口
- 单击并拖动边框以调整大小
- 双击以最大化和还原
- 右键单击以显示系统菜单
- 拖到顶部以最大化,拖离以取消最大化
- 最大化时,保持 Windows 任务栏可见
但是,它对以下附加功能帮助不大:
- 标题按钮(最小化、最大化/还原、关闭)
- 标题按钮鼠标悬停发光效果(本文未涉及)
- 边框阴影(本文未涉及)
- 窗口标题在窗口宽度较小时截断并显示省略号(本文未涉及)
在尝试创建功能齐全的自定义边框窗口时,我找不到与标准 Windows 最小化/最大化/关闭按钮类似的现有按钮代码,并且我希望有一种便捷的方式将此功能整合到未来的项目中,因此我创建了这个新库,并将其命名为“WPF Custom Chrome Library”。
从标准边框到自定义边框
标准边框
这是我们都熟悉的标准 Windows 边框(在 Windows 7 中)

这是具有标准边框的窗口的基本 XAML 代码片段
<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">
当您将 WindowStyle
设置为 "None
" 时,您将失去标题栏和标题按钮,但仍然拥有调整大小的边框

将 ResizeMode
设置为 "NoResize
" 以移除调整大小的边框

现在我们有了想要的基本自定义外观,但是我们丢失了标题按钮、拖动、双击最大化和系统菜单等标准窗口功能。这是窗口声明的 XAML 代码:
<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">
恢复标准行为
WPF Shell Integration Library 可恢复基本功能。

这是窗口声明的 XAML 代码以及来自 Shell Integration Library 的 WindowChrome
:
<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>
WindowChrome
类恢复了我们在将 WindowStyle
设置为 "None
" 时丢失的基本窗口功能。该类的全部功能及其自定义方法需要另写一篇文章。需要注意的一些重要事项:
ResizeBorderThickness
设置为6
,以提供足够的区域供用户单击以调整窗口大小。这不会以任何方式影响窗口的外观。CaptionHeight
指定窗口顶部有多少区域被标题栏占用,并且应该响应单击并拖动、双击最大化以及右键单击以打开系统菜单。CornerRadius
指定窗口角落的圆角程度。根据文档,CornerRadius
仅在禁用标准窗口玻璃框架时才有效(因为 Aero 未启用或GlassFrameThickness
设置为0
)。GlassFrameThickness
设置为0
以禁用标准的窗口玻璃。
请注意,总的来说,这些 WindowChrome
设置没有视觉效果;它们仅控制行为(例外是 GlassFrameThickness
,如果设置为非零值,则会显示窗口玻璃)。我们应该根据我们自定义边框窗口的视觉尺寸来匹配 WindowChrome
设置。
另请注意,当 WindowChrome
类应用于我们的窗口时,它会自动设置 WindowStyle="None"
和 ResizeMode
为 "NoResize
",因此我们不必手动设置。
有关 WindowChrome
类的更多信息,请参阅 MSDN 文档。
功能齐全的自定义边框窗口
完成自定义边框窗口的最后几个步骤是:
- 使用预定义的
CaptionButtons
用户控件添加标题按钮,或者如果您想自定义按钮,则使用单独的CaptionButton
控件。 - 使用
WindowIcon
控件在单击图标时显示系统菜单,并在双击图标时关闭窗口。 - 使窗口继承自我们自己的自定义窗口基类,该基类提供一个属性,可以正确计算窗口最大化时的标题按钮边距。
首先展示窗口及其 XAML,然后解释底层代码和支持库。
<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
提供自定义边框窗口的完整功能的核心是 CustomChromeLibrary
。该库提供了以下功能:
CaptionButton
- 扩展Button
,提供依赖项属性CornerRadius
、HoverBackground
和PressedBackground
。作为最小化、最大化、关闭和其他标题按钮的基类。MinimizeButton
MaximizeButton
CloseButton (关闭按钮)
CaptionButtons
- 将MinimizeButton
、MaximizeButton
和CloseButton
组合成一个标准的组,可以方便地包含在自定义边框窗口中。WindowIcon
- 提供窗口图标的包装器,可在单击时显示系统菜单,并在双击时关闭窗口。CustomChromeWindow
- 扩展 Window,添加CaptionButtonMargin
,为窗口最大化时的标题按钮和窗口图标提供调整后的边距。
本文的第二部分将更详细地解释这些类以及如何使用它们。