WPF 实现完全可控制的漂亮自定义窗口
阅读原文时间:2023年07月09日阅读:2

在WPF界面开发中,有时候不想用系统的死板的窗口,想要来点新花样,常会自定义窗口。

那么,先抛出问题,想搞出下面这样的窗口,该咋整 ?

a

下面看一个啥也没设置过的普通窗口,这样的窗口,我们只能控制客户区,也就是白色部分,标题的棕色部分长啥样,我们控制不了。

所以我们要做的就是把窗口的棕色部分,也整成我们想写啥就写啥。下面放上我实现的效果,源码  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" />



先不改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就行了

完美!!

剩下的,就是把窗口做的漂亮点啦。开搞把!