WPF拖放控件

拖放官方文档

拖放操作通常涉及两个参与方:拖动对象所源自的拖动源和接收放置对象的拖放目标。 拖动源和放置目标可能是相同应用程序或不同应用程序中的 UI 元素。

我这里实现的是对TabControl的Tab页面进行拖放,以达成类似Chrome浏览器的拖放功能。

对拖放相关事件研究后发现,最终只需要在拖动源事件处理程序中调用 DoDragDrop 方法启动拖动操作,然后在拖放目标上,响应Drop事件。启动拖放操作的拖动源事件通常是 MouseMove事件,另外需要判断鼠标左键是否为按下状态。另外,最好判断一下鼠标移动的距离是否大于最小拖动距离(在我没有添加这个检查之前,双击也会触发拖动事件)。

除了正常拖动(将Tab页从一个TabControl 拖动到另一个 TabControl 中),考虑将Tab页拖动到窗口之外的情况(浏览器在这种情况下会创建一个新的窗口来显示这个Tab页),我这里采用类似的方式,创建一个预设的子窗口来显示该Tab页面。

此外,当Tab页面在原TabControl内部拖动时,根据拖动位置,调整Tab页面的顺序。

我这里对原生的TabControl进行了继承封装了几个响应事件,轻量化的实现了拖放效果。

TabControlDragable.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;namespace WpfLibrary;public class TabControlDragable : TabControl
{public TabControlDragable(){AddHandler(MouseLeftButtonDownEvent, new MouseButtonEventHandler(OnMouseLeftButtonDown), true);AddHandler(MouseLeftButtonUpEvent, new MouseButtonEventHandler(OnMouseLeftButtonUp), true);AddHandler(MouseMoveEvent, new MouseEventHandler(OnMouseMove), true);AddHandler(DropEvent, new DragEventHandler(OnDrop), true);MinWidth = 100;MinHeight = 100;AllowDrop = true;}private bool IsDraging = false;private object? DragData;private Point DragStartPosition;private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e){Point p = e.GetPosition(this);DragData = DragUtility.GetDataObjectFromItemsControl(this, p);if (DragData != null){DragStartPosition = p;}e.Handled = true;}private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e){ResetState();e.Handled = true;}private void OnMouseMove(object sender, MouseEventArgs e){if (DragData == null || DragData is not TabItem){return;}Point currentPosition = e.GetPosition(this);if (e.LeftButton == MouseButtonState.Pressed && !IsDraging&& ((Math.Abs(currentPosition.X - DragStartPosition.X) > SystemParameters.MinimumHorizontalDragDistance) || (Math.Abs(currentPosition.Y - DragStartPosition.Y) > SystemParameters.MinimumVerticalDragDistance))){DataObject dataObject = new DataObject();dataObject.SetData("DragData", DragData);var effects = DragDrop.DoDragDrop(this, dataObject, DragDropEffects.Move);if (effects == DragDropEffects.None){if (Items.Count != 1){var tabItem = DragData as TabItem;var parentWindow = DragUtility.GetParentWindow(tabItem);Items.Remove(tabItem);ChildWindow newChildWindow = new ChildWindow();if (parentWindow is ChildWindow){newChildWindow.Owner = parentWindow.Owner;}else{newChildWindow.Owner = parentWindow;}newChildWindow.DataContext = parentWindow?.DataContext;var tabControl = new TabControlDragable();newChildWindow.MyGrid.Children.Add(tabControl);tabControl.Items.Add(tabItem);newChildWindow.Show();}}ResetState();}e.Handled = true;}private int MeasureIndex(TabControl targetTabControl, Point point){double preWidth = 0.0;for (var i = 0; i < targetTabControl.Items.Count; i++){var item = targetTabControl.Items[i] as TabItem;if (item == null){continue;}if (point.Y > item.ActualHeight){return targetTabControl.Items.Count;}if (point.X < preWidth + item.ActualWidth / 2){return i;}else{preWidth += item.ActualWidth;}}return targetTabControl.Items.Count;}private void OnDrop(object sender, DragEventArgs e){var tabItem = e.Data.GetData("DragData") as TabItem;var parentTabControl = tabItem?.Parent as TabControl;if (tabItem == null || parentTabControl == null){return;}var point = e.GetPosition(this);int index = MeasureIndex(this, point);if (this != parentTabControl){var parentWindow = DragUtility.GetParentWindow(parentTabControl);if (parentTabControl.Items.Count == 1){var childWindow = parentWindow as ChildWindow;if (childWindow != null){parentTabControl.Items.Remove(tabItem);this.Items.Insert(index, tabItem);this.SelectedItem = tabItem;childWindow?.Close();}}else{parentTabControl.Items.Remove(tabItem);this.Items.Insert(index, tabItem);this.SelectedItem = tabItem;}}else if (index < parentTabControl.Items.IndexOf(tabItem)){parentTabControl.Items.Remove(tabItem);parentTabControl.Items.Insert(index, tabItem);parentTabControl.SelectedItem = tabItem;}else if (index > parentTabControl.Items.IndexOf(tabItem)){parentTabControl.Items.Remove(tabItem);parentTabControl.Items.Insert(index - 1, tabItem);parentTabControl.SelectedItem = tabItem;}e.Handled = true;}private void ResetState(){IsDraging = false;DragData = null;}
}

