使用
WindowChrome
创建自定义圆角窗体。WindowChrome
在.NET Framework 4.5
之后才被集成。之前创建自定义窗口,都是把原有样式都取消,然后自己写一套控制逻辑来实现,有了WindowChrome
多了一个选择,开发效率也更高效,下面就是一个简单基础示例。 本次示例项目采用的是.net 6,开发工具VS2022 内容参考:WPF中自定义标题栏时窗体最大化处理之WindowChrome - 奇葩史 - 博客园 (cnblogs.com)
快速上手
通过一个简单示例,来说明使用
WindowChrome
自定义窗体的创建流程。对于不理解的部分可以先不用理解,等整体弄完后自己再去查阅微软文档理解。
创建项目
创建一个WPF项,保持它最初始的样式
添加样式
为了方便编写,采用在
<Window.Resources>
标签内编写样式。最后需要把这里面的样式转移到样式资源字典里面。
基础样式
这里需要注意的是Sytle的TargetType
需要指定为local:MainWindow
,如果指定为Window则运行出错。等后期转移到资源字典的时候可以指定为Window,主窗体使用Sytle属性引用。
xml
<Window.Resources>
<!-- 自定义窗体样式 -->
<Style TargetType="local:MainWindow">
<!-- 设置窗体的WindowChrome -->
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<!-- ResizeBorderThickness:拖拽改变窗体大小的边框厚度;-->
<!-- CornerRadius:窗体圆角;-->
<!-- CaptionHeight顶部标题的高度;-->
<!-- GlassFrameThickness:默认边框的大小,0为不使用默认边框(这样定义的圆角才有效),-1为使用默认边框默认值-->
<WindowChrome ResizeBorderThickness="4" CornerRadius="10" CaptionHeight="50" GlassFrameThickness="0"/>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Window">
<Grid x:Name="MainWindowArea">
<Border Background="White"/>
</Grid>
<!-- 用于处理windowschrom,边框最大化出溢出问题 -->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
点击运行项目,你会得到这样一个带圆角的窗体,由于覆盖了默认控制按钮,而此时有没有编写自定义按钮,因此只能通过VS强行关闭。
标题栏区域样式
这一部分是在控件模板中编写标题栏的布局样式,主要分为3个区域:Icon;Title;ControlButton。
在<Grid x:Name="MainWindowArea">
标签里面填写如下样式布局。定义了两行,标题栏高度为50,50像素以下的区域用作窗体内容的显示。 需要注意的是Image
控件中的图片资源我采用的是图片编译资源的引入,你可以更具实际情况不想用资源的话,可以使用图片路径。 按钮样式是我自定义按钮样式,可以更具你想要的样式自己编写。==示例样式==,我在文章最后会贴出。
xml
<Grid x:Name="MainWindowContentGrid">
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid x:Name="TitleBar">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- Top Icon -->
<Button x:Name="btnIcon" Command="{Binding MenuCommand}">
<Button.Style>
<Style TargetType="Button">
<!-- 这个属性不设置,在Caption上点击无效,无法触发button -->
<Setter Property="WindowChrome.IsHitTestVisibleInChrome" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border FocusVisualStyle="{x:Null}">
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
<Button.Content>
<!-- 这里采用的为内嵌资源引入,你可以自己改为图片路径 -->
<Image Source="{StaticResource App_Icon_TitleBar}" Width="32" Height="32" Margin="9" Stretch="UniformToFill"/>
</Button.Content>
</Button>
<!-- Top Title ViewBox可以自适应内容-->
<Viewbox Grid.Column="0" Grid.ColumnSpan="3">
<StackPanel Margin="10">
<TextBlock TextAlignment="Center" HorizontalAlignment="Center" Text="测试Demo"/>
</StackPanel>
</Viewbox>
<!-- Top ControlBtn -->
<StackPanel Grid.Column="2" Orientation="Horizontal">
<Button Style="{StaticResource ControlButtonBase}" Content="---" Command="{Binding MinimizeCommand}"/>
<Button Style="{StaticResource ControlButtonBase}" Content="▢" Command="{Binding MaximizeCommand}"/>
<Button Style="{StaticResource CloseButton}" Content="╳" Command="{Binding CloseCommand}"/>
</StackPanel>
</Grid>
<!-- window content show-->
<Border Grid.Row="1" Padding="0" >
<ContentPresenter Content="{TemplateBinding Content}"/>
</Border>
</Grid>
点击运行后会得到一个带有自定义标题的空白窗体
这时候我们在窗体里面添加点内容,在</Window>
标签上面添加如下代码
xml
<!-- window Content -->
<Grid>
<Border Background="White" BorderBrush="Red" BorderThickness="4">
<TextBlock Text="这个是内容区域" FontSize="28" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
</Grid>
然后我们就会得到一个拥有内容的窗体。上面的Grid
代码会被作为Window.Content
填充到 ContentPresenter
内渲染显示。
按钮逻辑
按钮点击没有反应,那是没有添加ViewModel中的代码,导致无法执行命令。此部分不属于文章内容不在此详细说明,有多种方法很简单自己实现即可。参考代码见末尾远程仓库。
最大化窗口溢出问题
点击最大化,你会发现窗口红色边框消失,感觉窗口撑破了屏幕,由于计算问题,需要重新定义窗口边缘大小。
有两种方法:
- 方法1:模板添加最大化触发器,设置最大化时,内部布局Margin设为8
- 方法2:模板添加最大化触发器,设置最大化时,限制布局最大化的宽高最大值
这里采用方式1
xml
<!-- 用于处理windowschrom,边框最大化出溢出问题 -->
<ControlTemplate.Triggers>
<Trigger Property="WindowState" Value="Maximized">
<Setter Property="Margin" Value="8" TargetName="MainWindowContentGrid"/>
</Trigger>
</ControlTemplate.Triggers>
按钮样式
样式前期你可以写在窗体资源里面,但写完后最好移动到资源字典里面。
xml
<!-- 控制按钮基础 -->
<Style x:Key="ControlButtonBase" TargetType="Button">
<Setter Property="WindowChrome.IsHitTestVisibleInChrome" Value="True"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Padding" Value="8"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="Foreground" Value="#686868"/>
<Setter Property="FontSize" Value="28"/>
<Setter Property="Width" Value="50"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}">
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#efefef"/>
</Trigger>
</Style.Triggers>
</Style>
<!-- 关闭按钮 -->
<Style x:Key="CloseButton" TargetType="Button" BasedOn="{StaticResource ControlButtonBase}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#FF4500"/>
</Trigger>
</Style.Triggers>
</Style>
结尾
这次只是简单的使用,后续会更新进阶使用,主要介绍里面属性的用法。如果自己感兴趣也可以去查看官方文档,里面解释的很清楚。