注意:

  • 本文方法基础是WindowChrome,而WindowChrome在.NET Framework 4.5之后才集成发布的。见:WindowChrome Class
  • .NET Framework 4.0中使用WindowChrome,需要安装Ribbon来支持WindowChrome
  • 目前官方文档的内容较为陈旧(但仍有参考价值),其中提到了SystemParameters2,这个应该是Ribbon里的东西,4.5想用可以安装Xceed.Wpf.AvalonDock库,这里面有现成的Microsoft.Windows.Shell.SystemParameters2实现——当然,自己不怕麻烦,可以自己实现来获取要用的系统变量。(一般用不着,4.5SystemParameters添加了许多系统变量的实现)

实现步骤

第一步:基本实现【保留系统基础操作按钮】

  • 添加Window的Style定义,并设置WindowChrome.WindowChrome属性;
  • 设置WindowChrome标题栏:
    • CaptionHeight——主要用于拖动有效区;
    • GlassFrameThickness——影响标题栏系统按钮显示,0表示不使用系统按钮【后面介绍】,-1表示用的系统默认值,如下示例则表示标题栏高度30;
    • 自定义窗体Title

注意:控件模板中的定义:

  • 1、最外层Border背景无颜色,否则会覆盖标题栏,看不到系统按钮。
  • 2、内部布局定义,使用Grid隔出30的标题栏高度,也可以直接对ContentPresenter设置Margin【主要是为了让顶部显示出标题栏】。
    <Style x:Key="WindowStyle1" TargetType="{x:Type Window}">
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome CaptionHeight="30" GlassFrameThickness="0,30,0,0"/>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" >
<AdornerDecorator >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="1"/>
<TextBlock Text="{TemplateBinding Title}" HorizontalAlignment="Left" VerticalAlignment="Center" />
</Grid>
</AdornerDecorator>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

存在的问题: 最大化边框问题 —— 会有部分溢出。

根据观察,这个溢出宽度应该是8,此值的来源:(SystemParameters.MaximizedPrimaryScreenWidth - SystemParameters.WorkArea.Width)/2 ,高度也可以这样计算。

第二步:优化边界处理

  • 方法1:模板添加最大化触发器,设置最大化时,内部布局Margin设为8
  • 方法2:模板添加最大化触发器,设置最大化时,限制布局最大化的宽高最大值

不管使用哪种方法,最大化时,系统的标题栏高度都发生了变化,故,需要重新设置模板中定义的标题栏高度。

    <ControlTemplate TargetType="{x:Type Window}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" >
<AdornerDecorator >
<Grid Name="win_content">
<Grid.RowDefinitions>
<RowDefinition Height="30" x:Name="row_title"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="1"/>
<TextBlock Text="{TemplateBinding Title}" HorizontalAlignment="Left" VerticalAlignment="Center" />
</Grid>
</AdornerDecorator>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="WindowState" Value="Maximized">
<Setter Property="Margin" TargetName="win_content" Value="8"/>
<!--二选一-->
<Setter Property="MaxWidth" TargetName="win_content" Value="{Binding Source={x:Static SystemParameters.WorkArea},Path=Width}" />
<Setter Property="MaxHeight" TargetName="win_content" Value="{Binding Source={x:Static SystemParameters.WorkArea},Path=Height}"/> <Setter Property="Height" TargetName="row_title" Value="22" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>

至此,都是系统标题栏和我们自定义内容组合使用,但这样总有些边边角角要修正,下面就完全自定义标题栏

第三步:完全自定义标题栏【即,不使用系统的操作按钮】

  • 初步操作类似第一步,其中将GlassFrameThickness设置为0
  • 在内容定义部分添加自定义的标题栏,添加操作按钮,并设置按钮属性WindowChrome.IsHitTestVisibleInChrome="True"

如果不设置WindowChrome.IsHitTestVisibleInChrome,则由于我们之前设置CaptionHeight,则这个区域内,按钮将失效。

但是,也不能将整个标题栏布局设置这个属性,那样会完全覆盖系统标题栏的操作,如拖动效果,即CaptionHeight设置的那个区域。

    <!--样式定义-->
<Style x:Key="WindowStyle2" TargetType="{x:Type Window}">
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome UseAeroCaptionButtons="False" GlassFrameThickness="0" CaptionHeight="30" />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" >
<AdornerDecorator >
<ContentPresenter x:Name="win_content" />
</AdornerDecorator>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="WindowState" Value="Maximized">
<Setter Property="Margin" TargetName="win_content" Value="8"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
    <!--定义标题栏-->
<Grid Background="Red" >
<Grid Height="30" HorizontalAlignment="Stretch" VerticalAlignment="Top" Background="Blue">
<StackPanel Orientation="Horizontal" WindowChrome.IsHitTestVisibleInChrome="True" HorizontalAlignment="Right" >
<Button Name="btn_Min" Content="—" ></Button>
<Button Name="btn_Max" Content="☐" ></Button>
<Button Name="btn_Close" Content="✕" ></Button>
</StackPanel>
</Grid>
</Grid>
    //标题栏按钮功能实现
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent(); this.btn_Min.Click += Btn_Min_Click;
this.btn_Max.Click += Btn_Max_Click;
this.btn_Close.Click += Btn_Close_Click;
} private void Btn_Close_Click(object sender, RoutedEventArgs e)
{
this.Close();
} private void Btn_Max_Click(object sender, RoutedEventArgs e)
{
this.WindowState = WindowState.Maximized == this.WindowState ? WindowState.Normal : WindowState.Maximized;
} private void Btn_Min_Click(object sender, RoutedEventArgs e)
{
this.WindowState = WindowState.Minimized;
}
}

关联了解:

