浅谈WPF之控件拖拽与拖动

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

涉及知识点

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

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

实现步骤

1. 页面布局

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

XML 复制代码
<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事件命令,当鼠标左键按下时触发对应的事件,并开始拖拽。如下所示:

cs 复制代码
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事件,在此事件中创建新的控件,如下所示:

cs 复制代码
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时停止

cs 复制代码
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之控件拖拽与拖动的全部内容,希望能够一起学习,共同进步。

相关推荐
c#上位机2 小时前
wpf之RelativeSource用法总结
c#·wpf
玖笙&3 天前
✨WPF编程基础【2.1】布局原则
c++·wpf·visual studio
玖笙&3 天前
✨WPF编程基础【2.2】:布局面板实战
c++·wpf·visual studio
SEO-狼术3 天前
.NET WPF 数据编辑器集合提供列表框控件
.net·wpf
FuckPatience7 天前
WPF 具有跨线程功能的UI元素
wpf
诗仙&李白7 天前
HEFrame.WpfUI :一个现代化的 开源 WPF UI库
ui·开源·wpf
He BianGu7 天前
【笔记】在WPF中Binding里的详细功能介绍
笔记·wpf
He BianGu7 天前
【笔记】在WPF中 BulletDecorator 的功能、使用方式并对比 HeaderedContentControl 与常见 Panel 布局的区别
笔记·wpf
123梦野8 天前
WPF——效果和可视化对象
wpf
He BianGu8 天前
【笔记】在WPF中Decorator是什么以及何时优先考虑 Decorator 派生类
笔记·wpf