WPF TreeView拖动排序拖拽排列

news2024/11/25 0:58:07

底部附有Demo示例。需要的朋友可以去下载参考

一、图示

先上图,不知为啥,GIF总看起来特别卡,实际却很流畅。

由于录制问题,GIF动画只会播放一次,需要重复观看的,请将网页关闭后重新打开再观看

WPF的资料太少了,找些动画什么的都没有,最近工作中要用Treeview进行拖动排序,但是呢,网上几乎没有相关的Demo示例等,只能自己手撸,在这过程中,也学到了一些知识,我在此记录下,防止下次用到,或者需要的小伙伴用到。

二、 原理

我是利用控件的平移实现拖动效果的。当平移到边界的时候,会触发TreeView的上下滚动(滚动的时候也会平移);脱离边界则停止滚动;滚到最上和最下时,停止平移;当手指抬起来时,捕获当前鼠标的位置,将拖动的Item插入到当前鼠标位置的Item前面,同时删除数据集合旧的数据,添加新的数据到添加的位置

技术点:

1. TreeView代码控制滚动

2. 控件拖动,平移

3. 获取控件在其他父控件中的相对位置

4. 利用鼠标事件做平移,和拖动结束后的排序

三、代码解析

首先我们将布局写好,布局就是一个普通的TreeView。不过我们需要给控件添加一个MouseMove事件,该事件用来判断是否鼠标移动到了边界,然后做响应的自动滚动动作。

<Window x:Class="WpfAppTest.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfAppTest"
        mc:Ignorable="d"
        Loaded="OnViewLoaded"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <TreeView x:Name="mTreeView" Width="300" HorizontalAlignment="Left" MouseMove="ParentMove"/>
    </Grid>
</Window>

接下来在初始化的时候,我们将TreeView的数据添加到Dictionary里面,再根据Dictionary的数据初始化TreeView的Item。

private void initData()
{
	datas["A"] = new List<string>();
	datas["A"].Add("阿轲");
	datas["A"].Add("艾琳");
	datas["A"].Add("安其拉");

	datas["B"] = new List<string>();
	datas["B"].Add("白起");
	datas["B"].Add("百里守约");
	datas["B"].Add("扁鹊");
	datas["B"].Add("百里玄策");
	datas["B"].Add("不知火舞");

	datas["C"] = new List<string>();
	datas["C"].Add("蔡文姬");
	datas["C"].Add("曹操");
	datas["C"].Add("陈咬金");
	datas["C"].Add("嫦娥");
	datas["C"].Add("成吉思汗");

	datas["D"] = new List<string>();
	datas["D"].Add("狄仁杰");
	datas["D"].Add("典韦");
	datas["D"].Add("貂蝉");
	datas["D"].Add("盾山");
	datas["D"].Add("东皇太一");
	datas["D"].Add("妲己");
	datas["D"].Add("大乔");
	datas["D"].Add("达摩");

	datas["G"] = new List<string>();
	datas["G"].Add("宫本武藏");
	datas["G"].Add("高渐离");
	datas["G"].Add("鬼谷子");
	datas["G"].Add("干将莫邪");
	datas["G"].Add("关羽");
	datas["G"].Add("公孙离");

	datas["H"] = new List<string>();
	datas["H"].Add("后裔");
	datas["H"].Add("韩信");
	datas["H"].Add("花木兰");
	datas["H"].Add("黄忠");
}

private void initViews()
{
	foreach (KeyValuePair<string, List<string>> kv in datas)
	{
		TreeViewItem parentItem = new TreeViewItem();
		parentItem.Header = kv.Key;
		parentItem.Tag = kv.Value;
		mTreeView.Items.Add(parentItem);
		foreach (string info in kv.Value)
			parentItem.Items.Add(creatItem(info));
	}
}

private TreeViewItem creatItem(object header)
{
	TreeViewItem newItem = new TreeViewItem();
	newItem.Header = header;
	newItem.PreviewMouseDown += ItemMouseDown;
	newItem.PreviewMouseMove += ItemMove;
	newItem.PreviewMouseUp += ItemMouseUp;
	newItem.Height = 30;
	return newItem;
}

