.Net默认的窗体样式只有四种:None、SingleBorderWindow、ThreeDBorderWindow、ToolWindow,都比较“丑”。而很多时候,我们希望自定义窗体,比如,无边框,有阴影,或者有模糊效果等。

在WPF中,要实现自定义窗体比较简单,主要有两种方法:

1)使用WindowChrome;

2)使用WindowStyle = “None”。

一、使用WindowChrome。

  WindowChrome,可以翻译为:窗体装饰条,官方文档中的定义是:表示一个对象,它描述窗口非工作区区域的自定义。(官方链接:WindowChrome 类 (System.Windows.Shell) | Microsoft Learn

  在官方的解释中,窗口由两部分构成:客户区域,非客户区域。

图中,Client Area表示客户区域;其他的部分,统称为非客户区域。

那么WindowChrome的作用是,将客户区域扩展至整个窗体(遮住了非客户区),同时提供部分标准窗体的功能。如下所示:

<Window x:Class="ControlTest.WindowNone"
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:ControlTest"
mc:Ignorable="d"
Title="WindowNone" Height="450" Width="800"> <!-- WindowChrome将客户区域扩展至整个窗体,并遮住标题栏、按钮等-->
<WindowChrome.WindowChrome>
<WindowChrome />
</WindowChrome.WindowChrome> <Grid>
<TabControl>
<TabItem Header="项目"/>
<TabItem Header="代码"/>
</TabControl>
</Grid>
</Window>

   备注:这里的边框,是TabControl的边框,不是窗体的边框。

用上WindowChrome后,会惊奇的发现:在原标题栏的位置,可以用鼠标拖动了;在窗体的四周,可以调整窗体的大小了!Amazing!

但同时,又出现了一个新的问题:窗体中的所以内容,都不能交互(鼠标点击,用户输入)了。

这是为什么呢?可以这样理解。WindowChrome就像一个图层,它将窗体整个覆盖住了。因此窗体上的内容,自然就操作不了。那要如何才能点击呢?

这需要给交互控件,添加WindowChrome的附件属性:IsHitTestVisibleInChrome。如下所示。

    <Grid>
<!-- 使用WindowChrome的附件属性 -->
<TabControl WindowChrome.IsHitTestVisibleInChrome="True">
<TabItem Header="项目"/>
<TabItem Header="代码"/>
</TabControl>
</Grid>

  如果你以为这样就万事大吉了,那只能说太天真了,微软的东西,哪有那么简单的呢??哈哈~

  如果真的按照这个代码,你会发现,又不能使用鼠标拖动窗体了。这是为什么呢?明明之前都可以,为何为控件添加了一个附加属性后,就不行了呢?

  问题肯定出在WindowChrome上。那么我们再来看看WindowChrome:

图中有颜色的区域,实际上均为透明的,看不见的。此处附上颜色则是为了方便解释。

这个图就是WindowChrome的模型。其中Caption区域,表示标题栏,就是它,允许窗体被鼠标拖动。GlassFrameThickness就是Aero窗体的透明边框(Aero主体只在部分操作系统中支持)。ResizeBorderThickness就是调整窗体大小的边框的粗细,它提供了使用鼠标调整窗体大小的功能。而CornerRadius,则将窗体变成了圆角,它只有在GlassFrameThickness = 0 或者未启用Aero主体的窗口中才有效。。

再回到上面的问题,为什么添加了附加属性,就不能用鼠标拖动窗体了呢?

原因在于,TabControl进入了Caption区域。因为设置了附加属性(IsHitTestVisibleInChrome),表示鼠标可以“击穿”WindowChrome,那么自然就无法“点击”到Caption区域,自然就无法拖动窗体了。

那么如果解决这个问题呢?以及如何添加按钮呢?答案是手动添加标题栏。哈哈~ 如下代码所示:

Xaml代码:

<Window x:Class="ControlTest.WindowNone"
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:ControlTest"
mc:Ignorable="d"
Title="WindowNone" Height="450" Width="800"> <!-- WindowChrome将客户区域扩展至整个窗体,并遮住标题栏、按钮等 -->
<WindowChrome.WindowChrome>
     <!-- 设置了标题栏的高度 = 30,圆角 = 20 -->
<WindowChrome CaptionHeight="30" CornerRadius="20" GlassFrameThickness="0"/>
</WindowChrome.WindowChrome> <Border BorderThickness="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border Height="30" Background="YellowGreen">
<Grid>
<Grid.Resources>
<Style TargetType="Button">
<Setter Property="Width" Value="30"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
</Style>
</Grid.Resources>
<StackPanel Orientation="Horizontal" WindowChrome.IsHitTestVisibleInChrome="True">
<Image />
<TextBlock VerticalAlignment="Center" Margin="3,0" Text="{Binding Title, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"/>
</StackPanel> <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" WindowChrome.IsHitTestVisibleInChrome="True">
<Button Content="_" Click="Btn_Min"/>
<Button Content="Max" Click="Btn_Max"/>
<Button Content="X" Click="Btn_Close"/>
</StackPanel>
</Grid>
</Border>
<!-- 使用WindowChrome的附件属性 -->
<TabControl Grid.Row="1" WindowChrome.IsHitTestVisibleInChrome="True">
<TabItem Header="项目"/>
<TabItem Header="代码"/>
</TabControl>
</Grid>
</Border>
</Window> C# 代码: public partial class WindowNone : Window
{
public WindowNone()
{
InitializeComponent();
}

// 最小化
private void Btn_Min(object sender, RoutedEventArgs e)
{
this.WindowState = WindowState.Minimized;
}

  // 最大化、还原
private void Btn_Max(object sender, RoutedEventArgs e)
{
if(this.WindowState == WindowState.Normal)
{
this.WindowState = WindowState.Maximized;
}
else
{
this.WindowState = WindowState.Normal;
}
}

  // 关闭窗体
private void Btn_Close(object sender, RoutedEventArgs e)
{
this.Close();
}
}

 

手动添加了标题栏之后,在标题栏上,你就可以放上任何你放的东西。。。。

二、使用WindowStyle = "None"

将窗体的WindowStyle属性设置为None后,窗体呈现这样:

<Window x:Class="ControlTest.NoneWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="NoneWindow" Height="450" Width="800"
WindowStyle="None">
<Grid>
<TabControl>
<TabItem Header="项目"/>
<TabItem Header="代码"/>
</TabControl>
</Grid>
</Window>

  

这里,你会发现,窗体可以通过鼠标调整大小,但是不能用鼠标拖动。那解决的办法是什么呢?同样是手动设置一个标题栏:

Xaml 代码:
<Window x:Class="ControlTest.NoneWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="NoneWindow" Height="450" Width="800"
WindowStyle="None" BorderThickness="0" BorderBrush="Transparent">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border Height="30" Background="YellowGreen"
MouseDown="TitleMove">
<Grid>
<Grid.Resources>
<Style TargetType="Button">
<Setter Property="Width" Value="30"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
</Style>
</Grid.Resources>
<StackPanel Orientation="Horizontal" WindowChrome.IsHitTestVisibleInChrome="True">
<Image />
<TextBlock VerticalAlignment="Center" Margin="3,0" Text="{Binding Title, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"/>
</StackPanel> <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" WindowChrome.IsHitTestVisibleInChrome="True">
<Button Content="_" Click="Btn_Min"/>
<Button Content="Max" Click="Btn_Max"/>
<Button Content="X" Click="Btn_Close"/>
</StackPanel>
</Grid>
</Border>
<TabControl Grid.Row="1" Margin="10">
<TabItem Header="项目"/>
<TabItem Header="代码"/>
</TabControl>
</Grid>
</Window> C# 代码: public partial class NoneWindow : Window
{
public NoneWindow()
{
InitializeComponent();
} // 窗体移动
private void TitleMove(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton != MouseButton.Left) return; // 非左键点击,退出
if (e.ClickCount == 1)
{
this.DragMove(); // 拖动窗体
}
else
{
WindowMax(); // 双击时,最大化或者还原窗体
}
} // 最小化
private void Btn_Min(object sender, RoutedEventArgs e)
{
this.WindowState = WindowState.Minimized;
} // 关闭窗体
private void Btn_Close(object sender, RoutedEventArgs e)
{
this.Close();
} // 最大化、还原
private void Btn_Max(object sender, RoutedEventArgs e)
{
WindowMax();
} private void WindowMax()
{
if (this.WindowState == WindowState.Normal)
{
this.WindowState = WindowState.Maximized;
}
else
{
this.WindowState = WindowState.Normal;
}
} }

  

