0

WPF 自定义窗体(一) - 卓尔不设凡

 8 months ago
source link: https://www.cnblogs.com/raynado/p/17677289.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

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

1)使用WindowChrome;

2)使用WindowStyle = “None”。

一、使用WindowChrome。

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

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

2439874-20230904153541649-34197281.png

图中,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>

  

2439874-20230904154045201-556323175.png

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

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

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

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

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

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

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

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

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

2439874-20230904161950977-288043021.png

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

这个图就是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();
}
}

 

2439874-20230904165653968-616934067.png

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

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

  

2439874-20230904174548966-1679675719.png

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

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;
}
}
}

  

2439874-20230904175928739-1139158180.png

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK