WPF 实现完全可控制的漂亮自定义窗口
在WPF界面开发中,有时候不想用系统的死板的窗口,想要来点新花样,常会自定义窗口。
那么,先抛出问题,想搞出下面这样的窗口,该咋整 ?
aa
下面看一个啥也没设置过的普通窗口,这样的窗口,我们只能控制客户区,也就是白色部分,标题的棕色部分长啥样,我们控制不了。
所以我们要做的就是把窗口的棕色部分,也整成我们想写啥就写啥。下面放上我实现的效果,源码 https://files.cnblogs.com/files/CSSZBB/CustomWndow.rar
下面娓娓道来,一个窗口大概需要实现的功能如下
太久远的年代的做法我们就不讨论了,
现在随便一搜,应该有文章都会说用WindowChrome,如果没有windowchrome,上述的所有功能都需要自己实现,光是拖动边框改变大小这么一个,就够写半天的啦。
先说坑把,
1 主要就是最大化的逻辑,使用WindowChrome后最大化有点问题需要特殊处理,且受windowstyle="None" ResizeMode="NoResize" 这些参数的影响,最大化的效果也不同,需要注意。
2 在 captionHeight 的范围内的控件,需设置WindowChrome.IsHitTestVisibleInChrome="True" 这个附加属性,
另外说一下windowstyle="None" 这个参数,按我目前试过的经验来说,除非需要制作能透明的窗口,否则是不需要设置windowstyle="None" 的 ,windowstyle="None" 时,系统自带的最大化,最小化,关闭 三个按钮,都是没有的,需要自己去实现。
当你给窗口设置了WindowChrome后,
<Window.Style> <Style TargetType="Window"> <Setter Property="WindowChrome.WindowChrome"> <Setter.Value> <WindowChrome CornerRadius="0"
CaptionHeight="30" GlassFrameThickness="-1" UseAeroCaptionButtons="True" NonClientFrameEdges="None" /> </Setter.Value> </Setter> </Style> </Window.Style>
先不改window的style的其他参数。看到棕色的部分没有了,就是说设置了WindowChrome后,我们可控制的区域,扩展了本来不能控制的棕色头部
那么,是否系统给我们生成的棕色部分,没有了那?其他它还是在的,只是被我们的白色部分覆盖了。这里修改一下window的Template 。可以看到它又露出来了
<Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Window"> <Grid > <Grid.RowDefinitions> <RowDefinition Height="32"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid x:Name="Content_Panel" Grid.Row="1" Background="{TemplateBinding Background}"> <AdornerDecorator> <ContentPresenter/> </AdornerDecorator> </Grid> </Grid> </ControlTemplate> </Setter.Value> </Setter>
上面的代码中,我们给Window的模板,定义了一个32高度的行,这个行里啥也不放,也就是透明的。可以看到,系统给生成的最大化,最小化等按钮它又出来了,但是标题,ICO等就没有了。说明标题和ICON ,需要在Template中定义,它不是系统生成的。
那么,上面指出观察到的这个情况,有何意义那?
意义可能就是,有时候,我们只是想在标题栏(棕色部分) 添加几个控件,比如加个公司图标啊啥的,而不想改变其它时,可以这样做。这样,可以给我们省下自己去重写最大化,最小化,关闭按钮的时间
这里面忘记说了窗口重写Style的一个不算坑的坑,在编码阶段,不管你怎么设置Style,标题部分都是不变的,我一度以为是自己写的Style无效,一运行才发现是有效的
更多的时候,我们希望完全重写棕色部分。如下是一个框架的Window给我们提供的所有功能(包括边框拖动改变大小,按住标题区拖动改变位置),那么我们重写Style Template后,需要自己实现哪些部分那?
据我的经验,需要自己实现图标,标题,最小化,最大化,关闭这5个部分,
在没有WindowChrome的年代,重写window是需要自己实现所有功能的,所以说windowchrome还是提供了一些方便的。
来看下它的属性, 1 CaptionHeight 这个属性,是控制标题区(棕色部分,设置windowchrome后它不显示),接收鼠标响应的高度。 本来它现实的时候,这个部分右键,会弹出系统菜单,按住拖动,双击等都有响应的逻辑
现在棕色部分虽然不显示了,但是设置了CaptionHeight 后,相应高度的部分,仍然会对支持上述操作。
它也导致了一个问题,就是当你放上你自己写的最大化,最小化,关闭按钮时,你会发现点击无效,点这些按钮还是触发的 点击标题栏的事件。
这就牵扯出另外一个属性
WindowChrome.IsHitTestVisibleInChrome="True"。给你自己写的最大化,最小化,关闭按钮时,加上这个附加属性,他们就能响应鼠标了
到此,自定义标题栏,应该没啥问题了,你想加啥控件就加啥,完全自由。
其他属性影响不大
那么该说说里面的坑了,当你点击最大化窗口时,你会发现它的最大化是有问题的。如下图对比,最大化前,可以看到窗口里的3层的绿色边框,最大化后,发现左右下都只能看到一层边框了。且最大化等按钮,也超出了屏幕导致没显示全。
如果你设置了windows 的属性ResizeMode="NoResize",或者WindowStyle="None" 你会发现最大化后,它把任务栏也给占了,就想真正的全屏游戏一样!!
这里的原因想想也不难理解,最大化窗口,你自己实现最大化按钮一般只用设置WindowState = WindowState.Maximized;(不管是直接设置属性还是用系统命令)
而系统去做最大化这一步,其实是要很多步骤的,包括获取屏幕大小,计算窗口边框,调整窗口大小到屏幕大小(这里要去掉任务栏高度,去掉边框宽度)等等,
那么当你通过windowchrome重写了windowstyle改变了客户区的大小,系统默认给你的窗口边框边框也变了,它如果还是按原来的处理方式最大化,应该是有问题的。
这里的更精确的原理我们猜也猜不明白,也不是我们能控制的。只要能根据现在的现象调整出对的结果就行了
我直接说结果就是最大化时,系统把整个界面多最大化了8。上下左右都是。所以我们设置一个触发器,在最大化的时候,把我们自己定义的区域,缩小8就行了
完美!!
剩下的,就是把窗口做的漂亮点啦。开搞把!