当前位置:   article > 正文

WPF制作无边框窗体、圆角窗体、支持改变大小、拖动分屏等(一)_rrqmskin

rrqmskin

概述

Windows Presentation Foundation (WPF) 是一个可创建桌面客户端应用程序的 UI 框架。 WPF 开发平台支持广泛的应用开发功能,包括应用模型、资源、控件、图形、布局、数据绑定、文档和安全性。 此框架是 .NET Framework 的一部分,因此,在未来如果想快速的开发桌面软件,WPF腚是你的不二选择。

需求说明

WPF既然是桌面软件UI,那一定绕不开桌面软件的多元化(chan pin jing li de chou pi)显示方式,最常见的就是无边框窗体圆角窗体窗体阴影等,同时还需要支持改变窗口大小响应拖动分屏等。本文也会从这几个需求入手,打造一个让各位猿友满意的窗体。


开整(分割线搞个仪式)


制作无边框窗体

思路: 无边框窗体比较好办,思路就是设置窗体属性WindowStyle为None,这时无边框窗体就创建好了,而且支持改变尺寸,但是为了普适性,需要对窗体进行装饰,比如:窗体拖动、拖动分屏、窗体功能按钮、窗体是否支持改变大小等,这些都需要代码参与。

窗体拖动、拖动分屏

拖动操作一般是在窗体头操作的,所以需要对窗体进行Grid分行。然后在title的Grid里注册鼠标移动事件,然后在后台控制窗体拖动(title的Grid背景色必须赋值,哪怕是透明色,不然不会命中测试)。实际上在调用DragMove方法时,窗体就已经支持了左右分屏以及全屏,但是问题就是:全屏是满屏,把任务栏也遮住了,其次满屏后不能再拖动为正常尺寸。

<Window x:Class="WpfApp1.Window1"
        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:WpfApp1"
        mc:Ignorable="d"
        WindowStyle="None"
        Title="Window1" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid x:Name="title" Background="Transparent" MouseMove="title_MouseMove">
            
        </Grid>
        <Grid x:Name="body" Grid.Row="1">

        </Grid>
    </Grid>
</Window>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
namespace WpfApp1
{
    /// <summary>
    /// Window1.xaml 的交互逻辑
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void title_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton== MouseButtonState.Pressed)
            {
                this.DragMove();
            }
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

解决拖动问题

(1)首先就是最大化遮挡任务栏问题,可以通过重写OnStateChanged事件,在最大化时设置窗体边框以及最大允许尺寸(最大尺寸由工作区域+16组成,16的数字来源于经验),但是由于该事件需要激活,所以需要在Loaded空调用一次。

namespace WpfApp1
{
    /// <summary>
    /// Window1.xaml 的交互逻辑
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            this.Loaded += this.Window1_Loaded;
        }

        private void Window1_Loaded(object sender, RoutedEventArgs e)
        {
            OnStateChanged(null);
        }

        private void title_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton== MouseButtonState.Pressed)
            {
                this.DragMove();
            }
        }
        protected override void OnStateChanged(EventArgs e)
        {
            switch (this.WindowState)
            {
                case WindowState.Maximized:
                    this.MaxWidth = SystemParameters.WorkArea.Width + 16;
                    this.MaxHeight = SystemParameters.WorkArea.Height + 16;
                    this.BorderThickness = new Thickness(5); //最大化后需要调整

                    break;

                case WindowState.Normal:
                    this.BorderThickness = new Thickness(0);
                    this.MaxWidth = SystemParameters.WorkArea.Width + 16;
                    this.MaxHeight = SystemParameters.WorkArea.Height + 16;
                    break;
            }
        }
    }

   
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

(2)其次解决最大化时拖动问题,我们可以考虑在最大化时拖动时,先设置WindowState为Normal,但是直接这样做会导致鼠标和拖动点之间存在很大间距,所以还需要设置窗体位置。

namespace WpfApp1
{
    /// <summary>
    /// Window1.xaml 的交互逻辑
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            this.Loaded += this.Window1_Loaded;
        }

        private void Window1_Loaded(object sender, RoutedEventArgs e)
        {
            OnStateChanged(null);
        }

        private void title_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                if (this.WindowState == WindowState.Maximized)
                {
                    WindowState = WindowState.Normal;
                    Point point = e.MouseDevice.GetPosition(this);
                    Left = point.X - this.title.ActualWidth * point.X / SystemParameters.WorkArea.Width;
                    Top = point.Y - this.title.ActualHeight * point.Y / SystemParameters.WorkArea.Height;
                }

                this.DragMove();
            }
        }
        protected override void OnStateChanged(EventArgs e)
        {
            switch (this.WindowState)
            {
                case WindowState.Maximized:
                    this.MaxWidth = SystemParameters.WorkArea.Width + 16;
                    this.MaxHeight = SystemParameters.WorkArea.Height + 16;
                    this.BorderThickness = new Thickness(5); //最大化后需要调整

                    break;

                case WindowState.Normal:
                    this.BorderThickness = new Thickness(0);
                    this.MaxWidth = SystemParameters.WorkArea.Width + 16;
                    this.MaxHeight = SystemParameters.WorkArea.Height + 16;
                    break;
            }
        }
    }


}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

