Skip to content

Commit b54cbac

Browse files
committed
Drag drop
1 parent 2b2c2fc commit b54cbac

File tree

4 files changed

+278
-1
lines changed

4 files changed

+278
-1
lines changed

RetailCoder.VBE/Rubberduck.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,7 @@
482482
<DependentUpon>RemoveParametersView.xaml</DependentUpon>
483483
</Compile>
484484
<Compile Include="UI\Refactorings\RemoveParameters\RemoveParametersViewModel.cs" />
485+
<Compile Include="UI\Refactorings\ReorderParameters\DragAdorner.cs" />
485486
<Compile Include="UI\Refactorings\ReorderParameters\IndexIsNotLastConverter.cs" />
486487
<Compile Include="UI\Refactorings\ReorderParameters\IndexIsNotZeroConverter.cs" />
487488
<Compile Include="UI\Refactorings\ReorderParameters\ReorderParametersView.xaml.cs">
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
using System.Windows;
2+
using System.Windows.Documents;
3+
using System.Windows.Media;
4+
using System.Windows.Shapes;
5+
6+
namespace Rubberduck.UI.Refactorings.ReorderParameters
7+
{
8+
// borrowed from https://fxmax.wordpress.com/2010/10/05/wpf/
9+
public class DragAdorner : Adorner
10+
{
11+
private readonly Rectangle _child;
12+
private double _offsetLeft;
13+
private double _offsetTop;
14+
15+
/// <summary>
16+
/// Initializes a new instance of DragVisualAdorner.
17+
/// </summary>
18+
/// <param name="adornedElement">The element being adorned.</param>
19+
/// <param name="size">The size of the adorner.</param>
20+
/// <param name="brush">A brush to with which to paint the adorner.</param>
21+
public DragAdorner(UIElement adornedElement, Size size, Brush brush)
22+
: base(adornedElement)
23+
{
24+
var rect = new Rectangle
25+
{
26+
Fill = brush,
27+
Width = size.Width,
28+
Height = size.Height,
29+
IsHitTestVisible = false
30+
};
31+
_child = rect;
32+
}
33+
34+
public override GeneralTransform GetDesiredTransform(GeneralTransform transform)
35+
{
36+
var result = new GeneralTransformGroup();
37+
result.Children.Add(base.GetDesiredTransform(transform));
38+
result.Children.Add(new TranslateTransform(_offsetLeft, _offsetTop));
39+
return result;
40+
}
41+
42+
43+
/// <summary>
44+
/// Gets/sets the horizontal offset of the adorner.
45+
/// </summary>
46+
public double OffsetLeft
47+
{
48+
get { return _offsetLeft; }
49+
set
50+
{
51+
_offsetLeft = value;
52+
UpdateLocation();
53+
}
54+
}
55+
56+
57+
/// <summary>
58+
/// Updates the location of the adorner.
59+
/// </summary>
60+
/// <param name="left"></param>
61+
/// <param name="top"></param>
62+
public void SetOffsets(double left, double top)
63+
{
64+
_offsetLeft = left;
65+
_offsetTop = top;
66+
UpdateLocation();
67+
}
68+
69+
70+
/// <summary>
71+
/// Gets/sets the vertical offset of the adorner.
72+
/// </summary>
73+
public double OffsetTop
74+
{
75+
get { return _offsetTop; }
76+
set
77+
{
78+
_offsetTop = value;
79+
UpdateLocation();
80+
}
81+
}
82+
83+
/// <summary>
84+
/// Override.
85+
/// </summary>
86+
/// <param name="constraint"></param>
87+
/// <returns></returns>
88+
protected override Size MeasureOverride(Size constraint)
89+
{
90+
_child.Measure(constraint);
91+
return _child.DesiredSize;
92+
}
93+
94+
/// <summary>
95+
/// Override.
96+
/// </summary>
97+
/// <param name="finalSize"></param>
98+
/// <returns></returns>
99+
protected override Size ArrangeOverride(Size finalSize)
100+
{
101+
_child.Arrange(new Rect(finalSize));
102+
return finalSize;
103+
}
104+
105+
/// <summary>
106+
/// Override.
107+
/// </summary>
108+
/// <param name="index"></param>
109+
/// <returns></returns>
110+
protected override Visual GetVisualChild(int index)
111+
{
112+
return _child;
113+
}
114+
115+
/// <summary>
116+
/// Override. Always returns 1.
117+
/// </summary>
118+
protected override int VisualChildrenCount => 1;
119+
120+
private void UpdateLocation()
121+
{
122+
var adornerLayer = Parent as AdornerLayer;
123+
adornerLayer?.Update(AdornedElement);
124+
}
125+
}
126+
}