视图和数据准备好后,旧开始准备做拖动的动作,我上面给Item添加了3个事件,分别是PreviewMouseDown,PreviewMouseMove,PreviewMouseUp。这3个事件就是我们拖动Item的关键了

PreviewMouseDown:获取鼠标点击位置,作为移动的初始位置。初始化平移对象transform

PreviewMouseMove:根据鼠标的移动,对Item进行平移

PreviewMouseUp:鼠标抬起时捕获要插入的位置的Item,并做Item插入

上面我们不是还添加了TreeView的MouseMove事件吗,这个事件主要是边缘检测,也会在这里开启自动滚动

PreviewMouseDown事件的代码比较简单,lastPosition记录了上次移动的坐标,用来计算鼠标移动了多远,我们就将Item移动多远。_isMouseDown是拖动的标记,只有按下时,才会计算鼠标移动的距离,transform是我们用来拖动Item的对象

private void ItemMouseDown(object sender, MouseButtonEventArgs e)
{
	lastPosition = 0;
	var c = sender as UIElement;
	_isMouseDown = true;
	TreeViewItem treeViewItem = (TreeViewItem)sender;
	mouseDownOffset = e.GetPosition(treeViewItem).Y + 5;
	_mouseDownPosition = e.GetPosition(this);
	var transform = c.RenderTransform as TranslateTransform;
	if (transform == null)
	{
		transform = new TranslateTransform();
		c.RenderTransform = transform;
	}
	c.CaptureMouse();
}

PreviewMouseMove事件计算鼠标移动的距离,然后将Item也拖动响应的距离。然后将本次的Y轴位置服给lastPosition,以便下次计算使用。

e.GetPosition(UIElement) 是获取鼠标在控件中的位置,要要获取哪个控件的相对位置,就控件作为参数传给GetPosition,就可以获取位置。Position和Position之间可以做加减法,计算两个坐标之间的直线距离

if (_isMouseDown && !isRollingItem[0])
{
	TreeViewItem viewItem = (TreeViewItem)sender;
	dragingItemHeight = viewItem.ActualHeight;
	var c = sender as UIElement;
	var pos = e.GetPosition(this);
	var dp = pos - _mouseDownPosition;
	transform = c.RenderTransform as TranslateTransform;
	//transform.X = _mouseDownControlPosition.X + dp.X;
	//transform.Y = _mouseDownControlPosition.Y + dp.Y;
	transform.Y = transform.Y + (dp.Y - lastPosition);
	lastPosition = dp.Y;
}

PreviewMouseUp事件就直接计算当前位置,将拖动的Item插入到当前位置。

我在项目中使用控件时,发现向上拖动会在其他Item上面,向下拖动会在Item底下(被遮挡)。当然我的Item是我自定义的布局。 当鼠标弹起时,并不能及时捕获鼠标在哪个Item上停留,需要等待大约100毫秒后才能获取到。所以遇到问题还得变通下

下面我们看下TreeView自动滚动的代码,这个比较重要,需要TreeView滚动的同时,拖动的Item也要平移

private void ScrollingOnMainThread(object obj)
{
	bool isDownScroll = (bool)obj;
	if (isDownScroll)
	{
		if (scrollViewer.VerticalOffset >= (scrollViewer.ExtentHeight - mTreeView.ActualHeight)) return;
		scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + 6);    //向下调节垂直滚动条的位置;
		transform.Y += 6;
	}
	else
	{
		if (scrollViewer.VerticalOffset <= 0) return;
		scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - 6);    //向上调节垂直滚动条的位置;
		transform.Y -= 6;
	}
}

重要的也就这些,现在你的控件应该也能动起来了。

如果你还需要demo示例参考或查看效果。请查看链接

WPFTreeView拖动排序拖拽排列-桌面系统文档类资源-CSDN下载

本文代码实现简单,扩展性强,Demo示例总共也就267行,代码还包含了数据初始化,View控件初始化等非逻辑代码,代码简单以维护。 

