• WPF中自定义标题栏时窗体最大化处理之WindowChrome


    注意:

    • 本文方法基础是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买买买,咱就撸撸代码】

  • 相关阅读:
    好用的电脑软件
    Swoft HTTPServer 使用经验分享
    nginx location proxy_pass详解
    kafka文档
    es 安装
    rabbitmq 文档
    Mysql百万级数据迁移,怎么迁移?实战过没?
    用bat脚本在Windows上实现微信多开
    vscode保存时自动格式化
    引入bootstrap
  • 原文地址:https://www.cnblogs.com/huaxia283611/p/wpf-maximize-windowchrome.html
Copyright © 2020-2023  润新知