WPF之DragDrop与控件的移动🔥

1/12/2024 WPF

# 效果展示

mixureSecure

# 使用

本次示例的工程目录结构如下图所示: mixureSecure 工程中提及到的PersonView,StudentView,StudentViewModel与DataTemplate和我上一边文章的内容一模一样,可自行翻阅,本次重点讲解控件移动内容。

在PersonViewModel里面添加

[ObservableProperty]
public int zIndex;
[ObservableProperty]
public double xPos;
[ObservableProperty]
public double yPos;

public void Move(double x,double y)
{
	XPos += x;
	YPos += y;
}
1
2
3
4
5
6
7
8
9
10
11
12

1.编写拖动源SourceView和SourceViewModel

[ObservableObject]
public partial class SourceViewModel
{
	[ObservableProperty]
	public ObservableCollection<Node> nodeItems = new ObservableCollection<Node>();
    public SourceViewModel()
    {
		BitmapImage bmp = new BitmapImage();
		bmp.BeginInit();
		bmp.CacheOption = BitmapCacheOption.OnLoad;
		bmp.UriSource = new Uri("./Resources/images/Image.png", UriKind.RelativeOrAbsolute);
		bmp.EndInit();
		NodeItems.Add(new Node() { Name = "人员", ImageSource = bmp });
		NodeItems.Add(new Node() { Name = "学生", ImageSource = bmp });
	}
}

[ObservableObject]
public partial class Node
{
	[ObservableProperty]
	public string name;
	[ObservableProperty]
	public BitmapSource imageSource;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
查看详细代码
<UserControl x:Class="DragAndDropDemo.View.SourceView"
             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:DragAndDropDemo.View"
             xmlns:hc="https://handyorg.github.io/handycontrol"
             xmlns:viewmodel="clr-namespace:DragAndDropDemo.ViewModel"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="100">
    <UserControl.DataContext>
        <viewmodel:SourceViewModel/>
    </UserControl.DataContext>
    <Grid>
        <hc:ScrollViewer>
            <ItemsControl ItemsSource="{Binding NodeItems}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Grid Margin="5" PreviewMouseLeftButtonDown="Grid_PreviewMouseLeftButtonDown">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="30" />
                                <ColumnDefinition />
                            </Grid.ColumnDefinitions>
                            <Grid Grid.Column="0">
                                <Border CornerRadius="2" Background="LightSeaGreen"></Border>
                                <Rectangle Width="20" Height="20" IsHitTestVisible="False" Fill="White" >
                                    <Rectangle.OpacityMask>
                                        <ImageBrush ImageSource="{Binding ImageSource}"/>
                                    </Rectangle.OpacityMask>
                                </Rectangle>
                            </Grid>
                            <Grid Grid.Column="1" >
                                <Border CornerRadius="2" Background="White"></Border>
                                <StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="2,5,5,5" >
                                    <TextBlock Text="{Binding Name, Mode=OneWay}" Foreground="#438CFF" FontSize="13" FontWeight="Bold"/>
                                </StackPanel>
                            </Grid>
                        </Grid>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </hc:ScrollViewer>
    </Grid>
</UserControl>

/// <summary>
/// SourceView.xaml 的交互逻辑
/// </summary>
public partial class SourceView : UserControl
{
	public SourceView()
	{
		InitializeComponent();
	}

