WPF之DragDrop与控件的移动🔥
CZerocheng 1/12/2024 WPF
# 效果展示

# 使用
本次示例的工程目录结构如下图所示:
工程中提及到的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
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
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
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
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
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20