使用起来也很简单,只需要将TabControl 替换为 TabControlDragable 即可,事件已经绑定好了。

MainWindow.xaml

<Window x:Class="WpfLibraryTest.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:WpfLibraryTest"xmlns:wpfLibrary="clr-namespace:WpfLibrary;assembly=WpfLibrary"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800"><Grid><Grid.RowDefinitions><RowDefinition Height="*"/><RowDefinition Height="*"/></Grid.RowDefinitions><wpfLibrary:TabControlDragable Grid.Row="0" x:Name="tabControl1"><TabItem Header="tabControl1Item1"><DataGrid ItemsSource="{Binding Path=Accounts}" IsReadOnly="True"></DataGrid></TabItem><TabItem Header="tabControl1Item2"><DataGrid ItemsSource="{Binding Path=Accounts2}" IsReadOnly="True"></DataGrid></TabItem><TabItem Header="tabControl1Item3"><DataGrid ItemsSource="{Binding Path=Accounts3}" IsReadOnly="True"></DataGrid></TabItem></wpfLibrary:TabControlDragable><wpfLibrary:TabControlDragable Grid.Row="1" x:Name="tabControl2"><TabItem Header="tabControl2Item1"><DataGrid ItemsSource="{Binding Path=Accounts}" IsReadOnly="True"></DataGrid></TabItem><TabItem Header="tabControl2Item2"><DataGrid ItemsSource="{Binding Path=Accounts2}" IsReadOnly="True"></DataGrid></TabItem><TabItem Header="tabControl2Item3"><DataGrid ItemsSource="{Binding Path=Accounts3}" IsReadOnly="True"></DataGrid></TabItem></wpfLibrary:TabControlDragable></Grid>
</Window>

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

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

相关文章

【Webpack】webpack scope hoisting

scope hoisting 是 webpack 的内置优化&#xff0c;它是针对模块的优化&#xff0c;在生产环境打包时会自动开启。 在未开启 scope hoisting 时&#xff0c;webpack 会将每个模块的代码放置在一个独立的函数环境中&#xff0c;这样是为了保证模块的作用域互不干扰。 而 scope…

【Centos7+JDK1.8】Jenkins安装手册

一、安装环境 Centos7 JDK1.8 Jenkins-2.346.3 JDK1.8安装以及网络配置等 自行搜索资料解决。 二、卸载历史安装的Jenkins&#xff0c;直接全部复制粘贴下面的命令 service jenkins stop yum -y remove jenkins rpm -e jenkins rpm -ql jenkins rm -rf /etc/sysconfig/je…

解决远程链接的“Gtk-WARNING **: cannot open display;

1.需要检查ssh配置中X11Forwarding是否启用 2.检查本地ssh工具是否启用x11转发&#xff08;下图以mobaxterm为例&#xff09; 3.检查是否有防火墙等其他网络拦截&#xff0c;如果没有特殊需求&#xff0c;这里可以直接将防火墙服务关闭 4.按上诉操作检查后&#xff0c;正常情况…

P2P服务端模型配合 Tool.net P2pServerAsync 类使用

Tool.Net 支持的 P2P 服务器模型实例 说明服务器部分相关代码相关调用实例Tcp版本Udp版本 最后附一张思维图 说明 当前文章&#xff0c;仅是Tool.Net 开源库的一个缩影。本次更新V5.0版本以上提供支持。可以提供简单实现P2P功能用于业务开发。 服务器部分相关代码 完整代码&…

JVM运行时内存:垃圾回收器(Serial ParNew Parallel )详解

文章目录 1. 查看默认GC2. Serial GC : 串行回收3. ParNew GC&#xff1a;并行回收4. Parallel GC&#xff1a;吞吐量优先 1. 查看默认GC -XX:PrintCommandLineFlags&#xff1a;查看命令行相关参数&#xff08;包含使用的垃圾收集器&#xff09;使用命令行指令&#xff1a;ji…

数据库系统概论(超详解!!!)第九节 嵌入式SQL

SQL语言提供了两种不同的使用方式 &#xff1a;交互式&#xff0c; 嵌入式。 SQL语言是非过程性语言 。事务处理应用需要高级语言。 这两种方式细节上有差别&#xff0c;在程序设计的环境下&#xff0c;SQL语句要做某些必要的扩充。 1.嵌入式SQL的处理过程 嵌入式SQL是将SQL…

「51媒体」广西媒体资源,南宁活动媒体邀约

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 广西地区拥有丰富的媒体资源&#xff0c;在广西做活动&#xff0c;参加展览可以邀请他们到场采访报道。 央媒驻站&#xff1a;广西新华 广西人民 广西光明 广西央广 广西国际在线 广西中…

