纯XAML实现WPF窗口标题栏自定义

要实现以下目标:
1、窗口标题栏颜色可定义。
2、标题栏可添加控件。
3、为引用方便,只使用XAML。
使用WindowChrome是方便的办法,但遇到如下问题:
1、窗口最大化后,窗口边缘超出屏幕。
2、自定义的最小化、最大化、关闭按钮功能如何用XAML实现。
3、用鼠标拉伸改变窗口大小时,会出现闪烁。
经过参考,并想一些小技巧,终于解决。
1、解决最大化后边缘超出屏幕
设: a = SystemParameters.MaximizedPrimaryScreenWidth
      b=SystemParameters.PrimaryScreenWidth
超出部分尺寸:h = (a-b)/2
可在最大化时四周增加h单位Margin,使显示内容不超出屏幕。
同时最大化时标题栏显示区域高度减少h单位,使得与WindowChrome定义的CaptionHeight对齐。
由于涉及到减法运算,XAML没有加减功能,用Grid自动布局的特性,设计一个运算器.

同理可求最大化时标题栏减少后的高度
2、用纯XAML实现窗口右上角按钮功能
需用到Microsoft.Xaml.Behaviors.Wpf扩展包,先用NuGet下载安装,
再把其命名空间引入: xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
这使按钮可直接调用Window方法。
如实现关闭窗口:
<b:Interaction.Triggers>
    <b:EventTrigger EventName="Click">
        <b:CallMethodAction TargetObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" MethodName="Close"/>
    </b:EventTrigger>
</b:Interaction.Triggers>
3、消除拉伸改变窗口尺寸时白色闪烁
只需设定WindowChrome属性 NonClientFrameEdges="Right"

