zoukankan      html  css  js  c++  java
  • 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买买买,咱就撸撸代码】

  • 相关阅读:
    LeetCode 88. Merge Sorted Array
    LeetCode 75. Sort Colors
    LeetCode 581. Shortest Unsorted Continuous Subarray
    LeetCode 20. Valid Parentheses
    LeetCode 53. Maximum Subarray
    LeetCode 461. Hamming Distance
    LeetCode 448. Find All Numbers Disappeared in an Array
    LeetCode 976. Largest Perimeter Triangle
    LeetCode 1295. Find Numbers with Even Number of Digits
    如何自学并且系统学习计算机网络?(知乎问答)
  • 原文地址:https://www.cnblogs.com/huaxia283611/p/wpf-maximize-windowchrome.html
Copyright © 2011-2022 走看看