这种方式下,会发现在窗体的“标题栏”上面,还有一点留白无法去除,同样窗体的边框也是无法去除的。

如果解决?且听下回分解。  

WPF 自定义窗体(一)的更多相关文章

  1. wpf 自定义窗体的实现

    首先创建自定义窗体的资源文件 <ControlTemplate x:Key="BaseWindowControlTemplate" TargetType="Wind ...

  2. WPF自定义窗体仿新毒霸关闭特效(只能在自定义窗体中正常使用)

    比较简单的一个小功能,和新毒霸类似的效果. 效果代码: bool closeStoryBoardCompleted = false; DoubleAnimation closeAnimation1; ...

  3. WPF自定义控件与样式(13)-自定义窗体Window & 自适应内容大小消息框MessageBox

    一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要内容: 自定义 ...

  4. WPF 创建自定义窗体

    在前面的一篇博客"WPF 自定义Metro Style窗体",展示了如何创建一个类似于Metro Style的Window,并在程序中使用.但是这个窗体不能够自由的改变大小.今天的 ...

  5. WPF 之 自定义窗体标题栏

    在WPF中自定义窗体标题栏,首先需要将窗体的WindowStyle属性设置为None,隐藏掉WPF窗体的自带标题栏.然后可以在窗体内部自定义一个标题栏. 例如,标题栏如下: <WrapPanel ...

  6. 【转】WPF自定义控件与样式(13)-自定义窗体Window & 自适应内容大小消息框MessageBox

    一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等. 本文主要内容: 自定义Window窗体样式: 基于自定义窗体实现自定义MessageB ...

  7. WPF自定义Window窗体样式

    资源文件代码: <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation ...

  8. WPF自定义产品框架

    在讲解之前先看一下效果,有助于理解: 这是客户端的效果图.整个产品分为两部分:1.WPF开发的展示效果的客户端   2.WCF开发的提供数据服务接口的服务端   本章主要讲解一下实际中开发WPF开发客 ...

  9. WPF自定义Window样式(2)

    1. 引言 在上一篇中,介绍了如何建立自定义窗体.接下来,我们需要考虑将该自定义窗体基类放到类库中去,只有放到类库中,我们才能在其他地方去方便的引用该基类. 2. 创建类库 接上一篇的项目,先添加一个 ...

  10. WPF自定义Window样式(1)

    1. 引言 WPF是制作界面的一大利器.最近在做一个项目,用的就是WPF.既然使用了WPF了,那么理所当然的,需要自定义窗体样式.所使用的代码是在网上查到的,遗憾的是,整理完毕后,再找那篇帖子却怎么也 ...