窗体功能按钮

窗体的功能按钮是控制窗体的最重要控件。一般的有最小化、正常化切换、最大化三种功能按钮。但是不能直接在title里面直接部署这三个按钮,因为这样的话按钮也会响应拖动命令,这是我们所不希望的。所以必须重新对窗体头布局。然后对按钮注册click事件。
在这里插入图片描述

<Window x:Class="WpfApp1.Window1"
        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:WpfApp1"
        mc:Ignorable="d"
        WindowStyle="None"
        Title="Window1" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition Width="90"/>
            </Grid.ColumnDefinitions>
            <Grid x:Name="title" Background="Transparent" MouseMove="title_MouseMove">
            </Grid>
            <StackPanel Grid.Column="1" Orientation="Horizontal">
                <Button Content="" Width="30" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Click="Button_Click"/>
                <Button Content="" Width="30" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Click="Button_Click_1"/>
                <Button Content="X" Width="30" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Click="Button_Click_2"/>
            </StackPanel>
        </Grid>
       
        <Grid x:Name="body" Grid.Row="1">

        </Grid>
    </Grid>
</Window>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
namespace WpfApp1
{
    /// <summary>
    /// Window1.xaml 的交互逻辑
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            this.Loaded += this.Window1_Loaded;
        }

        private void Window1_Loaded(object sender, RoutedEventArgs e)
        {
            OnStateChanged(null);
        }

        private void title_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                if (this.WindowState == WindowState.Maximized)
                {
                    WindowState = WindowState.Normal;
                    Point point = e.MouseDevice.GetPosition(this);
                    Left = point.X - this.title.ActualWidth * point.X / SystemParameters.WorkArea.Width;
                    Top = point.Y - this.title.ActualHeight * point.Y / SystemParameters.WorkArea.Height;
                }

                this.DragMove();
            }
        }
        protected override void OnStateChanged(EventArgs e)
        {
            switch (this.WindowState)
            {
                case WindowState.Maximized:
                    this.MaxWidth = SystemParameters.WorkArea.Width + 16;
                    this.MaxHeight = SystemParameters.WorkArea.Height + 16;
                    this.BorderThickness = new Thickness(5); //最大化后需要调整

                    break;

                case WindowState.Normal:
                    this.BorderThickness = new Thickness(0);
                    this.MaxWidth = SystemParameters.WorkArea.Width + 16;
                    this.MaxHeight = SystemParameters.WorkArea.Height + 16;
                    break;
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            this.WindowState = WindowState.Minimized;
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            if (this.WindowState == WindowState.Normal)
            {
                this.WindowState = WindowState.Maximized;
            }
            else
            {
                this.WindowState = WindowState.Normal;
            }
        }

        private void Button_Click_2(object sender, RoutedEventArgs e)
        {
            this.Close();
        }
    }


}



  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80

使用RRQMSkin创建无边框窗体

实际上以上代码只能实现比较简单的功能,并不完全具备普适性,所以无边框窗体已经被本猿封装到了RRQMSkin中,如果大家想直接使用的话直接在Nuget搜索RRQMSkin即可,具体操作如下:
在这里插入图片描述
在这里插入图片描述
添加好引用后,在主窗体替换继承类为RRQMWindow即可(包括后台代码)。

<window:RRQMWindow x:Class="WpfApp1.MainWindow"
                   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:local="clr-namespace:WpfApp1"
                   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                   xmlns:window="若汝棋茗_Windows"
                   Height="450"
                   Width="800"
                   mc:Ignorable="d">
    <Grid />
</window:RRQMWindow>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
namespace WpfApp1
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : RRQMSkin.Windows.RRQMWindow
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

结束

OK,一个无边框窗体就创建完成了,下节说创建圆角窗体。

最后希望这篇博客能帮到各位猿友,当然如果还有什么不明白的,可以私信本猿哦,QQ群:234762506

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/511838
推荐阅读
相关标签
  

闽ICP备14008679号