当前位置: 首页 > news >正文

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><Grid.ColumnDefinitions><ColumnDefinition Width="3*" x:Name="col_mainlist" /><ColumnDefinition Width="3" /><ColumnDefinition Width="1*"  x:Name="col_preview" /></Grid.ColumnDefinitions><!-- 主列表 --><controls:DataGrid Grid.Column="0" x:Name="MainDataList" BorderBrush="Black" BorderThickness="1" Margin="2" Padding="2"Height="Auto" Background="LightGray"GridLinesVisibility="All" HorizontalGridLinesBrush="Blue" VerticalGridLinesBrush="Green" AutoGenerateColumns="True" /><!-- 分隔条 --><Border Grid.Column="1" x:Name="border_split" BorderBrush="Gray" BorderThickness="3" Margin="0,2,0,2" /><!-- 预览区 --><Grid Grid.Column="2" BorderBrush="Black" BorderThickness="1" Margin="2" Padding="2">。。。。。。</Grid></Grid>

         这个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。

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

四、效果

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

 


(这里是文档结束)

http://www.lqws.cn/news/489025.html

相关文章:

  • Java基础(三):逻辑运算符详解
  • 提高WordPress网站加载速度和用户体验
  • C# SolidWorks二次开发-实战2,解决SolidWorks2024转step文件名乱码问题
  • 【25】木材表面缺陷数据集(有v5/v8模型)/YOLO木材表面缺陷检测
  • 【开源工具】一键解决使用代理后无法访问浏览器网页问题 - 基于PyQt5的智能代理开关工具开发全攻略
  • 干货分享 如何做好数据可视化?
  • Qt联合Halcon开发四:【详细图解】海康相机配置并链接测试
  • Zynq + FreeRTOS + YAFFS2 + SQLite3 集成指南
  • Windows电脑数据恢复终极指南:从原理到实战
  • el-cascader 设置可以手动输入也可以下拉选择
  • 性能监控与智能诊断系统的全流程
  • (LeetCode 面试经典 150 题) 27.移除元素
  • Java 类加载机制详解
  • Spring AI 项目实战(十二):Spring Boot +AI + DeepSeek + 百度OCR 公司发票智能处理系统的技术实践(附完整源码)
  • C++11 <array>从入门到精通
  • Git新建分支并同步到远程
  • 终端创建虚拟环境
  • Blazor-内置输入组件
  • 华为云 Flexus+DeepSeek 征文|增值税发票智能提取小工具:基于大模型的自动化信息解析实践
  • 2025 年焊接相机十大品牌测评:抗光耐高温解决方案深度解析
  • Three.js入门第一步:两种方式搭建你的3D项目[特殊字符]️
  • CentOS 上安装snmp
  • mac隐藏文件现身快捷键
  • 从 0 到 1 实现 C++ string 类:深入理解动态字符串的底层机制--《Hello C++ Wrold!》(11)--(C/C++)
  • 编程实践:sigmastar330 调用IVE图像处理加速
  • Linux密码校验机制深度剖析:从shadow文件到crypt加密
  • PicHome结合容器化与内网穿透实现跨平台影像管理
  • 使用docx4j 实现word转pdf(linux乱码处理)
  • 支持java8的kafka版本
  • Halcon ——— OCR字符提取与多类型识别技术详解