WPF学习- AllowDrop 用户控件启用拖放功能
知识点:
- 创建自定义用户控件(UserControl)
- 使用户控件成为拖动源
- 使用户控件成为放置目标
- 使面板能够接收从用户控件放置的数据
创建项目:
1、新建WPF项目(Wpf-AllowDrop)
2、在MainWindow.xaml的 Grid控件添加源码
<Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <StackPanel Grid.Column="0" Background="Beige"> <TextBox Width="Auto" Margin="2" Text="green"/> </StackPanel> <StackPanel Grid.Column="1" Background="Bisque"> </StackPanel>
设计显示效果如下:
项目中添加用户控件
1、“项目”--> “添加用户控件”
2、重命名为Circle.xaml,添加。
3、在Circle.xaml的Grid中添加 Ellipse标签
<Ellipse x:Name="circleUI" Height="100" Width="100" Fill="Blue" />
显示效果如下:
4、在Circle.xaml.cs源文件中,重写构造函数
public partial class Circle : UserControl { public Circle() { InitializeComponent(); } public Circle(Circle c) //重写构造 { InitializeComponent(); this.circleUI.Height = c.circleUI.Height; this.circleUI.Width = c.circleUI.Height; this.circleUI.Fill = c.circleUI.Fill; } }
5、在MainWindow.xaml中的第一个StackPanel中添加两个自定义的圆控件
<StackPanel Grid.Column="0" Background="Beige"> <TextBox Width="Auto" Margin="2" Text="green"/> <!--添加圆控件--> <local:Circle Margin="2" /> <local:Circle Margin="2" /> </StackPanel>
效果图如下:
用户控件实现拖动源事件
1、在Circle.xaml.cs中重写OnMouseMove事件
protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); // 判断左键是否按下 if (e.LeftButton == MouseButtonState.Pressed) { //声明DataObject,并打包圆控件的图像绘制方式(包含颜色)、高度及其副本。 DataObject data = new DataObject(); data.SetData(DataFormats.StringFormat, circleUI.Fill.ToString()); data.SetData("Double", circleUI.Height); data.SetData("Object", this); //使用DragDrop的DoDragDrop方法开启拖动功能。拖动方式为拖动复制或移动 DragDrop.DoDragDrop(this, data, DragDropEffects.Copy | DragDropEffects.Move); } }
2、运行测试:单击一个圆,任意拖动查看效果:
3、重写OnGiveFeedback,增强拖动时,光标反馈效果。
protected override void OnGiveFeedback(GiveFeedbackEventArgs e) { base.OnGiveFeedback(e); //Effects获取拖动类型 if (e.Effects.HasFlag(DragDropEffects.Copy)) { Mouse.SetCursor(Cursors.Cross); //拖动复制显示红十字 } else if (e.Effects.HasFlag(DragDropEffects.Move)) { Mouse.SetCursor(Cursors.Pen); //移动显示笔形 } else { Mouse.SetCursor(Cursors.No); } e.Handled = true; }
4、运行测试:单击一个圆,任意拖动查看效果(主要指针效果)。
用户控件成为放置目标
1、在Circle.xaml的UserControl标记中添加AllowDrop属性为"True"
<UserControl x:Class="Wpf_AllowDrop.Circle" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:Wpf_AllowDrop" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300" AllowDrop="True">
2、在Circle.xaml.cs源码文件中,重写OnDrop事件方法
protected override void OnDrop(DragEventArgs e) { base.OnDrop(e); //检查数据是否包含制定字符串类型数据 if (e.Data.GetDataPresent(DataFormats.StringFormat)) { //获取字符串 string dataString = (string)e.Data.GetData(DataFormats.StringFormat); //使用BrushConverter将字符串转换成Brush对象并应用于提供圆形控件 UI 的 Ellipse 的 Fill BrushConverter converter = new BrushConverter(); if (converter.IsValid(dataString)) { Brush newFill = (Brush)converter.ConvertFromString(dataString); circleUI.Fill = newFill; if (e.KeyStates.HasFlag(DragDropKeyStates.ControlKey)) { e.Effects = DragDropEffects.Copy; } else { e.Effects = DragDropEffects.Move; } } } e.Handled = true; }
3、运行测试,查看TextBox文本green拖入圆形区域后效果:
4、重写OnDragOver,禁止无效颜色拖入圆形控件(鼠标显示禁止)
protected override void OnDragOver(DragEventArgs e) { base.OnDragOver(e); //设置Effects 为不接受数据 e.Effects = DragDropEffects.None; if (e.Data.GetDataPresent(DataFormats.StringFormat)) { string dataString = (string)e.Data.GetData(DataFormats.StringFormat); BrushConverter converter = new BrushConverter(); //判断拖动值是否有效 if (converter.IsValid(dataString)) { //判断Ctrl键是否按下 if (e.KeyStates.HasFlag(DragDropKeyStates.ControlKey)) { e.Effects = DragDropEffects.Copy; } else { e.Effects = DragDropEffects.Move; } } } e.Handled = true; }
5、运行测试,拖动Gre到圆型中,查看鼠标变化
6、添加一个Brush变量用来暂时存放目标控件属性,重写OnDrageEnter和OneDrageLeave,增加预览效果,未放置则离开,目标控件颜色不发生变化。
public partial class Circle : UserControl { private Brush _previousFill = null; //声明私有Brush,初始值为null
protected override void OnDragEnter(DragEventArgs e) { base.OnDragEnter(e); //控件原始属性存放在全局私有Brush对象中 _previousFill = circleUI.Fill; if (e.Data.GetDataPresent(DataFormats.StringFormat)) { string dataString = (string)e.Data.GetData(DataFormats.StringFormat); BrushConverter converter = new BrushConverter(); if (converter.IsValid(dataString)) { Brush newFill = (Brush)converter.ConvertFromString(dataString.ToString()); circleUI.Fill = newFill; } } }
protected override void OnDragLeave(DragEventArgs e) { base.OnDragLeave(e); //若直接离开(未释放鼠标),把存在全局私有变量_previousFill属性重新赋值 circleUI.Fill = _previousFill; }
7、运行测试,拖动时先不释放鼠标测试,再是否鼠标测试,查看效果:
使面板能够接受放置的数据
1、在MainWindow.xaml 的两个StackPanel控件中添加DragOver事件处理,名称为panel_DragOver,以及添加Drop事件处理,名称为panel_drop。并设置AllowDrop属性为"True"。
<StackPanel Grid.Column="0" Background="Beige" AllowDrop="True" DragOver="panel_DragOver" Drop="panel_Drop"> <TextBox Width="Auto" Margin="2" Text="green"/> <!--添加圆控件--> <local:Circle Margin="2" /> <local:Circle Margin="2" /> </StackPanel> <StackPanel Grid.Column="1" Background="Bisque" AllowDrop="True" DragOver="panel_DragOver" Drop="panel_Drop"> </StackPanel>
2、在MainWindow.xaml.cs中实现两个事件处理方法:
private void panel_DragOver(object sender, DragEventArgs e) { //检查拖动的数据是否包含由圆形用户控件打包在 DataObject 中并且在 DoDragDrop 调用中传递的“对象”数据 if (e.Data.GetDataPresent("Object")) { if (e.KeyStates == DragDropKeyStates.ControlKey) { e.Effects = DragDropEffects.Copy; } else { e.Effects = DragDropEffects.Move; } } }
private void panel_Drop(object sender, DragEventArgs e) { if (e.Handled == false) { Panel _panel = (Panel)sender; //取得当前panel对象 UIElement _element = (UIElement)e.Data.GetData("Object");//移动对象转换成WPF核心基类对象 if (_panel != null && _element != null) { //获取移动对象的父对象 Panel _parent = (Panel)VisualTreeHelper.GetParent(_element); if (_parent != null) { if (e.KeyStates == DragDropKeyStates.ControlKey && e.AllowedEffects.HasFlag(DragDropEffects.Copy)) { Circle _circle = new Circle((Circle)_element); _panel.Children.Add(_circle); //在其子元素中添加对象 e.Effects = DragDropEffects.Copy; } else if (e.AllowedEffects.HasFlag(DragDropEffects.Move)) { _parent.Children.Remove(_element); //移动时,从父对象中移除源文件 _panel.Children.Add(_element); e.Effects = DragDropEffects.Move; } } } } }
3、运行测试,随意拖动查看效果: