浅谈WPF之控件拖拽与拖动

使用过office的visio软件画图的小伙伴都知道,画图软件分为两部分,左侧图形库,存放各种图标,右侧是一个画布,将左侧图形库的图标控件拖拽到右侧画布,就会生成一个新的控件,并且可以自由拖动。那如何在WPF程序中,实现类似的功能呢?今天就以一个简单的小例子,简述如何在WPF中实现控件的拖拽和拖动,仅供学习分享使用,如有不足之处,还请指正。

涉及知识点

WPF控件的拖拽与拖动,主要涉及知识点如下所示:

  • 容器布局,本示例采用左右布局,主容器采用Grid并分成两列进行布局,左侧图标库采用UniformGrid布局,右侧画布采用Canvas布局。
  • 控件拖拽,当图标库中的图标控件被鼠标按下时,通过调用 DragDrop.DoDragDrop方法实现拖拽功能,并且设置画布的AllowDrop属性为true,并触发拖拽松开事件
  • 控件拖动,当图标库中的图标拖拽到新画布容器后,就会生成一个新的控件,通过属性按下事件,鼠标移动事件,鼠标升起事件,来实现控件的拖动

实现步骤

1. 页面布局

根据布局说明,页面分为左右两部分【Grid容器】,左侧图标库【UniformGrid】,右侧画布【Canvas】,如下所示:

<Window x:Class="DemoDragAndDrop.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:i="http://schemas.microsoft.com/xaml/behaviors"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:DemoDragAndDrop"mc:Ignorable="d"Title="MainWindow" Height="450" Width="800"><i:Interaction.Triggers><i:EventTrigger EventName="Loaded"><i:InvokeCommandAction Command="{Binding WinLoadedCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"/></i:EventTrigger></i:Interaction.Triggers><Grid><Grid.ColumnDefinitions><ColumnDefinition Width="Auto"></ColumnDefinition><ColumnDefinition Width="*"></ColumnDefinition></Grid.ColumnDefinitions><Border Grid.Column="0" BorderBrush="LightGray" BorderThickness="1"></Border><Border Grid.Column="1" BorderBrush="LightGray" BorderThickness="1"></Border><UniformGrid Grid.Column="0" Columns="2" VerticalAlignment="Top"><UniformGrid.Resources><Style TargetType="TextBlock"><Setter Property="Width" Value="100"></Setter><Setter Property="Height" Value="40"></Setter><Setter Property="FontSize" Value="18"></Setter><Setter Property="TextAlignment" Value="Center"></Setter><Setter Property="Padding" Value="10"></Setter><Setter Property="Margin" Value="5"></Setter><Setter Property="Background" Value="Blue"></Setter><Setter Property="FontWeight" Value="Bold"></Setter><Setter Property="Foreground" Value="White"></Setter></Style></UniformGrid.Resources><TextBlock Text="文本" Tag="Text"><i:Interaction.Triggers><i:EventTrigger EventName="MouseLeftButtonDown"><i:InvokeCommandAction Command="{Binding IconMouseLeftDownCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TextBlock}}"/></i:EventTrigger></i:Interaction.Triggers></TextBlock><TextBlock Text="按钮" Tag="Button"><i:Interaction.Triggers><i:EventTrigger EventName="MouseLeftButtonDown"><i:InvokeCommandAction Command="{Binding IconMouseLeftDownCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=TextBlock}}"/></i:EventTrigger></i:Interaction.Triggers></TextBlock><TextBlock Text="单选按钮"></TextBlock><TextBlock Text="复选按钮"></TextBlock><TextBlock Text="圆形"></TextBlock><TextBlock Text="长方形"></TextBlock><TextBlock Text="直线"></TextBlock><TextBlock Text="三角形"></TextBlock></UniformGrid><Canvas x:Name="container" Grid.Column="1" AllowDrop="True" Background="White"><i:Interaction.Triggers><i:EventTrigger EventName="Drop"><i:InvokeCommandAction Command="{Binding CanvasDropCommand}" PassEventArgsToCommand="True"/></i:EventTrigger></i:Interaction.Triggers></Canvas></Grid>
</Window>

 

注意,在页面布局中,为图标库中的图标绑定了MouseLeftButtonDown事件命令,当鼠标左键按下时触发对应的事件,并开始拖拽。如下所示:

private ICommand  iconMouseLeftDownCommand;public ICommand IconMouseLeftDownCommand
{get {if (iconMouseLeftDownCommand == null){iconMouseLeftDownCommand = new RelayCommand<object>(IconMouseLeftDown);}return iconMouseLeftDownCommand; }
}private void IconMouseLeftDown(object sender)
{var tag = (sender as TextBlock)?.Tag?.ToString();if (tag == null){return;}var data = new DragDropData() { Tag = tag };//开启准备拖动操作DragDrop.DoDragDrop((DependencyObject)sender, data, DragDropEffects.Copy);
}

 

注意,在调用DragDrop.DoDragDrop方法开始拖拽时,此方法有三个参数【DoDragDrop(DependencyObject dragSource, object data, DragDropEffects allowedEffects)】,说明如下:

  • 第一个参数是拖拽源控件。
  • 第二个参数用于传递数据,可以传递参数,用于区分详细信息。
  • 第三个参数是拖拽效果

在画布容器中松开拖拽的鼠标左键时,触发画布Drop事件,在此事件中创建新的控件,如下所示:

private ICommand canvasDropCommand;public ICommand CanvasDropCommand
{get {if (canvasDropCommand == null){canvasDropCommand = new RelayCommand<DragEventArgs>(CanvasDrop);}return canvasDropCommand; }
}private void CanvasDrop(DragEventArgs e)
{var data = e.Data.GetData(typeof(DragDropData)) as DragDropData;if (data != null){var position = e.GetPosition(this.containerCanvas);if (data.Tag == "Text"){//创建文本Border border = new Border();border.BorderThickness = new Thickness(1);border.BorderBrush = Brushes.Black;TextBlock text = new TextBlock(){Width = 120,Height = 30,Text = "文本1",FontSize = 14,Background = Brushes.LightGray,TextAlignment = TextAlignment.Center,Padding = new Thickness(5)};border.Child = text;border.MouseDown += Container_Control_MouseDown;border.MouseMove += Container_Control_MouseMove;border.MouseUp += Container_Control_MouseUp;this.containerCanvas.Children.Add(border);Canvas.SetLeft(border, position.X - 60);Canvas.SetTop(border, position.Y - 15);}if (data.Tag == "Button"){Button button = new Button(){Width = 120,Height = 30,Content = "按钮1",FontSize = 14,Background = Brushes.LightGray,HorizontalContentAlignment = HorizontalAlignment.Center,VerticalContentAlignment = VerticalAlignment.Center,Padding = new Thickness(5),BorderBrush = Brushes.Black,BorderThickness = new Thickness(1)};button.AddHandler(Button.MouseDownEvent,new MouseButtonEventHandler( Container_Control_MouseDown),true);button.AddHandler(Button.MouseMoveEvent, new MouseEventHandler(Container_Control_MouseMove), true);button.AddHandler(Button.MouseUpEvent, new MouseButtonEventHandler(Container_Control_MouseUp), true);this.containerCanvas.Children.Add(button);Canvas.SetLeft(button, position.X - 60);Canvas.SetTop(button, position.Y - 15);}}
}

 

 注意:在此事件中,以下几点需要注意:

  • 通过e.Data.GetData方法获取传递的参数。
  • 通过e.GetPosition方法获取鼠标相对位置。参数是相对的对象,如Canvas容器等。
  • 容器的Drop事件中,根据传递的内容创建控件对象,并为新创建的控件对象绑定MouseDown,MouseMove,MouseUp方法。其中Button按钮,由于鼠标按下事件和本省自带的Click事件相冲突,所以需要通过AddHandler方法添加鼠标事件。
  • 通过Canvas.SetLeft,Canvas.SetTop方法设置控件对象在画布容器中的位置。

2. 控件拖动

在控件对象的MouseDown,MouseMove,MouseUp三个事件中,实现控件的拖动效果。即在MouseDown时开始,MouseMove中不断设置控件的Left,Top的值随鼠标而动,在MouseUp时停止

private void Container_Control_MouseUp(object sender, MouseButtonEventArgs e)
{if(e.LeftButton== MouseButtonState.Released){Mouse.Capture(null);}
}private void Container_Control_MouseMove(object sender, MouseEventArgs e)
{if (e.LeftButton == MouseButtonState.Pressed){var position = e.GetPosition(this.containerCanvas);Canvas.SetLeft((UIElement)sender,position.X-60);Canvas.SetTop((UIElement)sender,position.Y-15);}
}private void Container_Control_MouseDown(object sender, MouseButtonEventArgs e)
{if(e.LeftButton ==MouseButtonState.Pressed){Mouse.Capture((IInputElement)sender);}
}

 

注意,启动Mouse.Capture功能是为了捕获鼠标的焦点,使其在鼠标移动期间一直保持焦点,防止鼠标与控件分离

示例效果

本示例主要为了说明,只是简单地 实现了控件拖拽,拖动等效果,具体如下所示:

以上就是WPF之控件拖拽与拖动的全部内容,希望能够一起学习,共同进步。

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

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

相关文章

1 接口测试介绍

在软件测试工作中&#xff0c;接口测试是必不可少的。接口测试一般是发生在单元测试之后&#xff0c;系统测试之前。当开发人员输出API文档后&#xff0c;测试人员就可以开始编写接口测试用例了。接口测试可以让测试人员更早的介入&#xff0c;不需要等待前后端联调完成才开始测…

银行卡二要素API的应用案例:从在线购物到金融投资

引言 随着互联网技术的不断发展&#xff0c;人们的金融需求也在不断增加。随之而来的是各种新型金融服务的涌现&#xff0c;让用户的金融体验更加便利快捷。其中&#xff0c;银行卡二要素API的应用&#xff0c;则为用户的金融体验和安全性提供了极大的保障。 银行卡二要素API…

知识蒸馏的蒸馏损失方法代码总结(包括:基于logits的方法:KLDiv,dist,dkd等,基于中间层提示的方法:)

有两种知识蒸馏方法&#xff1a;一种利用教师模型的输出概率&#xff08;基于logits的方法&#xff09;[15,14,11]&#xff0c;另一种利用教师模型的中间表示&#xff08;基于提示的方法&#xff09;[12,13,18,17]。基于logits的方法利用教师的输出作为辅助信号来训练一个较小的…

【STM32】TIM定时器输出比较

1 输出比较 1.1 输出比较简介 OC&#xff08;Output Compare&#xff09;输出比较&#xff1b;IC&#xff08;Input Capture&#xff09;输入捕获&#xff1b;CC&#xff08;Capture/Compare&#xff09;输入捕获和输出比较的单元输出比较可以通过比较CNT与CCR寄存器值&#…

JavaWeb-HTTP协议

1. 什么是HTTP协议 HTTP超文本传输协(Hyper Text transfer protocol)&#xff0c;是一种用于用于分布式、协作式和超媒体信息系统的应用层协议。它于1990年提出&#xff0c;经过十几年的使用与发展&#xff0c;得到不断地完善和扩展。HTTP 是为 Web 浏览器与 Web 服务器之间的…

AI自动生成代码工具

AI自动生成代码工具是一种利用人工智能技术来辅助或自动化软件开发过程中的编码任务的工具。这些工具使用机器学习和自然语言处理等技术&#xff0c;根据开发者的需求生成相应的源代码。以下是一些常见的AI自动生成代码工具&#xff0c;希望对大家有所帮助。北京木奇移动技术有…

HCIP —— BGP 基础 (上)

BGP --- 边界网关协议 &#xff08;路径矢量协议&#xff09; IGP --- 内部网关协议 --- OSPF RIP ISIS EGP --- 外部网关协议 --- EGP BGP AS --- 自治系统 由单一的组织或者机构独立维护的网络设备以及网络资源的集合。 因 网络范围太大 需 自治 。 为区分不同的AS&#…

vim常见操作

vim常见操作 文章目录 vim常见操作1. 回退/前进2. 搜索3. 删除4. 定位到50行5. 显示行号6. 复制粘贴7. 剪贴8. 替换9. vim打开文件的时候出现 1. 回退/前进 1.esc进入命令模式 2.ctrlr 前进 u 回退2. 搜索 1&#xff09; esc进入命令模式 2&#xff09; /text  查找text&am…

【STM32】TIM定时器输入捕获

1 输入捕获 1.1 输入捕获简介 IC&#xff08;Input Capture&#xff09;输入捕获 输入捕获模式下&#xff0c;当通道输入引脚出现指定电平跳变时&#xff08;上升沿/下降沿&#xff09;&#xff0c;当前CNT的值将被锁存到CCR中&#xff08;把CNT的值读出来&#xff0c;写入到…

类风湿性关节炎口腔黏膜破裂引发抗瓜氨酸细菌和人蛋白抗体反应

今天给同学们分享一篇实验文章“Oral mucosal breaks trigger anti-citrullinated bacterial and human protein antibody responses in rheumatoid arthritis”&#xff0c;这篇文章发表在Sci Transl Med期刊上&#xff0c;影响因子为17.1。 结果解读&#xff1a; 口腔黏膜破…

Redis主从复制的配置和实现原理

Redis的持久化功能在一定程度上保证了数据的安全性&#xff0c;即便是服务器宕机的情况下&#xff0c;也可以保证数据的丢失非常少。通常&#xff0c;为了避免服务的单点故障&#xff0c;会把数据复制到多个副本放在不同的服务器上&#xff0c;且这些拥有数据副本的服务器可以用…

如何快速构建知识服务平台,打造个人或企业私域流量

随着互联网的快速发展&#xff0c;传统的知识付费平台已经不能满足用户的需求。而SaaS知识付费小程序平台则是一种新型的知识付费方式&#xff0c;具有灵活、便捷、高效等特点&#xff0c;为用户提供了更加优质的付费知识服务。本文将介绍如何搭建自己的SaaS知识付费小程序平台…

如何掌握构建 LMS 网站的艺术

目录 什么是学习管理系统 (LMS) 在线课程和 LMS 网站的好处 为什么 WordPress 对于 LMS 网站很重要 统一学习中心 多功能性和可扩展性 提高教育参与度 简化管理和监控 节省时间和费用 技能评估和绩效监督 持续学习和技能提升 使用 WordPress 插件构建成功的 LMS 课程 专注于您的…

sparkc程序idea调试提示内存不足

报错如下&#xff1a; Exception in thread "main" java.lang.IllegalArgumentException: System memory 259522560 must be at least 471859200. Please increase heap size using the --driver-memory option or spark.driver.memory in Spark configuration. 测…

自动驾驶:传感器初始标定

手眼标定 机器人手眼标定AxxB&#xff08;eye to hand和eye in hand&#xff09;及平面九点法标定 Ax xB问题求解&#xff0c;旋转和平移分步求解法 手眼标定AXXB求解方法&#xff08;文献总结&#xff09; 基于靶的方法 相机标定 (1) ApriTag (2) 棋盘格&#xff1a;cv::f…

富时中国A50指数暴跌

近年来&#xff0c;中国股市的波动一直备受关注&#xff0c;而富时中国A50指数更是其中一项备受瞩目的指标之一。然而&#xff0c;近期却出现了一场引人瞩目的暴跌&#xff0c;引发了广泛的关注和讨论。 富时中国A50指数简介 富时中国A50指数&#xff0c;作为富时罗素指数系列…

全新UI彩虹外链网盘系统源码V5.5/支持批量封禁+优化加载速度+用户系统与分块上传

源码简介&#xff1a; 全新UI彩虹外链网盘系统源码V5.5&#xff0c;它可以支持批量封禁优化加载速度。新增用户系统与分块上传。 彩虹外链网盘&#xff0c;作为一款PHP网盘与外链分享程序&#xff0c;具备广泛的文件格式支持能力。它不仅能够实现各种格式文件的上传&#xff…

CLASS60 DM蓝牙5.2双模热插拔PCB

键盘使用说明索引&#xff08;均为出厂默认值&#xff09; 软件支持&#xff08;驱动的详细使用帮助&#xff09;一些常见问题解答&#xff08;FAQ&#xff09;首次使用步骤蓝牙配对规则&#xff08;重要&#xff09;蓝牙和USB切换键盘默认层默认触发层0的FN键配置的功能默认功…

使用word中的VBA 批量设置Word中所有图片大小

在VBA编辑器中&#xff0c;你可以创建、编辑和运行VBA宏代码&#xff0c;以实现自动化任务和自定义Word 功能。如果你是VBA编程初学者&#xff0c;可以在VBA编辑器中查看Word VBA宏代码示例&#xff0c;以便更好地了解如何使用VBA编写代码。 要打开VBA编辑器&#xff0c;你可以…

【Vue】修改组件样式并动态添加样式

文章目录 目标修改样式动态添加/删除样式样式不生效 注意&#xff1a;类似效果el-step也可以实现&#xff0c;可以不用手动实现。这里只是练习。 目标 使用组件库中的组件&#xff0c;修改它的样式并动态添加/删除样式。 修改样式 组件中的一些类可能添加样式无法生效。如Ele…