Rhinoceros v7.5 解锁版安装教程 (3D三维造型软件)

前言 Rhinoceros 中文名称犀牛是一款超强的三维建模工具&#xff0c;全称Rhinoceros&#xff0c;Rhino是美国Robert McNeel & Assoc开发的PC上强大的专业3D造型软件&#xff0c;它可以广泛地应用于三维动画制作、工业制造、科学研究以及机械设计等领域。它能轻易整合3DS M…

【数据结构】第七节:堆

个人主页&#xff1a; 深情秋刀鱼-CSDN博客 数据结构专栏&#xff1a;数据结构与算法 源码获取&#xff1a;数据结构: 上传我写的关于数据结构的代码 (gitee.com) ​ 目录 一、堆 1.堆的概念 2.堆的定义 二、堆的实现 1.初始化和销毁 2.插入 向上调整算法 3.删除 向下调整算法…

BioMistral 7B——医疗领域的新方法,专为医疗领域设计的大规模语言模型

1. 概述 自然语言处理领域正在以惊人的速度发展&#xff0c;ChatGPT 和 Vicuna 等大型语言模型正在从根本上改变我们与计算机交互的方式。从简单的文本理解到复杂的问题解决&#xff0c;这些先进的模型展示了类似人类的推理能力。 特别是&#xff0c;BLOOM 和 LLaMA 等开源模…

asp.net core接入prometheus

安装prometheus和Grafana 参考之前的文章->安装prometheus和Grafana教程 源代码 dotnet源代码 新建.net core7 web项目 修改Program.cs using Prometheus;namespace PrometheusStu01;public class Program {public static void Main(string[] args){var builder We…

字符函数:分类函数与转换函数

字符函数 一.字符分类函数二.字符转换函数 在编程的过程中&#xff0c;我们经常要处理字符和字符串&#xff0c;为了方便操作字符和字符串&#xff0c;C语⾔标准库中提供了一系列库函数&#xff0c;接下来我们就学习⼀下这些函数。 一.字符分类函数 C语言中有⼀系列的函数是专门…

自然语言处理实战项目29-深度上下文相关的词嵌入语言模型ELMo的搭建与NLP任务的实战

大家好,我是微学AI,今天给大家介绍一下自然语言处理实战项目29-深度上下文相关的词嵌入语言模型ELMo的搭建与NLP任务的实战,ELMo(Embeddings from Language Models)是一种深度上下文相关的词嵌入语言模型,它采用了多层双向LSTM编码器构建语言模型,并通过各层LSTM的隐藏状…

整理好了!2024年最常见 20 道 Redis面试题(五)

上一篇地址&#xff1a;整理好了&#xff01;2024年最常见 20 道 Redis面试题&#xff08;四&#xff09;-CSDN博客 九、Redis 的哨兵系统是什么&#xff1f; Redis 的哨兵系统&#xff0c;通常被称为 Sentinel&#xff0c;是 Redis 的一种高可用性解决方案1347810。它由一个…

文件流下载优化:由表单提交方式修改为Ajax请求

如果想直接看怎么写的可以跳转到 解决方法 节&#xff01; 需求描述 目前我们系统导出文件时&#xff0c;都是通过表单提交后&#xff0c;接收文件流自动下载。但由于在表单提交时没有相关调用前和调用后的回调函数&#xff0c;所以我们存在的问题&#xff0c;假如导出数据需…

MyBatisPlus使用流程

引入依赖 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.4</version> </dependency> 版本号根据需要选取 在实体类上加注解声明&#xff0c;表信息 根据数…

Redis教程(十):Redis中三种特殊数据类型命令

一、Geospatial Geospatial是地理位置类型&#xff0c;我们可以用来查询附近的人、计算两人之间的距离等。 新增&#xff1a; geoadd city 106.54041 29.40268 chongqing 118.8921 31.32751 nanjing 117.30794 31.79322 hefei 102.82147 24.88554 kunming 91.13775 29.65262…

js实现png纯色填充

最近需要把logo换个填充颜色&#xff0c;写了个页面&#xff0c;效果还行 png颜色填充 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, i…

get和post的区别,二者是幂等的吗?

一、什么是幂等 所谓幂等性通俗的将就是一次请求和多次请求同一个资源产生相同的副作用。 维基百科定义&#xff1a;幂等&#xff08;idempotent、idempotence&#xff09;是一个数学与计算机学概念&#xff0c;常见于抽象代数中。 在编程中一个幂等操作的特点是其任意多次执…

U-Mail邮件系统为用户提供更加安全的数据保护机制

据外媒报道&#xff0c;近日美国国家安全委员会泄露了其成员的近1万封电子邮件和密码&#xff0c;暴露了政府组织和大公司在内的2000家公司。其中包括美国国家航空航天局和特斯拉等。报道称该漏洞于3月7日被研究人员发现&#xff0c;通过该漏洞攻击者能够访问对web服务器操作至…