如果在使用中有不懂的,您还可以通过私信询问,我经常在线的,帮您解答使用中的困难。 Demo示例有可能不太完善,如果您发现问题,可以提出来大家共同学习,将项目完善起来,本文试我手撸的代码,如果您喜欢,还请点赞支持下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/89413.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自动化运维与普通运维有什么区别?

本文首发于知乎&#xff0c;由嘉为蓝鲸原创。 商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 01. 何为自动化运维&普通运维&#xff1f; 在了解两者的区别前&#xff0c;我们得先明确对二者的定义&#xff0c;总的来说运维工作的目的都是为了保障企业业…

Gateway简介

文档&#xff1a;Spring Cloud Gateway 小结&#xff1a; nacos &#xff1a;注册中心&#xff0c;解决服务的注册与发现 nacos &#xff1a;配置中心&#xff0c;配置文件中心化管理 Ribbon&#xff1a;客户端负载均衡器&#xff0c;解决微服务集群负载均衡的问题 Openfeign…

网络工程毕业设计 SSM游戏攻略资讯补丁售卖系统(源码+论文)

文章目录1 项目简介2 实现效果2.1 界面展示3 设计方案3.1 概述3.2 系统流程3.3 系统结构设计4 项目获取1 项目简介 Hi&#xff0c;各位同学好呀&#xff0c;这里是M学姐&#xff01; 今天向大家分享一个今年(2022)最新完成的毕业设计项目作品&#xff0c;【基于SSM的游戏攻略…

容器安全——云原生中镜像容器全生命周期防护思路

文章目录容器简介容器镜像传统安全面对容器安全的表现无法匹配动态IP无法侦测到容器内的各种行为无法扫描各种分层存储的镜像文件无法适应新的容器引擎和框架镜像容器全生命周期的安全防护Build防护Ship防护Run防护容器简介 我们都知道&#xff0c;VM是通过Hypervisor虚拟化技…

没有备份电脑照片怎么恢复?分享3种找回照片方法

随着科学技术的不断发展&#xff0c;越来越多的人喜欢使用手机或相机进行拍摄&#xff0c;但是这些设备存储照片量多后&#xff0c;容易导致卡顿甚至无法拍摄的情况发生&#xff0c;对此&#xff0c;婚纱/新闻/美食/广告摄影等职业人员&#xff0c;会习惯性将这些设备上的照片导…

VR如何改变办公场所,听一听Meta的预测

近期&#xff0c;为了更好的了解人们对于元宇宙、VR的看法&#xff0c;以及这对办公场景带来的影响&#xff0c;Meta对全球2000多名企业员工和400名企业领导进行调研。调查结果显示&#xff0c;66%的受调查者认为VR有助于还原线下办公的团结感&#xff08;togetherness&#xf…

Linux系统Initramfs机制

概述 现代Linux系统都使用到了一种功能比较特殊的微型系统&#xff0c;作为Linux内核初始化完成但未进入最终系统时的过渡系统&#xff0c;主要的目的是为了将最终系统载入到根文件系统上&#xff0c;辅助内核启动最终系统&#xff0c;可以称之为“辅助系统”。 辅助系统的作…

【C语言数据结构(基础篇)】第三站:链表(一)

目录 一、动态顺序表的缺陷以及链表的引入 1.动态顺序表的缺陷&#xff0c;以及链表的引入 2. 链表的概念 3.链表的声明 4.链表的逻辑结构与物理结构 二、单链表的实现 1.单链表的创建 2.单链表的打印 3.单链表的尾插 4.单链表的头插 5.单链表的头删、尾删 6.查找链表…

WPF入门第四篇 WPF模板

WPF模板 1、ControlTemplate 上一篇已经试用过控件模板&#xff0c;我们知道WPF的控件都是继承自Control&#xff0c;在Control类中有一个Template属性&#xff0c;类型就是ControlTemplate。那么利用这个ControlTemplate就可以彻底的颠覆控件的默认外观。 <Window x:Cla…

如何使用Unity ARFoundation和XR Interaction Toolkit对Prefab进行选择、缩放、移动、和旋转操作?

本文分享一种很常见的AR体验的实现。这种AR体验即&#xff0c;手机相机检测到指定图片/平面/实物之后&#xff0c;虚拟模型随之出现&#xff0c;并允许用户在屏幕上使用手势&#xff08;例如双指捏合&#xff09;对该虚拟模型进行选择、缩放、移动、和旋转操作。 这种体验有很…