如果你平时也使用Visual Studio Code【VSCode】,文中的第一步和第三步,在vscode的设置中有对应效果:

  • 第一步 —— 将vscode用户设置中Title Bar Style值设为native;
  • 第三步 —— 将vscode用户设置中Title Bar Style值设为custom。

相关下载

点击查看完整源代码

解:奇葩史


落后要挨打,个人以前一直局限于老旧方式实现窗体处理,没有跟进WPF技术改进,在此,非常感谢@vbfool的提醒。【之前说周末搞出来,自己这几周浪了,没研究。今天趁着别人双11买买买,咱就撸撸代码】

WPF中自定义标题栏时窗体最大化处理之WindowChrome的更多相关文章

  1. 在WPF中自定义你的绘制(一)

    原文:在WPF中自定义你的绘制(一)   在WPF中自定义你的绘制(一)                                                                 ...

  2. 在WPF中自定义你的绘制(二)

    原文:在WPF中自定义你的绘制(二)   在WPF中自定义你的绘制(二)                                                                 ...

  3. WPF中制作无边框窗体

    原文:WPF中制作无边框窗体 众所周知,在WinForm中,如果要制作一个无边框窗体,可以将窗体的FormBorderStyle属性设置为None来完成.如果要制作成异形窗体,则需要使用图片或者使用G ...

  4. 在VS2005中设置WPF中自定义按钮的事件

    原文:在VS2005中设置WPF中自定义按钮的事件 上篇讲了如何在Blend中绘制圆角矩形(http://blog.csdn.net/johnsuna/archive/2007/08/13/17407 ...

  5. WPF中自定义的DataTemplate中的控件,在Window_Loaded事件中加载机制初探

    原文:WPF中自定义的DataTemplate中的控件,在Window_Loaded事件中加载机制初探         最近因为项目需要,开始学习如何使用WPF开发桌面程序.使用WPF一段时间之后,感 ...

  6. 示例:WPF中自定义MessageService应用DialogHost、Snackbar、NotifyIcon显示各种场景提示消息

    原文:示例:WPF中自定义MessageService应用DialogHost.Snackbar.NotifyIcon显示各种场景提示消息 一.目的:不同交互场景需要提示不同的消息,不同的消息需要用不 ...

  7. 示例:WPF中自定义StoryBoarService在代码中封装StoryBoard、Animation用于简化动画编写

    原文:示例:WPF中自定义StoryBoarService在代码中封装StoryBoard.Animation用于简化动画编写 一.目的:通过对StoryBoard和Animation的封装来简化动画 ...

  8. 在WPF中自定义你的绘制(五)

    原文:在WPF中自定义你的绘制(五) 在WPF中自定义你的绘制(五)                                                                   ...

  9. 在WPF中自定义你的绘制(三)

    原文:在WPF中自定义你的绘制(三) 在WPF中自定义你的绘制(三)                                                                  ...

随机推荐

  1. selenium之 chromedriver与chrome版本映射表(更新至v2.43)

    看到网上基本没有最新的chromedriver与chrome的对应关系表,便兴起整理了一份如下,希望对大家有用: chromedriver版本 支持的Chrome版本 chromedriver版本 支 ...

  2. AspNetCore 基于流下载文件与示例代码

    昨天说了,AspNetCore如何进行上传文件,其中写了两种方式ajax与模型,其文章地址为:https://www.cnblogs.com/ZaraNet/p/9949167.html 那么既然有上 ...

  3. [转]基于.NET平台常用的框架整理

    自从学习.NET以来,优雅的编程风格,极度简单的可扩展性,足够强大开发工具,极小的学习曲线,让我对这个平台产生了浓厚的兴趣,在工作和学习中也积累了一些开源的组件,就目前想到的先整理于此,如果再想到,就 ...

  4. DDD实战进阶第一波(十五):开发一般业务的大健康行业直销系统(总结篇)

    前面我们花了14篇的文章来给大家介绍经典DDD的概念.架构和实践.这篇文章我们来做一个完整的总结,另外生成一个Api接口文档. 一.DDD解决传统的开发的几大问题: 没有描述需求的设计模型:而是直接通 ...

  5. 使用ML.NET实现猜动画片台词

    前面几篇主要内容出自微软官方,经我特意修改的案例的文章: 使用ML.NET实现情感分析[新手篇] 使用ML.NET预测纽约出租车费 .NET Core玩转机器学习 使用ML.NET实现情感分析[新手篇 ...

  6. 【朝花夕拾】Android性能篇之(五)Android虚拟机

    前言 Android虚拟机的使用,使得android应用和Linux内核分离,这样做使得android系统更稳定可靠,比如程序中即使包含恶意代码,也不会直接影响系统文件:也提高了跨平台兼容性.在And ...

  7. Spring Boot 项目配置的使用方法

    第一种写法resources目录下的application.properties文件 第二种写法resources目录下的application.yml文件 在项目中获取配置项: 分组配置:  (配置 ...

  8. OCR识别

    最近作者项目中用到了身份证识别跟营业执照的OCR识别,就研究了一下百度云跟腾讯云的OCR产品接口. 1.腾讯云OCR 收费:身份证OCR和营业执照OCR接口,每个接口每个月各有1000次的免费调用 接 ...

  9. glusterfs+heketi为k8s提供共享存储

    背景 近来在研究k8s,学习到pv.pvc .storageclass的时候,自己捣腾的时候使用nfs手工提供pv的方式,看到官方文档大量文档都是使用storageclass来定义一个后端存储服务, ...

  10. Android单个控件占父控件宽度一半且水平居中

    前些天,在工作中遇到了一个需求:一个“加载上一页”的按钮宽度为父控件宽度一半,且水平居中于父控件中. 在此给出两种思路: 1.直接在Activity代码中获取到当前父控件的宽度,并将此按钮宽度值设置成 ...