WinUI3入门9:自制SplitPanel

初级代码游戏的专栏介绍与文章目录-CSDN博客

我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。

这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。

源码指引:github源码指引_初级代码游戏的博客-CSDN博客

C#是我多年以来的业余爱好,新搞的东西能用C#的就用C#了。


        WinUI3里面有个SplitView,实现了两块面板,但是是不带鼠标拖动改变大小的功能的,其设计目标不是两块面板,而是根据需要隐藏一个面板。

        一般来说,移动设备上确实不需要用户改变界面比例,但是你是WinUI3啊。所以说微软脑子里都是些什么啊,不如大家都散了算了。

        因为写Winforms程序的时候我非常依赖手动调整界面比例,对于不能自主调整的完全不能忍,所以,我必须实现一个SplitPanel。

目录

一、设计SplitPanel的基本原理

二、编写xaml

三、处理鼠标事件

3.1 初始化

3.2 鼠标事件

四、效果


一、设计SplitPanel的基本原理

        通过鼠标改变界面大小的功能总是涉及到这些技术:

  • 动态设置控件/窗口的位置和尺寸,当然这没什么难度
  • 处理鼠标事件,根据鼠标位置计算鼠标移动的距离,从而改变界面比例
  • 捕获鼠标,为什么需要捕获鼠标?因为鼠标移出控件/窗口之后就无法获得鼠标事件,这样能实现缩小但是无法实现变大。而捕获鼠标就是告诉操作系统即使鼠标移出也仍然要发送鼠标事件

        结合以上几点,一般实现鼠标改变界面大小的实现方法是:

  • 在鼠标按下时记录鼠标位置,捕获鼠标
  • 在鼠标移动时根据鼠标移动距离改变界面大小
  • 在鼠标释放时停止捕获鼠标

        在WinUI3里面我们可以借助Grid来实现:

左面板 分隔条 右面板

        分隔条用Border就可以了,宽度2-3像素,1个像素很难点击,7、8个像素又太粗壮不好看。

        左面板、右面板任意。

二、编写xaml

		
			
				
				
				
			
		
			
			

			
			
			
			
			
				。。。。。。
			
		

         这个Grid只有一行,所以没有定义行。有三个列,分别对应左面板、分隔条、右面板。分隔条固定为3个像素,左面板和右面板为3:1分配。左面板是DataGrid,右面板是个子Grid。

        左右面板都有x:Name,因为我们需要同时改变左右面板的大小。你说改变DataGrid和子Grid的大小,让总Grid自适应行不行?这种自适应的东西很诡异,我是不太放心的。

        用作分隔条的Border上也有x:Name,我们设置鼠标事件都在它上面。

        这段xaml中的主列表和预览区是我实际的内容,和要实现的SplitPanel功能没有任何关系。

三、处理鼠标事件

3.1 初始化

			//设置拖动手柄
			border_split.PointerPressed += OnPointerPressed;
			border_split.PointerReleased += OnPointerReleased;

        在窗口的构造函数里增加上述代码。为什么没有设置PointerMoved事件?因为没有按下鼠标的时候处理PointerMoved是没有意义的。

3.2 鼠标事件

		private void OnPointerMoved(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
		{
			FrameworkElement? control = sender as FrameworkElement;
			if (null == control) return;

			try
			{
				foreach (var pointer in control.PointerCaptures)
				{
					//control.PointerCaptures.Contains(e.Pointer)是无效的,必须用PointerId来比较
					if (pointer.PointerId== e.Pointer.PointerId)
					{
						ChangeControlSize(control, e);
					}
				}
			}
			catch (Exception ex)
			{
				_ = ShowMessageBox(ex.Message, "Exception");
			}
		}

		private void OnPointerReleased(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
		{
			FrameworkElement? control = sender as FrameworkElement;
			if (null == control) return;

			try
			{
				foreach (var pointer in control.PointerCaptures)
				{
					//control.PointerCaptures.Contains(e.Pointer)是无效的,必须用PointerId来比较
					if (pointer.PointerId == e.Pointer.PointerId)
					{
						control.PointerMoved -= OnPointerMoved; ;
						this.Title = "PointerReleased";
						control.ReleasePointerCapture(e.Pointer);
					}
				}
			}
			catch (Exception ex)
			{
				_ = ShowMessageBox(ex.Message, "Exception");
			}
		}
		private void OnPointerPressed(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
		{
			FrameworkElement? control = sender as FrameworkElement;
			if (null == control) return;

			if (!control.CapturePointer(e.Pointer))
			{
				this.Title = "CapturePointer error";
				return;
			}
			control.PointerMoved += OnPointerMoved; ;
			pos = e.GetCurrentPoint(control).Position.X;
			this.Title = "PointerPressed " + pos.ToString() + " - " + control.ActualWidth.ToString()+ " control.PointerCaptures "+ control.PointerCaptures.Count.ToString();
		}

		double pos = 0;
		private void ChangeControlSize(FrameworkElement? control, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
		{
			if (null == control) return;
			double move = e.GetCurrentPoint(control).Position.X - pos;
			if (move >= 0)
			{
				if (col_preview.ActualWidth - move < 0) return;
			}
			else
			{
				if (col_mainlist.ActualWidth + move < 0) return;
			}
			col_preview.Width = new GridLength(col_preview.ActualWidth - move);
			col_mainlist.Width = new GridLength(col_mainlist.ActualWidth + move);
		}

        这段代码还是比较严谨的,左右面板的名字可以随意替换。唯一不太严谨的是记录鼠标初始位置的pos,一般不建议全局变量用这样简单的方式命名。

        这个代码里面有一部分没有完善处理:假设了只有一个鼠标,但仍然判断了是否正在进行鼠标捕获(根据这个假设和鼠标按下才处理鼠标移动,并不需要检查是否是正在捕获的鼠标)。严谨的方式是根据鼠标ID来分别处理,设想,如果有两个鼠标同时操作会怎么样?

        处理是否是正在捕获的鼠标的时候遇到了一个坑,我已经在代码里指出。并不能简单地用鼠标对象是否在集合中来判断,必须一个一个比较ID。

        这段代码只处理了宽度,如果要做纵向的,稍微修改就可以了。

四、效果

WinUI3入门9:自制SplitPanel_第1张图片

WinUI3入门9:自制SplitPanel_第2张图片

        差不多就是这个意思了。当然,最好能把鼠标改成专门的鼠标形状。

 


(这里是文档结束)

你可能感兴趣的:(WinUI,WinUI,C#,SplitPanel,拉伸)