【Spring】——14、如何使用@Value注解为bean的属性赋值呢?

&#x1f4eb;作者简介&#xff1a;zhz小白 公众号&#xff1a;小白的Java进阶之路 专业技能&#xff1a; 1、Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理 2、熟悉Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理&#xff0c;具备⼀定的线…

flask之g对象、flask-session使用、数据库连接池、信号

目录 g对象 flask-session的使用 数据库连接池 flask中集成mysql wtfroms使用(了解) 信号 g对象 全称global&#xff0c;是一个全局对象在此次请求过程中一直有效&#xff0c;其实就是请求的上下文从请求进来就一直存在直到请求结束&#xff0c;所以在当次请求过程中&…

华为云WeLink云空间,企业的多啦A梦「百宝袋」办公助手

我们知道&#xff0c;源自华为19万员工的数字化办公实践的华为云WeLink&#xff0c;作为新一代智能工作平台、远程办公平台、移动办公平台、协同办公软件&#xff0c;已经给成为企业数字化转型的连接器。今天&#xff0c;我们来聊一聊WeLink提供的一项优质服务——云空间。 We…

论文复现-2代码研读:Black-Box Tuning for Language-Model-as-a-Service

第一步&#xff1a;将作者所给代码跑通。 下载代码&#xff0c;放置在本地文件夹。 报错问题一&#xff1a; 使用hugging face 中loaddataset函数报错。显示connect error。 修改如下&#xff1a;将数据集下载文件.py文件在本地&#xff0c;然后从.py文件中加载数据集。 解决…

【深度学习】PyTorch深度学习实践 - Lecture_13_RNN_Classifier

文章目录一、问题描述二、OurModel三、准备数据3.1 Data Convert3.2 Padding Data3.3 Label Convert四、双向RNN五、PyTorch代码实现5.1 引入相关库5.2 创建Tensors函数5.3 将名字转化为字符列表函数5.4 国家名字数据集对象5.5 RNN&#xff08;GRU&#xff09;分类器对象5.6 训…

Nacos--多环境的实现方案

原文网址&#xff1a;Nacos--多环境的实现方案_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍Nacos实现多环境的方案。 方案概述 多环境有很多方案&#xff0c;如下&#xff1a; 单租户方案&#xff08;适用于项目很少的场景&#xff09; 命名空间区分环境&#xff0c;GR…

Python简介

Python简介 目录1. 概述2. 安装3. 编译器4. 注释5. 缩进6. 编码规范7. 基本输入输出使用print()函数输出使用input()函数输入8. 练习1. 概述 Python的中文意思是蟒蛇&#xff0c;python是一种面向对象的解释型的计算机程序设计语言。支持面向过程&#xff0c;面向对象&#xff…

(十四)Vue之收集表单数据

文章目录v-model的三个修饰符收集文本框收集单选按钮收集复选框收集下拉列表收集文本域演示程序Vue学习目录 上一篇&#xff1a;&#xff08;十三&#xff09;Vue之监测数据改变的原理 v-model的三个修饰符 v-model的三个修饰符&#xff1a; lazy&#xff1a;失去焦点再收集…

MIT6.830-2022-lab2实验思路详细讲解

目录一、Exercise1.1、Exercise1&#xff1a; Filter and Join1.2、Exercise2&#xff1a; Aggregates1.3、Exercise 3&#xff1a;HeapFile Mutability1.4、Exercise 4&#xff1a;Insertion and deletion1.5、Exercise 5&#xff1a; Page eviction二、总结一、Exercise 1.1…

人工智能课后作业_python实现A*算法实现8数码问题(附源码)

3 A*算法实现8数码问题 3.1算法介绍3.2实验代码3.3实验结果3.4实验总结 3.1算法介绍 Astar算法是一种求解最短路径最有效的直接搜索方法&#xff0c;也是许多其他问题的常用启发式算法。它的启发函数为f(n)g(n)h(n),其中&#xff0c;f(n) 是从初始状态经由状态n到目标状态的…