快速创建WPF自定义窗体

使用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>

结尾

这次只是简单的使用,后续会更新进阶使用,主要介绍里面属性的用法。如果自己感兴趣也可以去查看官方文档,里面解释的很清楚。

参考代码

项目地址:gitee.com/Boting_DD_b...

相关推荐
姑苏洛言3 分钟前
扫码小程序实现仓库进销存管理中遇到的问题 setStorageSync 存储大小限制错误解决方案
前端·后端
光而不耀@lgy18 分钟前
C++初登门槛
linux·开发语言·网络·c++·后端
方圆想当图灵37 分钟前
由 Mybatis 源码畅谈软件设计(七):SQL “染色” 拦截器实战
后端·mybatis·代码规范
毅航1 小时前
MyBatis 事务管理:一文掌握Mybatis事务管理核心逻辑
java·后端·mybatis
我的golang之路果然有问题1 小时前
速成GO访问sql,个人笔记
经验分享·笔记·后端·sql·golang·go·database
柏油1 小时前
MySql InnoDB 事务实现之 undo log 日志
数据库·后端·mysql
写bug写bug3 小时前
Java Streams 中的7个常见错误
java·后端
Luck小吕3 小时前
两天两夜!这个 GB28181 的坑让我差点卸载 VSCode
后端·网络协议
M1A14 小时前
全栈开发必备:Windows安装VS Code全流程
前端·后端·全栈
蜗牛快跑1234 小时前
github 源码阅读神器 deepwiki,自动生成源码架构图和知识库
前端·后端