随机推荐

  1. Shiro 授权绕过 (CVE-2022-32532)

    Shiro 授权绕过 (CVE-2022-32532) 一.产品简介 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理. 1.9.1 之前的 Apache ...

  2. vscode 配置代码自动格式化加修复

    子曰:"工欲善其事,必先利其器", 编码必须的就是有一个顺手的ide,然而光有还不行,还要懂得配置,毕竟不同的团队代码规范不同,如目前用得较多的就是eslint,今天就顺便记录下v ...

  3. Python数据类型 - 元祖

    介绍 元祖和列表都是有序数列,列表是用 [ ],元祖使用() 元祖不同的地方在于创建后不能修改   注意:当元祖中只有一个元素的时候,要加上逗号(一个括号会被当成运算符使用) 比如: (123, ) ...

  4. ODOO13之 八:Odoo 13开发之业务逻辑 – 业务流程的支持

    在前面的文章中,我们学习了模型层.如何创建应用数据结构以及如何使用 ORM API 来存储查看数据.本文中我们将利用前面所学的模型和记录集知识实现应用中常用的业务逻辑模式. 本文的主要内容有: 以单据 ...

  5. MySQL uuid及其相关的一些简单性能测试

    运维同事导入一批大约500万左右的数据,耗时较久.他使用的是纯SQL导入,主键使用的是UUID,因为业务原因没有使用自增ID. 因为是内网,不能远程访问. 通过沟通,大致觉得有两个原因,一是因为UUI ...

  6. 【python基础】函数-初识函数

    函数是带名字的代码块,用于完成具体的工作,无需反复编写完成该工作的代码.之前我们接触过print函数,数据类型转换中的int函数.str函数,还有列表中的append函数.pop函数.remove函数 ...

  7. CKS 考试题整理 (05)-Container 安全上下文

    Context Container Security Context 应在特定 namespace 中修改 Deployment. Task 按照如下要求修改 sec-ns 命名空间里的 Deploy ...

  8. vue2父传子,子传父

    首先看父传子 自定义一个子组件 <template> <div> 子组件: <span>{{inputName}}</span> </div> ...

  9. 一文了解Go语言的I/O接口设计

    1. 引言 I/O 操作在编程中扮演着至关重要的角色.它涉及程序与外部世界之间的数据交换,允许程序从外部,如键盘.文件.网络等地方读取数据,也能够将外界输入的数据重新写入到目标位置中.使得程序能够与外 ...

  10. CPython, Pypy, MicroPython...还在傻傻分不清楚?

    哈喽大家好,我是咸鱼 当我们说 Python 时,通常指的是官方实现的 CPython 但还有很多比如 Pypy.Jython.MicroPython.Brython.RustPython 等 &qu ...