WPF实现自定义窗体的示例代码

 更新时间:2023年09月05日 09:18:11   作者:卓尔不设凡  
.Net默认的窗体样式只有四种,而且都比较“丑”,但是很多时候,我们希望自定义窗体,比如,无边框,有阴影等,所以本文为大家介绍了WPF实现自定义窗体的方法,希望对大家有所帮助

.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实现自定义窗体的示例代码的文章就介绍到这了,更多相关WPF自定义窗体内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C#实现更快读写超级大文件的方法详解

    C#实现更快读写超级大文件的方法详解

    这篇文章主要来和大家介绍一下C#实现更快读写超级大文件的方法,文中的示例代码简洁易懂,对我们深入了解C#有一定的帮助,快跟随小编一起学习起来吧
    2023-06-06
  • 关于C# dynamic装箱问题

    关于C# dynamic装箱问题

    装箱是将值类型转换为类型对象或此值类型实现的任何接口类型的过程,装箱和拆箱的概念是 C# 类型系统统一视图的基础,其中任何类型的值都可以视为对象,这篇文章主要介绍了关于C# dynamic装箱引发的思考,需要的朋友可以参考下
    2022-05-05
  • P/Invoke之C#调用动态链接库DLL示例详解

    P/Invoke之C#调用动态链接库DLL示例详解

    这篇文章主要为大家介绍了P/Invoke之C#调用动态链接库DLL示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-03-03
  • C# new与malloc的使用与区别

    C# new与malloc的使用与区别

    本文主要介绍了C# new与malloc的使用与区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • C#中重载相等(==)运算符示例

    C#中重载相等(==)运算符示例

    这篇文章主要介绍了C#中重载相等(==)运算符示例,运算符重载一直是一个很诡异事情,本文通过实例探讨由运算符重载引出的一个问题,需要的朋友可以参考下
    2015-06-06
  • C#中私有构造函数的特点和用途实例解析

    C#中私有构造函数的特点和用途实例解析

    这篇文章主要介绍了C#中私有构造函数的特点和用途,需要的朋友可以参考下
    2014-08-08
  • C#封装DBHelper类

    C#封装DBHelper类

    DBHelper类是用类将ADO.NET用方法封装起来,用以减少程序员的工作量。本文为大家提供一个C#封装的DBHelper类,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05
  • C#实现将json转换为DataTable的方法

    C#实现将json转换为DataTable的方法

    这篇文章主要介绍了C#实现将json转换为DataTable的方法,涉及C#操作json及DataTable的技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-03-03
  • webBrowser代理设置c#代码

    webBrowser代理设置c#代码

    本文将介绍C# 为webBrowser设置代理实现代码,需要了解的朋友可以参考下
    2012-11-11
  • C#执行js动态编译的方法

    C#执行js动态编译的方法

    这篇文章主要介绍了C#执行js动态编译的方法,是涉及动态编译脚本非常实用的技巧,需要的朋友可以参考下
    2015-01-01

最新评论