WPF自定义界面WindowChrome
WPF自定义界面WindowChrome
默认WPF的界面其实也还行,就是满足不了日渐增长的需求,界面还是需要有更高的自定义程度,包括标题栏也要能够塞下更多的操作控件。
默认窗口介绍
新建WPF项目,给里面内容设置一点颜色:
默认创建的界面(Win10上的效果),能够看到两块区域,一块是以颜色#0078D4的内容区,一块是顶部白色的非内容区,按照官方的说法它们依次是客户区和非客户区。
客户区就是我们的主体内容,目前里面有一排文字,这没什么好说的, 你想放什么内容,就往这里放就行了。
非客户区里面的东西稍微有一点多,一点一点来列吧:
左上角的应用图标和标题:
右上角的最小化、最大化和关闭按钮:
还有一个就是我们窗体的边框线,黑色的那条。
以上都是我们能够看到的,还有一些看不见的窗口行为
如缩放,标题栏的拖动以及鼠标拖到窗口到桌面边缘位置,可以对窗口进行一个大小的调整:
点击应用图标之后弹出的窗口操作:
以及在窗口拖动和在拖动的过程中中,窗口会自动扩充到对应的屏幕:
其实呢, 还有一些行为,这里就不做叙述了,下面我们来看看如何自定义
自定义界面方式
WPF中自定义的界面的方式可以分为两种,一种是使用 AllowsTransparency="True"和WindowStyle="None",这种呢就相当于直接把原生非客户区给干掉了,然后我们在内容区域自己去实现非客户区,就会导致窗口自定的行为如:缩放,拖动,停靠边界放大。。。这些功能全都没有了,如果需要的话,是需要自己手动代码添加的。
第二种呢就是使用我们的WindowChrome来自定义界面, 这种方式保留了一个窗口基本的行为,只需要我们重新规划一下客户区和非客户区就行了。
两种方式都不复杂,不过界面客户区和非客户区都是需要自己去定义的,但是WindowChrome的方式保留了窗口的一些基本行为,显示性能也会比第一种强。
至于二者窗口性能的对比,可以看下这个大佬的文章:
(walterlv 吕毅)WPF 制作高性能的透明背景异形窗口(使用 WindowChrome 而不要使用 AllowsTransparency=True)
使用WindowChrome
两种方式自定义界面没什么太大区别, 这里就以WindowChrome来进行举例。
使用WindowChrome的方式也很简单,直接声明一下就行了:
<WindowChrome.WindowChrome>
<WindowChrome />
</WindowChrome.WindowChrome>
<Grid Background="#0078D4">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="50"
FontWeight="Bold"
Foreground="White"
Text="丑萌气质狗" />
</Grid>
得到如下的界面:
这个界面拥有默认窗口的所有窗口行为,就是没有应用图标、标题、操作按钮(最大化、最小化、关闭),其实不是没有,只是被当前Window的默认样式给遮挡了。
只需要去除当前Window的样式,就可以看到WindowChrome的样式:
<Window.Template>
<ControlTemplate TargetType="{x:Type Window}">
<Border />
</ControlTemplate>
</Window.Template>
作为对比看一下默认的Window是什么样子的:
可以看到,仅仅在关闭的地方,WindowChrome的样式就和默认的Window不一致,操作栏都无法靠边界,而且尝试修改这些样式也不好调整到我们想要的样子,所以我们直接抛弃原来的样式,重写WindowChrome,自己定义客户区和非客户区,如果对WindowChrome的样式调整有兴趣,可以看一下:
(walterlv 吕毅)WPF 使用 WindowChrome,在自定义窗口标题栏的同时最大程度保留原生窗口样式(类似 UWP/Chrome)
除了需要这些东西以外, 还需要添加我们自定义的操作按钮,下面我们就要重写一下窗口的样式,自定义里面的客户区和非客户区。
自定义WindowChrome说明
在重写样式之前呢,先来介绍几个WindowChrome的几个属性:
CaptionHeight:标题栏的操作高度,一般跟随非客户区高度,设置为0表示界面无法响应鼠标的任何操作(拖动窗口, 双击标题栏的最大化最小化。。。 仅仅是行为区域,并不影响外观)。
GlassFrameThickness:用来设置距离区域的厚度,设置为-1,可以使得区域覆盖整个界面
ResizeBorderThickness:可以设置窗口缩放的边框厚度,不便于设置过大。下图是设置为50的效果:
可以看到侵入内容区这么多,依然可以进行窗口缩放。
还有一个很重要的属性,不是给WindowChrome设置的,是给界面中的元素设置的,如果你的元素在CaptionHeight的高度内,是无法响应鼠标事件的,此时需要给元素设置属性WindowChrome.IsHitTestVisibleInChrome="True",很常见的问题,在自定义标题按钮的时候,会发现自己点击不了标题按钮!
自定义WindowChrome
既然是要自定义客户区和非客户区,那么我们稍微设计一下(下面示例所有属性均写死,自行更改)
根元素使用Border,我们设置它的边框和颜色,内部使用Grid元素,进行上下两行分割,第一行客户区, 第二行非客户区,第一行的高度改为Auto,第二行默认使用剩下全部区域。
为了撑开非客户区,这里绑定了一个窗口的标题。
大概代码如下
<Window.Template>
<ControlTemplate TargetType="{x:Type Window}">
<Border
x:Name="border"
Background="White"
BorderBrush="#0078d4"
BorderThickness="1"
UseLayoutRounding="True">
<Grid>
<!-- 上下区域 上面是非客户区,下面是客户区 -->
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<!-- 非客户区 -->
<Grid
Grid.Row="0"
Background="#0078d4">
<TextBlock
Padding="10,0,0,0"
VerticalAlignment="Center"
Text="{TemplateBinding Title}" />
</Grid>
<!-- 客户区 -->
<AdornerDecorator Grid.Row="1">
<ContentPresenter ClipToBounds="True" />
</AdornerDecorator>
</Grid>
</Border>
</ControlTemplate>
</Window.Template>
呈现的效果如下(用装饰器AdornerDecorator包装了一下客户区,为了后期方便添加遮罩层,有文章说明,目前没写,后续弄好了链接再贴过来吧):
客户区没啥说的,你自己想怎么写就怎么写, 主要需要说一下非客户区的图标、标题(已经有了)、操作按钮、自定义按钮,直接贴一下代码,样式这么就不加了,采用自己的样式,主要说明下使用系统的几个命令。
<!-- 非客户区 -->
<Grid
Grid.Row="0"
Background=" #0078d4">
<!-- 左右区域 左边是窗体图标文字 右边是操作按钮、最小化、最大化、关闭 -->
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- 图片 标题 -->
<StackPanel
HorizontalAlignment="Left"
VerticalAlignment="Center"
Orientation="Horizontal"
WindowChrome.IsHitTestVisibleInChrome="True">
<Button
Width="16"
Height="16"
Margin="10,0,0,0"
Command="{x:Static SystemCommands.ShowSystemMenuCommand}"
Content="我是图标" />
<TextBlock
Margin="5,0,0,0"
VerticalAlignment="Center"
Text="{TemplateBinding Title}" />
</StackPanel>
<!-- 操作按钮 -->
<StackPanel
Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Orientation="Horizontal"
WindowChrome.IsHitTestVisibleInChrome="True">
<Button>
<Path
Width="12"
Height="12"
Data="F1 M 17.412109 9.648438 C 17.412109 9.707031 17.413736 9.765625 17.416992 9.824219 C 17.420246 9.882812 17.421875 9.941406 17.421875 10 C 17.421875 10.058594 17.420246 10.117188 17.416992 10.175781 C 17.413736 10.234375 17.412109 10.292969 17.412109 10.351562 L 19.941406 11.923828 L 18.388672 15.664062 L 15.488281 15 C 15.332031 15.169271 15.169271 15.332031 15 15.488281 L 15.664062 18.388672 L 11.923828 19.941406 L 10.351562 17.412109 C 10.292969 17.412109 10.234375 17.413736 10.175781 17.416992 C 10.117188 17.420248 10.058594 17.421875 10 17.421875 C 9.941406 17.421875 9.882812 17.420248 9.824219 17.416992 C 9.765625 17.413736 9.707031 17.412109 9.648438 17.412109 L 8.076172 19.941406 L 4.335938 18.388672 L 5 15.488281 C 4.830729 15.332031 4.667969 15.169271 4.511719 15 L 1.611328 15.664062 L 0.058594 11.923828 L 2.587891 10.351562 C 2.587891 10.292969 2.586263 10.234375 2.583008 10.175781 C 2.579752 10.117188 2.578125 10.058594 2.578125 10 C 2.578125 9.941406 2.579752 9.882812 2.583008 9.824219 C 2.586263 9.765625 2.587891 9.707031 2.587891 9.648438 L 0.058594 8.076172 L 1.611328 4.335938 L 4.511719 5 C 4.667969 4.830729 4.830729 4.667969 5 4.511719 L 4.335938 1.611328 L 8.076172 0.058594 L 9.648438 2.587891 C 9.707031 2.587891 9.765625 2.586264 9.824219 2.583008 C 9.882812 2.579754 9.941406 2.578125 10 2.578125 C 10.058594 2.578125 10.117188 2.579754 10.175781 2.583008 C 10.234375 2.586264 10.292969 2.587891 10.351562 2.587891 L 11.923828 0.058594 L 15.664062 1.611328 L 15 4.511719 C 15.169271 4.667969 15.332031 4.830729 15.488281 5 L 18.388672 4.335938 L 19.941406 8.076172 Z M 16.269531 10.917969 C 16.282551 10.761719 16.295572 10.607097 16.308594 10.454102 C 16.321613 10.301107 16.328125 10.146484 16.328125 9.990234 C 16.328125 9.840495 16.321613 9.6875 16.308594 9.53125 C 16.295572 9.375 16.282551 9.222006 16.269531 9.072266 L 18.574219 7.636719 L 17.734375 5.605469 L 15.087891 6.220703 C 14.886067 5.973309 14.679361 5.745443 14.467773 5.537109 C 14.256184 5.328776 14.026691 5.120443 13.779297 4.912109 L 14.394531 2.265625 L 12.363281 1.425781 L 10.917969 3.730469 C 10.768229 3.717449 10.615234 3.704428 10.458984 3.691406 C 10.302734 3.678387 10.149739 3.671875 10 3.671875 C 9.84375 3.671875 9.689127 3.678387 9.536133 3.691406 C 9.383138 3.704428 9.228516 3.717449 9.072266 3.730469 L 7.636719 1.425781 L 5.605469 2.265625 L 6.220703 4.912109 C 5.973307 5.113933 5.745442 5.320639 5.537109 5.532227 C 5.328776 5.743816 5.120442 5.973309 4.912109 6.220703 L 2.265625 5.605469 L 1.425781 7.636719 L 3.730469 9.082031 C 3.717448 9.238281 3.704427 9.392904 3.691406 9.545898 C 3.678385 9.698894 3.671875 9.853516 3.671875 10.009766 C 3.671875 10.159506 3.678385 10.3125 3.691406 10.46875 C 3.704427 10.625 3.717448 10.777995 3.730469 10.927734 L 1.425781 12.363281 L 2.265625 14.394531 L 4.912109 13.779297 C 5.113932 14.026693 5.320638 14.254558 5.532227 14.462891 C 5.743815 14.671225 5.973307 14.879558 6.220703 15.087891 L 5.605469 17.734375 L 7.636719 18.574219 L 9.082031 16.269531 C 9.231771 16.282553 9.384766 16.295572 9.541016 16.308594 C 9.697266 16.321615 9.85026 16.328125 10 16.328125 C 10.15625 16.328125 10.310872 16.321615 10.463867 16.308594 C 10.616861 16.295572 10.771484 16.282553 10.927734 16.269531 L 12.363281 18.574219 L 14.394531 17.734375 L 13.779297 15.087891 C 14.026691 14.886068 14.254557 14.679362 14.462891 14.467773 C 14.671224 14.256186 14.879557 14.026693 15.087891 13.779297 L 17.734375 14.394531 L 18.574219 12.363281 Z M 10 6.328125 C 10.507812 6.328125 10.9847 6.424154 11.430664 6.616211 C 11.876627 6.80827 12.265624 7.070313 12.597656 7.402344 C 12.929687 7.734376 13.19173 8.123373 13.383789 8.569336 C 13.575846 9.0153 13.671875 9.492188 13.671875 10 C 13.671875 10.507812 13.575846 10.984701 13.383789 11.430664 C 13.19173 11.876628 12.929687 12.265625 12.597656 12.597656 C 12.265624 12.929688 11.876627 13.191732 11.430664 13.383789 C 10.9847 13.575847 10.507812 13.671875 10 13.671875 C 9.492188 13.671875 9.015299 13.575847 8.569336 13.383789 C 8.123372 13.191732 7.734375 12.929688 7.402344 12.597656 C 7.070312 12.265625 6.808268 11.876628 6.616211 11.430664 C 6.424153 10.984701 6.328125 10.507812 6.328125 10 C 6.328125 9.492188 6.424153 9.0153 6.616211 8.569336 C 6.808268 8.123373 7.070312 7.734376 7.402344 7.402344 C 7.734375 7.070313 8.123372 6.80827 8.569336 6.616211 C 9.015299 6.424154 9.492188 6.328125 10 6.328125 Z M 10 12.578125 C 10.358072 12.578125 10.693359 12.511394 11.005859 12.37793 C 11.318359 12.244467 11.591797 12.060547 11.826172 11.826172 C 12.060547 11.591797 12.244466 11.318359 12.37793 11.005859 C 12.511393 10.693359 12.578125 10.358073 12.578125 10 C 12.578125 9.641928 12.511393 9.306641 12.37793 8.994141 C 12.244466 8.681641 12.060547 8.408203 11.826172 8.173828 C 11.591797 7.939453 11.318359 7.755534 11.005859 7.62207 C 10.693359 7.488607 10.358072 7.421875 10 7.421875 C 9.641927 7.421875 9.306641 7.488607 8.994141 7.62207 C 8.681641 7.755534 8.408203 7.939453 8.173828 8.173828 C 7.939453 8.408203 7.755533 8.681641 7.62207 8.994141 C 7.488606 9.306641 7.421875 9.641928 7.421875 10 C 7.421875 10.358073 7.488606 10.693359 7.62207 11.005859 C 7.755533 11.318359 7.939453 11.591797 8.173828 11.826172 C 8.408203 12.060547 8.681641 12.244467 8.994141 12.37793 C 9.306641 12.511394 9.641927 12.578125 10 12.578125 Z "
Fill="{Binding (TextBlock.Foreground), RelativeSource={RelativeSource AncestorType=Button}}"
RenderTransformOrigin="0.5,0.5"
Stretch="Uniform" />
</Button>
<Button
x:Name="btnMinimizeButton"
Command="{x:Static SystemCommands.MinimizeWindowCommand}">
<Path
Width="10"
Height="10"
Data="M0,4 L10,4 L10,5 L0,5 z"
Fill="{Binding (TextBlock.Foreground), RelativeSource={RelativeSource AncestorType=Button}}"
RenderTransformOrigin="0.5,0.5"
Stretch="Uniform" />
</Button>
<Button
x:Name="btnMaximizeButton"
Command="{x:Static SystemCommands.MaximizeWindowCommand}">
<Path
Width="10"
Height="10"
Data="M1,1 L1,9 L9,9 L9,1 z M0,0 L10,0 L10,10 L0,10 z"
Fill="{Binding (TextBlock.Foreground), RelativeSource={RelativeSource AncestorType=Button}}"
RenderTransformOrigin="0.5,0.5"
Stretch="Uniform" />
</Button>
<Button
x:Name="btnRestoreButton"
Command="{x:Static SystemCommands.RestoreWindowCommand}"
Visibility="Collapsed">
<Path
Width="10"
Height="10"
Data="M1,3 L1,9 L7,9 L7,3 z M3,1 L3,2 L8,2 L8,7 L9,7 L9,1 z M2,0 L10,0 L10,8 L8,8 L8,10 L0,10 L0,2 L2,2 z"
Fill="{Binding (TextBlock.Foreground), RelativeSource={RelativeSource AncestorType=Button}}"
RenderTransformOrigin="0.5,0.5"
Stretch="Uniform" />
</Button>
<Button
x:Name="btnCloseButton"
Command="{x:Static SystemCommands.CloseWindowCommand}">
<Path
Width="10"
Height="10"
Data="M0.7,0 L5,4.3 L9.3,0 L10,0.7 L5.7,5 L10,9.3 L9.3,10 L5,5.7 L0.7,10 L0,9.3 L4.3,5 L0,0.7 z"
Fill="{Binding (TextBlock.Foreground), RelativeSource={RelativeSource AncestorType=ContentPresenter}}"
RenderTransformOrigin="0.5,0.5"
Stretch="Uniform" />
</Button>
</StackPanel>
</Grid>
样式出完的效果是这样的:
这里记得需要给按钮的父组件添加WindowChrome.IsHitTestVisibleInChrome="True",也可以给按钮本身添加,看自己的需要,不然会导致按钮无法点击。
虽然添加了属性,但是发现图中依然有按钮是置灰的,那是因为我们给按钮绑定了系统的命令,但是了这个命令并没有初始化,所以需要到后台,进行命令的初始化
public MainWindow()
{
InitializeComponent();
CommandBindings.Add(new CommandBinding(SystemCommands.CloseWindowCommand, (_, __) => { SystemCommands.CloseWindow(this); }));
CommandBindings.Add(new CommandBinding(SystemCommands.MinimizeWindowCommand, (_, __) => { SystemCommands.MinimizeWindow(this); }));
CommandBindings.Add(new CommandBinding(SystemCommands.MaximizeWindowCommand, (_, __) => { SystemCommands.MaximizeWindow(this); }));
CommandBindings.Add(new CommandBinding(SystemCommands.RestoreWindowCommand, (_, __) => { SystemCommands.RestoreWindow(this); }));
CommandBindings.Add(new CommandBinding(SystemCommands.ShowSystemMenuCommand, ShowSystemMenu));
}
private void ShowSystemMenu(object sender, ExecutedRoutedEventArgs e)
{
var element = e.OriginalSource as FrameworkElement;
if (element == null)
return;
var position = WindowState == WindowState.Maximized ? new Point(0, element.ActualHeight)
: new Point(Left + BorderThickness.Left, element.ActualHeight + Top + BorderThickness.Top);
position = element.TransformToAncestor(this).Transform(position);
SystemCommands.ShowSystemMenu(this, position);
}
现在窗口是正常了,但是放大之后并没有更换放大区域的图标,可以看到,我们摆放按钮的时候, 有一个按钮被隐藏了, 这就是我们的还原界面按钮,所以需要添加一些触发器,让界面在放大之后,可以更换一下图标,并且我们在放大界面的时候, 明显能够感觉到标题栏超出了屏幕外围(窗口最大化的时候设置BorderThickness=8解决此问题):
上面说的这些都可以放在样式和样式的触发器中去实现,具体的步骤就不说明了,直接贴一下完整的代码,供学习参考(因为很多东西都写死了,所以根据自己的需要,自行进行动态更改)
完整代码的效果:
最后说一句
AllowsTransparency="True"的这种方式依然是适用的,重在开发快速高效,有时候公司的项目,并不需要过多的默认窗口行为,还有就是有些老哥就是喜欢自己添加这些窗口行为,任何一种方式都有有利有弊,性能之战也无终章。
在满足工作要求的时候,我们也要有更高的追求!
没有银弹!
本文来自博客园,作者:丑萌气质狗,转载请注明原文链接:https://www.cnblogs.com/choumengqizhigou/p/15739993.html
转载请注明出处QQ群:560611514