RetailCoder.VBE/UI/Refactorings/ReorderParameters/ReorderParametersView.xaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@
3535
ItemsSource="{Binding Parameters, UpdateSourceTrigger=PropertyChanged}"
3636
SelectedIndex="0"
3737
Name="ParameterGrid"
38+
AllowDrop="True"
39+
Drop="ParameterGrid_Drop"
40+
DragEnter="ParameterGrid_DragEnter"
41+
PreviewMouseMove="ParameterGrid_PreviewMouseMove"
42+
PreviewMouseLeftButtonDown="ParameterGrid_PreviewMouseLeftButtonDown"
3843
AlternationCount="2">
3944
<ListView.ItemTemplate>
4045
<DataTemplate>
Lines changed: 146 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,155 @@
1-
namespace Rubberduck.UI.Refactorings.ReorderParameters
1+
using System;
2+
using System.Windows;
3+
using System.Windows.Controls;
4+
using System.Windows.Documents;
5+
using System.Windows.Input;
6+
using System.Windows.Media;
7+
using Rubberduck.Refactorings.ReorderParameters;
8+
9+
namespace Rubberduck.UI.Refactorings.ReorderParameters
210
{
311
public partial class ReorderParametersView
412
{
13+
// borrowed the drag/drop from https://fxmax.wordpress.com/2010/10/05/wpf/
14+
private Point _startPoint;
15+
private DragAdorner _adorner;
16+
private AdornerLayer _layer;
17+
18+
private ReorderParametersViewModel ViewModel => (ReorderParametersViewModel)DataContext;
19+
520
public ReorderParametersView()
621
{
722
InitializeComponent();
823
}
24+
25+
private void ParameterGrid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
26+
{
27+
_startPoint = e.GetPosition(ParameterGrid);
28+
}
29+
30+
private void ParameterGrid_PreviewMouseMove(object sender, MouseEventArgs e)
31+
{
32+
if (e.LeftButton == MouseButtonState.Pressed)
33+
{
34+
var position = e.GetPosition(null);
35+
36+
if (Math.Abs(position.X - _startPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||
37+
Math.Abs(position.Y - _startPoint.Y) > SystemParameters.MinimumVerticalDragDistance)
38+
{
39+
BeginDrag(e);
40+
}
41+
}
42+
}
43+
44+
private void BeginDrag(MouseEventArgs e)
45+
{
46+
var listViewItem = FindAncestor<ListViewItem>((DependencyObject)e.OriginalSource);
47+
48+
if (listViewItem == null) { return; }
49+
50+
// get the data for the ListViewItem
51+
var parameter = (Parameter)ParameterGrid.ItemContainerGenerator.ItemFromContainer(listViewItem);
52+
53+
//setup the drag adorner.
54+
InitialiseAdorner(listViewItem);
55+
56+
//add handles to update the adorner.
57+
ParameterGrid.PreviewDragOver += ParameterGrid_DragOver;
58+
ParameterGrid.DragLeave += ParameterGrid_DragLeave;
59+
ParameterGrid.DragEnter += ParameterGrid_DragEnter;
60+
61+
var data = new DataObject(typeof(Parameter), parameter);
62+
DragDrop.DoDragDrop(ParameterGrid, data, DragDropEffects.Move);
63+
64+
//cleanup
65+
ParameterGrid.PreviewDragOver -= ParameterGrid_DragOver;
66+
ParameterGrid.DragLeave -= ParameterGrid_DragLeave;
67+
ParameterGrid.DragEnter -= ParameterGrid_DragEnter;
68+
69+
if (_adorner != null)
70+
{
71+
AdornerLayer.GetAdornerLayer(ParameterGrid).Remove(_adorner);
72+
_adorner = null;
73+
}
74+
}
75+
76+
private void ParameterGrid_DragEnter(object sender, DragEventArgs e)
77+
{
78+
if (!e.Data.GetDataPresent(typeof(Parameter)) ||
79+
sender == e.Source)
80+
{
81+
e.Effects = DragDropEffects.None;
82+
}
83+
}
84+
85+
86+
private void ParameterGrid_Drop(object sender, DragEventArgs e)
87+
{
88+
if (e.Data.GetDataPresent(typeof(Parameter)))
89+
{
90+
var parameter = e.Data.GetData(typeof(Parameter)) as Parameter;
91+
var listViewItem = FindAncestor<ListViewItem>((DependencyObject)e.OriginalSource);
92+
93+
if (listViewItem != null)
94+
{
95+
var parameterToReplace = (Parameter)ParameterGrid.ItemContainerGenerator.ItemFromContainer(listViewItem);
96+
var index = ParameterGrid.Items.IndexOf(parameterToReplace);
97+
98+
if (index >= 0)
99+
{
100+
ViewModel.Parameters.Move(ViewModel.Parameters.IndexOf(parameter), index);
101+
}
102+
}
103+
else
104+
{
105+
ViewModel.Parameters.Move(ViewModel.Parameters.IndexOf(parameter), ViewModel.Parameters.Count - 1);
106+
}
107+
}
108+
}
109+
110+
private void InitialiseAdorner(ListViewItem listViewItem)
111+
{
112+
var brush = new VisualBrush(listViewItem);
113+
_adorner = new DragAdorner(listViewItem, listViewItem.RenderSize, brush) {Opacity = 0.5};
114+
_layer = AdornerLayer.GetAdornerLayer(ParameterGrid);
115+
_layer.Add(_adorner);
116+
}
117+
118+
void ParameterGrid_DragLeave(object sender, DragEventArgs e)
119+
{
120+
if (e.OriginalSource == ParameterGrid)
121+
{
122+
var p = e.GetPosition(ParameterGrid);
123+
var r = VisualTreeHelper.GetContentBounds(ParameterGrid);
124+
if (!r.Contains(p))
125+
{
126+
e.Handled = true;
127+
}
128+
}
129+
}
130+
131+
void ParameterGrid_DragOver(object sender, DragEventArgs args)
132+
{
133+
if (_adorner == null) { return; }
134+
135+
_adorner.OffsetLeft = args.GetPosition(ParameterGrid).X;
136+
_adorner.OffsetTop = args.GetPosition(ParameterGrid).Y - _startPoint.Y;
137+
}
138+
139+
// Helper to search up the VisualTree
140+
private static T FindAncestor<T>(DependencyObject current)
141+
where T : DependencyObject
142+
{
143+
do
144+
{
145+
if (current is T)
146+
{
147+
return (T)current;
148+
}
149+
current = VisualTreeHelper.GetParent(current);
150+
}
151+
while (current != null);
152+
return null;
153+
}
9154
}
10155
}

0 commit comments

Comments
 (0)