在 WPF 中的网格中的单元格之间拖放自定义控件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25282443/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Drag and Drop custom controls between cells in a grid in WPF
提问by Gentatsu
I've got some custom controls which are dynamically added to a custom grid. These controls can span over several columns and rows(which are all the same size). I'd like to drag and drop between the rows and columns. I can drag the individual controls, but they can move anywhere without limit. Even off the grid. I'd like to do it so it can only be dragged inside the grid AND snaps to the column/row it's dragged to.
我有一些动态添加到自定义网格的自定义控件。这些控件可以跨越多个列和行(它们的大小都相同)。我想在行和列之间拖放。我可以拖动单个控件,但它们可以不受限制地移动到任何地方。甚至离网。我想这样做,所以它只能在网格内拖动并捕捉到它拖动到的列/行。
Is there any easy-ish way to do this?
有没有什么简单的方法可以做到这一点?
Honestly, if I could get the current row/column that it's over, then all I'd need to do is set the column/row of it to them and that would probably do it and then just worry about keeping it inside the grid.
老实说,如果我能得到它结束的当前行/列,那么我需要做的就是将它的列/行设置为它们,这可能会做到,然后只需担心将其保留在网格内。
采纳答案by Gentatsu
I figured out a nice and fun way!
我想出了一个又好又有趣的方法!
I worked out the position on the grid that the the mouse is on on the MouseUp event and then the relative position of the mouse on the control since it spans several rows/columns.
我计算了鼠标在 MouseUp 事件上的网格上的位置,然后计算了鼠标在控件上的相对位置,因为它跨越了几行/几列。
public void getPosition(UIElement element, out int col, out int row)
{
DControl control = parent as DControl;
var point = Mouse.GetPosition(element);
row = 0;
col = 0;
double accumulatedHeight = 0.0;
double accumulatedWidth = 0.0;
// calc row mouse was over
foreach (var rowDefinition in control.RowDefinitions)
{
accumulatedHeight += rowDefinition.ActualHeight;
if (accumulatedHeight >= point.Y)
break;
row++;
}
// calc col mouse was over
foreach (var columnDefinition in control.ColumnDefinitions)
{
accumulatedWidth += columnDefinition.ActualWidth;
if (accumulatedWidth >= point.X)
break;
col++;
}
}
I then take away the relative positions from the normal positions so that when you drop it, it always drops on the top left of the screen. When I move my controls, I use margins to move it, which screws up the position on the grid at the time, as shown below:
然后我从正常位置中取出相对位置,这样当你放下它时,它总是落在屏幕的左上角。当我移动我的控件时,我使用边距来移动它,这在当时弄乱了网格上的位置,如下所示:
void Chart_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (IsMouseCaptured)
{
Point mouseDelta = Mouse.GetPosition(this);
mouseDelta.Offset(-mouseOffset.X, -mouseOffset.Y);
Margin = new Thickness(
Margin.Left + mouseDelta.X,
Margin.Top + mouseDelta.Y,
Margin.Right - mouseDelta.X,
Margin.Bottom - mouseDelta.Y);
}
}
void Chart_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
mouseOffset = Mouse.GetPosition(this);
CaptureMouse();
parent.currentObject = this;
}
To tackle this, I simply reset the margin.
为了解决这个问题,我只是重置了边距。
public void updatePosition()
{
Grid.SetRow(this, (int)position.Y);
Grid.SetColumn(this, (int)position.X);
Margin = new Thickness();
}
I hope this helps someone else since it was rather frustrating for me to find the answer and in the end I managed to get lots of little fragments of how to do things and eventually came up with my own solution.
我希望这对其他人有所帮助,因为找到答案对我来说相当令人沮丧,最后我设法获得了很多关于如何做事的小片段,并最终想出了我自己的解决方案。
回答by Sheridan
Is there any easy-ish way to do this?
有没有什么简单的方法可以做到这一点?
I'd say that the answer to this question very much depends on your experience using Drag and Drop functionality... for a beginner, I'd say that the answer to this was no, but for someone with some experience and some common sense, it might not be too bad.
我会说这个问题的答案在很大程度上取决于您使用拖放功能的经验......对于初学者,我会说这个答案是否定的,但对于有一些经验和常识的人,应该不会太差。
To determine which Gridcell the user's mouse is over will not be straight forward. You can handle the PreviewDragOverevent and use the VisualTreeHelper.HitTestmethodto check which control the mouse is currently over:
要确定Grid用户的鼠标悬停在哪个单元格上不会很简单。您可以处理该PreviewDragOver事件并使用该VisualTreeHelper.HitTest方法来检查鼠标当前位于哪个控件上:
private void PreviewDragOver(object sender, DragEventArgs e)
{
HitTestResult hitTestResult = VisualTreeHelper.HitTest(adornedUIElement,
e.GetPosition(adornedUIElement));
Control controlUnderMouse = hitTestResult.VisualHit.GetParentOfType<Control>();
}
The GetParentOfTypemethod is a useful extension method that I created, but you can convert it to a normal method easily enough:
该GetParentOfType方法是我创建的一个有用的扩展方法,但您可以很容易地将其转换为普通方法:
public static T GetParentOfType<T>(this DependencyObject element) where T : DependencyObject
{
Type type = typeof(T);
if (element == null) return null;
DependencyObject parent = VisualTreeHelper.GetParent(element);
if (parent == null && ((FrameworkElement)element).Parent is DependencyObject) parent = ((FrameworkElement)element).Parent;
if (parent == null) return null;
else if (parent.GetType() == type || parent.GetType().IsSubclassOf(type)) return parent as T;
return GetParentOfType<T>(parent);
}
Of course, once you have a Controlin your controlUnderMousevariable, you'll still have some considerable work to do as you work your way through the UIElements until you get to the Grid... you can of course make further use of the GetParentOfTypemethod to make your job easier.
当然,一旦你Control的controlUnderMouse变量中有 a,当你通过UIElements 直到你到达Grid......你当然可以进一步使用该GetParentOfType方法来使你的工作更轻松。