	private void Grid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
	{
		FrameworkElement item = (FrameworkElement)sender;
		Node nodeTag = item.DataContext as Node;
		if (nodeTag != null)
		{
			if(nodeTag.Name=="人员")
			{
				PersonViewModel personViewModel = new PersonViewModel();
				personViewModel.Name = "张三";
				personViewModel.Age = 23;
				personViewModel.Height = 187;
				personViewModel.Weight = 70;
				DragDrop.DoDragDrop(item,personViewModel,DragDropEffects.Copy);
			}
			if(nodeTag.Name=="学生")
			{
				StudentViewModel studentViewModel = new StudentViewModel();
				studentViewModel.Name = "张三";
				studentViewModel.ID = 002;
				studentViewModel.TotalScore = 290;
				studentViewModel.AverageScore = 90;
				DragDrop.DoDragDrop(item,studentViewModel,DragDropEffects.Copy);
			}
		}
        Console.WriteLine("人员");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

通过绑定ItemsControl的数据源来实现拖动源的数据源绑定,本次的数据源由两个一个是“人员”,一个是“学生”,订阅每个数据源的PreviewMouseLeftButtonDown来进行对象的判断和DragDrop.DoDragDrop来启动拖放操作。

2.编写拖放目标DestinationView和DestinationViewModel

查看代码
<UserControl x:Class="UserControlMoveDemo.View.DestinationView"
             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:UserControlMoveDemo.View"
             xmlns:viewmodel="clr-namespace:UserControlMoveDemo.ViewModel"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800"
             AllowDrop="True"
             x:Name="root"
             Drop="StackPanel_Drop"
            >
    <UserControl.DataContext>
        <viewmodel:DestinationViewModel/>
    </UserControl.DataContext>
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="../Resources/DataTemplates.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>
    <Canvas Height="{Binding ElementName=root, Path=ActualHeight}" ClipToBounds="True">
        <ItemsControl ItemsSource="{Binding PersonData}" x:Name="layerPanel" MouseLeftButtonDown="MouseDown" MouseMove="MouseMove" MouseLeftButtonUp="MouseUp">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas  x:Name="container"  AllowDrop="True" Background="White"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemContainerStyle>
                <Style TargetType="ContentPresenter">
                    <Setter Property="Canvas.ZIndex" Value="{Binding ZIndex}" />
                    <Setter Property="Canvas.Left" Value="{Binding XPos}" />
                    <Setter Property="Canvas.Top" Value="{Binding YPos}" />
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ItemsControl>
    </Canvas>
</UserControl>


/// <summary>
/// DestinationView.xaml 的交互逻辑
/// </summary>
public partial class DestinationView : UserControl
{
	bool is_drag = false;
	private Point prov_point = new Point();
    public PersonViewModel SelectedLayer { get; private set; }
	
	public DestinationView()
	{
		InitializeComponent();
	}

	public DestinationViewModel Data
	{
		get
		{
			return (DestinationViewModel)DataContext;
		}
	}

	private void StackPanel_Drop(object sender, DragEventArgs e)
{
	if (e.Data.GetDataPresent(typeof(PersonViewModel)))
	{
		e.Effects = DragDropEffects.Copy;
		PersonViewModel model = (PersonViewModel)e.Data.GetData(typeof(PersonViewModel));
		Data.PersonData.Add(model);
		Point p = e.GetPosition(layerPanel);
		model.XPos = p.X;
		model.YPos = p.Y;
		prov_point = p;
		Console.WriteLine($"StackPanel_Drop:第{Data.PersonData.Count}个添加成功");
	}
	is_drag = false;
}

private void MouseDown(object sender, MouseButtonEventArgs e)
{
	Console.WriteLine("MouseDown");
	if (!is_drag)
	{
		Point p = e.GetPosition(layerPanel);
		PersonView layer = Utils.GetChildData<PersonView>(layerPanel, p);
		if (layer != null)
		{
			is_drag = true;
			prov_point = p;
			SelectedLayer = layer.DataContext as PersonViewModel;
		}
	}
}
	

private void MouseUp(object sender, MouseButtonEventArgs e)
{
	Console.WriteLine("MouseUp");
	is_drag = false;
}

private void MouseMove(object sender, MouseEventArgs e)
{
	if (is_drag)
	{
		Console.WriteLine("MouseMove");
		Point p = e.GetPosition(layerPanel);
		double x = p.X - prov_point.X;
		double y = p.Y - prov_point.Y;
		SelectedLayer.Move(x, y);
		prov_point = p;
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
[INotifyPropertyChanged]
public partial class DestinationViewModel
{
	[ObservableProperty]
	public ObservableCollection<PersonViewModel> personData = new ObservableCollection<PersonViewModel>();
}
1
2
3
4
5
6

在拖放目标上设置AllowDrop="True",订阅Drop事件,然后通过Drop得到的目标对象进行相应的操作。

# 工具类

public static class Utils
{
	public static T GetChildData<T>(UIElement el, Point p) where T : DependencyObject
	{
		IInputElement obj = el.InputHitTest(p);
		DependencyObject target = obj as DependencyObject;
        
		while (target != null)
		{
			if (target is T)
			{
				T t = (T)target;
				return t;
			}
			target = VisualTreeHelper.GetParent(target);
		}

		return default(T);
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20