未尽事宜全在代码中

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib"
                    >
    

    <!--通过下面3个参数调整标题栏高度与resize区域宽度-->
    <!--CaptionAreaHeight 为窗口正常状态下标题栏的总高度,包括拖动窗口的区域与改变大小区域-->
    <!-- CaptionAreaHeight =  CaptionHeight + ResizeBorderThickness.top -->
    <sys:Double x:Key="CaptionAreaHeight">36</sys:Double>
    <sys:Double x:Key="CaptionHeight">30</sys:Double>
    <Thickness x:Key="ResizeBorderThickness">10 6 10 10</Thickness>
    <!--标题栏背景色与前景色-->
    <Brush x:Key="CaptionBackground">#202020</Brush>
    <Brush x:Key="CaptionForeground">#d6d6d6</Brush>
    <!--MouseOver按钮背景色-->
    <Brush x:Key="MouseOverButtonColor">#2d2d2d</Brush>

    <!--标题栏关闭按钮-->
    <Style x:Key="CloseButton" TargetType="Button">
        <Setter Property="Width" Value="50"/>
        <!--WindowChrome.IsHitTestVisibleInChrome=True 让按钮在自定义的标题栏里能被点按-->
        <Setter Property="WindowChrome.IsHitTestVisibleInChrome" Value="True"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button" >
                    <Border x:Name="border"   CornerRadius="0" Background="Transparent">
                        <ContentPresenter Margin="0 1 1 0"
                            HorizontalAlignment="Center"
                            VerticalAlignment="Center" />
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="border" Property="Background" Value="#c42b1c"/>
                        </Trigger>
                        <Trigger Property="IsPressed" Value="True">
                            <Setter TargetName="border" Property="Opacity" Value="0.8"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!--标题栏按钮-->
    <Style x:Key="CaptionButton" TargetType="Button">
        <Setter Property="Width" Value="50"/>
        <Setter Property="WindowChrome.IsHitTestVisibleInChrome" Value="True"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Button" >
                    <Border x:Name="border"   CornerRadius="0" Background="Transparent">
                        <ContentPresenter Margin="0 1 1 0"
                            HorizontalAlignment="Center"
                            VerticalAlignment="Center" />
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter TargetName="border" Property="Background" Value="{StaticResource MouseOverButtonColor}"/>
                        </Trigger>
                        <Trigger Property="IsPressed" Value="True">
                            <Setter TargetName="border" Property="Opacity" Value="0.8"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    <!--窗口-->
    <Style x:Key="ChromeWindow" TargetType="Window">
        <!--NonClientFrameEdges="Right"避免拉伸时出现闪烁-->
        <Setter Property="WindowChrome.WindowChrome">
            <Setter.Value>
                <WindowChrome 
                    NonClientFrameEdges="Right"
                    CaptionHeight="{StaticResource CaptionHeight}"
                    ResizeBorderThickness="{StaticResource ResizeBorderThickness}"
                    UseAeroCaptionButtons="False"
                              />
            </Setter.Value>
        </Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Window">
                    <Grid Background="{TemplateBinding Background}" >
                        
                        <!--通过元素自动分布,计算最大化后超出屏幕的尺寸-->
                        <!--计算公式: (SystemParameters.MaximizedPrimaryScreenWidth - SystemParameters.PrimaryScreenWidth)/2 -->
                        <Grid Height="0" Width="{x:Static SystemParameters.MaximizedPrimaryScreenWidth}">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="auto"/>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            <Border x:Name="outOfScreenPart" Grid.Column="0" Background="Yellow"/>
                            <Border Grid.Column="1" Background="Red" Width="{x:Static SystemParameters.PrimaryScreenWidth}"/>
                            <Border Grid.Column="2" Background="Yellow"/>
                        </Grid>
                        <!--计算最大化后标题栏显示的高度-->
                        <!--计算公式:CaptionAreaHeight - outOfScreenPart.ActualWidth -->
                        <Grid Height="0" Width="{StaticResource CaptionAreaHeight}" Background="Green">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="auto"/>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            <Border Grid.Column="0" Background="Red" Width="{Binding ElementName=outOfScreenPart, Path=ActualWidth}"/>
                            <Border x:Name="maximizedCaptionPart" Grid.Column="1" Background="Yellow"/>
                        </Grid>

                        <Grid x:Name="winGrid">
                            <Grid.RowDefinitions>
                                    <RowDefinition Height="auto"/>
                                    <RowDefinition Height="*"/>
                            </Grid.RowDefinitions>
                            
                            <!--标题栏区域-->
                            <Grid Background="{StaticResource CaptionBackground}" x:Name="captionGrid" Height="{StaticResource CaptionAreaHeight}">
                                <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,0">
                                    <!--标题绑定窗口Title-->
                                    <TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=Title}" Foreground="{StaticResource CaptionForeground}"/>
                                </StackPanel>
                                <!--右侧按钮-->
                                <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
                                    <!--最小化按钮-->
                                    <Button Style="{StaticResource CaptionButton}">
                                        <Path Data="M 0 0 L 9 0" Stroke="{StaticResource CaptionForeground}" StrokeThickness="1"/>
                                        <b:Interaction.Triggers>
                                            <b:EventTrigger EventName="Click">
                                                <b:ChangePropertyAction TargetObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" PropertyName="WindowState" Value="1"/>
                                            </b:EventTrigger>
                                        </b:Interaction.Triggers>
                                    </Button>
                                    <!--最大化按钮-->
                                    <Button Style="{StaticResource CaptionButton}" Name="maxbt">
                                            <Path Data="M 0 0 L 9 0 L 9 9 L 0 9 Z" Stroke="{StaticResource CaptionForeground}" StrokeThickness="1"/>
                                        <b:Interaction.Triggers>
                                            <b:EventTrigger EventName="Click">
                                                <b:ChangePropertyAction TargetObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" PropertyName="WindowState" Value="2"/>
                                            </b:EventTrigger>
                                        </b:Interaction.Triggers>
                                    </Button>
                                    <!--正常化按钮-->
                                    <Button Style="{StaticResource CaptionButton}" Name="normalbt">
                                        <Path  Data="M 2 2 L 2 0 L 9 0 L 9 7 L 7 7 M 0 2 L 7 2 L 7 9 L 0 9 Z" Stroke="{StaticResource CaptionForeground}" StrokeThickness="1"/>
                                        <b:Interaction.Triggers>
                                            <b:EventTrigger EventName="Click">
                                                <b:ChangePropertyAction TargetObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" PropertyName="WindowState" Value="0"/>
                                            </b:EventTrigger>
                                        </b:Interaction.Triggers>
                                    </Button>
                                    <!--关闭按钮-->
                                    <Button Style="{StaticResource CloseButton}">
                                        <Path Data="M 0 0 L 9 9 M 9 0 L 0 9" Stroke="{StaticResource CaptionForeground}" StrokeThickness="1"/>
                                        <b:Interaction.Triggers>
                                            <b:EventTrigger EventName="Click">
                                                <b:CallMethodAction TargetObject="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" MethodName="Close"/>
                                            </b:EventTrigger>
                                        </b:Interaction.Triggers>
                                    </Button>
                                </StackPanel>
                            </Grid>
                            
                            <!--窗口工作区-->
                            <ContentPresenter Grid.Row="1" x:Name="win_content"></ContentPresenter>
                        </Grid>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="WindowState" Value="Maximized">
                            <!--最大化后避免内容到屏幕外-->
                            <Setter TargetName="winGrid" Property="Margin" Value="{Binding ElementName=outOfScreenPart, Path=ActualWidth}"/>
                            <!--最大化后,windowChrome标题栏一部分会到屏幕外,为了区域对齐,相应缩小可见标题栏显示区域高度-->
                            <Setter TargetName="captionGrid" Property="Height" Value="{Binding ElementName=maximizedCaptionPart, Path=ActualWidth}"/>
                            <!--最大化后隐藏最大化按钮-->
                            <Setter TargetName="maxbt" Property="Width" Value="0"/>
                        </Trigger>
                        <Trigger Property="WindowState" Value="Normal">
                            <!--正常大小后隐藏正常化按钮-->
                            <Setter TargetName="normalbt" Property="Width" Value="0"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
                
                
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="WindowState" Value="Maximized">
            </Trigger>
        </Style.Triggers>
    </Style>


</ResourceDictionary>

窗口直接应用Style即可

<Window x:Class="TouchEditor4_1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TouchEditor4_1" 
        Title="MainWindow" Height="450" Width="800" Background="Blue" Style="{StaticResource ChromeWindow}">
    <Grid>
        
    </Grid>
</Window>




       

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值