我在写我的第一个WPF应用程序。 我有帆布,其中用户可以添加包含表单用户控件子类。 用户应该能够拖动画布这些用户控件。 什么是与WPF做到这一点的最佳做法? 谢谢。
Answer 1:
这在Silverlight和不在WPF做,但它应该工作一样。
创建于控制两个私有属性:
protected bool isDragging;
private Point clickPosition;
然后attatch一些事件处理程序的控制的构造函数:
this.MouseLeftButtonDown += new MouseButtonEventHandler(Control_MouseLeftButtonDown);
this.MouseLeftButtonUp += new MouseButtonEventHandler(Control_MouseLeftButtonUp);
this.MouseMove += new MouseEventHandler(Control_MouseMove);
现在创建这些方法:
private void Control_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
isDragging = true;
var draggableControl = sender as UserControl;
clickPosition = e.GetPosition(this);
draggableControl.CaptureMouse();
}
private void Control_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
isDragging = false;
var draggable = sender as UserControl;
draggable.ReleaseMouseCapture();
}
private void Control_MouseMove(object sender, MouseEventArgs e)
{
var draggableControl = sender as UserControl;
if (isDragging && draggableControl != null)
{
Point currentPosition = e.GetPosition(this.Parent as UIElement);
var transform = draggableControl.RenderTransform as TranslateTransform;
if (transform == null)
{
transform = new TranslateTransform();
draggableControl.RenderTransform = transform;
}
transform.X = currentPosition.X - clickPosition.X;
transform.Y = currentPosition.Y - clickPosition.Y;
}
}
有几件事情要注意这里:
1.这并不一定是在一个画布。 它可以在一个StackPanel或网格为好。
2.这使得整个控制拖动,如果在控制任意位置单击并拖动它会拖累整个控制意思。 不知道如果多数民众赞成正是你想要的。
编辑-
扩大对一些在你的问题的具体的:我会实现这个的最好方法是创建一个从用户控件,这是建立与此代码,则所有可拖动控制应延长DraggableControl也许叫DraggableControl继承的类。
编辑2 - 有小问题,当你有这个控件的DataGrid。 如果排序在的MouseLeftButtonUp事件永远不会触发数据网格的列。 我已经更新,以便isDragging被保护的代码。 我找到了最好的解决方案是,以配合该匿名方法到DataGrid的LostMouseCapture事件:
this.MyDataGrid.LostMouseCapture += (sender, e) => { this.isDragging = false; };
Answer 2:
关于科瑞Sunwold的解决方案-我摆脱的MouseUp和MouseDown事件中,我使用简化为MouseButtonState如下:)我使用Canvas.SetLeft()和Canvas.SetTop(的MouseMove方法)来代替的RenderTransform所以我并不需要存储旧从MouseDown事件位置。
if (e.LeftButton == MouseButtonState.Pressed && draggableControl != null)
{
//...
}
Answer 3:
科里的答案是正确的大部分,但它缺少一个重要的元素:什么样的最后转换为内存。 否则,当你移动一个项目,松开鼠标按钮,然后再次单击该项目,变换重置为(0,0)
和控制跳回到它的起源。
下面是对我工作的稍作修改的版本:
public partial class DragItem : UserControl
{
protected Boolean isDragging;
private Point mousePosition;
private Double prevX, prevY;
public DragItem()
{
InitializeComponent();
}
private void UserControl_MouseLeftButtonDown(Object sender, MouseButtonEventArgs e)
{
isDragging = true;
var draggableControl = (sender as UserControl);
mousePosition = e.GetPosition(Parent as UIElement);
draggableControl.CaptureMouse();
}
private void UserControl_MouseLeftButtonUp(Object sender, MouseButtonEventArgs e)
{
isDragging = false;
var draggable = (sender as UserControl);
var transform = (draggable.RenderTransform as TranslateTransform);
if (transform != null)
{
prevX = transform.X;
prevY = transform.Y;
}
draggable.ReleaseMouseCapture();
}
private void UserControl_MouseMove(Object sender, MouseEventArgs e)
{
var draggableControl = (sender as UserControl);
if (isDragging && draggableControl != null)
{
var currentPosition = e.GetPosition(Parent as UIElement);
var transform = (draggableControl.RenderTransform as TranslateTransform);
if (transform == null)
{
transform = new TranslateTransform();
draggableControl.RenderTransform = transform;
}
transform.X = (currentPosition.X - mousePosition.X);
transform.Y = (currentPosition.Y - mousePosition.Y);
if (prevX > 0)
{
transform.X += prevX;
transform.Y += prevY;
}
}
}
}
关键是存储先前的X和Y偏移量,然后用它们来扩大当前运动的,以在正确的偏移总偏移到达。
Answer 4:
我遇到了一些麻烦与给定的解决方案,并结束了与此:
public partial class UserControlDraggable : UserControl
{
public UserControlDraggable()
{
InitializeComponent();
MouseLeftButtonDown += new MouseButtonEventHandler(Control_MouseLeftButtonDown);
MouseLeftButtonUp += new MouseButtonEventHandler(Control_MouseLeftButtonUp);
MouseMove += new MouseEventHandler(Control_MouseMove);
}
private void Control_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_isDragging = true;
_mouseLocationWithinMe = e.GetPosition(this);
CaptureMouse();
}
private void Control_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_isDragging = false;
this.ReleaseMouseCapture();
}
private void Control_MouseMove(object sender, MouseEventArgs e)
{
if (_isDragging)
{
var mouseWithinParent = e.GetPosition(Parent as UIElement);
Canvas.SetLeft(this, mouseWithinParent.X - _mouseLocationWithinMe.X);
Canvas.SetTop(this, mouseWithinParent.Y - _mouseLocationWithinMe.Y);
}
}
protected bool _isDragging;
Point _mouseLocationWithinMe;
}
它基本上是科里的例子,但是从Hawlett利用提示。 它的工作原理,只有当父容器为画布。 此外,值得打扮得有一些限制,以保持用户从拖动控制的地方它确实不应该。
Answer 5:
此代码的工作完美!
Button newBtn = new Button();
newBtn.AddHandler(Button.ClickEvent, new RoutedEventHandler(BtTable_Click));
newBtn.AddHandler(Button.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(BtTable_MouseLeftButtonDown));
newBtn.AddHandler(Button.PreviewMouseLeftButtonUpEvent, new MouseButtonEventHandler(BtTable_MouseLeftButtonUp));
newBtn.AddHandler(Button.PreviewMouseMoveEvent, new MouseEventHandler(BtTable_MouseMove));
按钮将
private object movingObject;
private double firstXPos, firstYPos;
private int ButtonSize = 50;
private void BtTable_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Button newBtn = sender as Button;
Canvas canvas = newBtn.Parent as Canvas;
firstXPos = e.GetPosition(newBtn).X;
firstYPos = e.GetPosition(newBtn).Y - ButtonSize;
movingObject = sender;
// Put the image currently being dragged on top of the others
int top = Canvas.GetZIndex(newBtn);
foreach (Button child in canvas.Children)
if (top < Canvas.GetZIndex(child))
top = Canvas.GetZIndex(child);
Canvas.SetZIndex(newBtn, top + 1);
Mouse.Capture(null);
}
private void BtTable_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Button newBtn = sender as Button;
Canvas canvas = newBtn.Parent as Canvas;
movingObject = null;
// Put the image currently being dragged on top of the others
int top = Canvas.GetZIndex(newBtn);
foreach (Button child in canvas.Children)
if (top > Canvas.GetZIndex(child))
top = Canvas.GetZIndex(child);
Canvas.SetZIndex(newBtn, top + 1);
Mouse.Capture(newBtn);
}
private void BtTable_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed && sender == movingObject)
{
Button newBtn = sender as Button;
Canvas canvas = newBtn.Parent as Canvas;
// Horizontal
double newLeft = e.GetPosition(canvas).X - firstXPos - canvas.Margin.Left;
// newLeft inside canvas right-border?
if (newLeft > canvas.Margin.Left + canvas.ActualWidth - newBtn.ActualWidth)
newLeft = canvas.Margin.Left + canvas.ActualWidth - newBtn.ActualWidth;
// newLeft inside canvas left-border?
else if (newLeft < canvas.Margin.Left)
newLeft = canvas.Margin.Left;
newBtn.SetValue(Canvas.LeftProperty, newLeft);
//Vertical
double newTop = e.GetPosition(canvas).Y - firstYPos - canvas.Margin.Top;
// newTop inside canvas bottom-border?
// -- Bottom --
if (newTop > canvas.Margin.Top + canvas.ActualHeight - newBtn.ActualHeight - ButtonSize)
newTop = canvas.Margin.Top + canvas.ActualHeight - newBtn.ActualHeight - ButtonSize;
// newTop inside canvas top-border?
// -- Top --
else if (newTop < canvas.Margin.Top - ButtonSize)
newTop = canvas.Margin.Top - ButtonSize;
newBtn.SetValue(Canvas.TopProperty, newTop);
}
}
快乐编码;)
Answer 6:
我实现了这两个WPF和UWP商店应用。 并添加所有的代码在用户控件本身,而不是它使用它的控制,你可以修改它的面值您的需要。
WPF
public partial class DragUserControl : UserControl
{
public DragUserControl()
{
InitializeComponent();
}
object MovingObject;
double FirstXPos, FirstYPos;
private void Button_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
this.MovingObject = this;
FirstXPos = e.GetPosition(MovingObject as Control).X;
FirstYPos = e.GetPosition(MovingObject as Control).Y;
Canvas canvas = this.Parent as Canvas;
if (canvas != null)
{
canvas.PreviewMouseMove += this.MouseMove;
}
}
private void MouseMove(object sender, MouseEventArgs e)
{
/*
* In this event, at first we check the mouse left button state. If it is pressed and
* event sender object is similar with our moving object, we can move our control with
* some effects.
*/
Canvas canvas = sender as Canvas;
Point canvasPoint = e.GetPosition(canvas);
Point objPosition = e.GetPosition((MovingObject as FrameworkElement));
if (e.LeftButton == MouseButtonState.Pressed)
{
if (MovingObject != null)
{
//This condition will take care that control should not go outside the canvas.
if ((e.GetPosition((MovingObject as FrameworkElement).Parent as FrameworkElement).X - FirstXPos > 0) && (e.GetPosition((MovingObject as FrameworkElement).Parent as FrameworkElement).X - FirstXPos < canvas.ActualWidth - (MovingObject as FrameworkElement).ActualWidth))
{
(MovingObject as FrameworkElement).SetValue(Canvas.LeftProperty, e.GetPosition((MovingObject as FrameworkElement).Parent as FrameworkElement).X - FirstXPos);
}
//This condition will take care that control should not go outside the canvas.
if ((e.GetPosition((MovingObject as FrameworkElement).Parent as FrameworkElement).Y - FirstYPos > 0) && (e.GetPosition((MovingObject as FrameworkElement).Parent as FrameworkElement).Y - FirstYPos < canvas.ActualHeight - (MovingObject as FrameworkElement).ActualHeight))
{
(MovingObject as FrameworkElement).SetValue(Canvas.TopProperty, e.GetPosition((MovingObject as FrameworkElement).Parent as FrameworkElement).Y - FirstYPos);
}
}
}
}
private void Ellipse_PreviewMouseLeftButtonUp_1(object sender, MouseButtonEventArgs e)
{
MovingObject = null;
}
}
Button_MouseLeftButtonDown是希望通过其拖动控制按钮的单击事件。
UWP
public sealed partial class DragUserControl : UserControl
{
MovingObject;
double FirstXPos, FirstYPos;
public DragUserControl()
{
InitializeComponent();
}
private void Ellipse_PointerPressed(object sender, PointerRoutedEventArgs e)
{
this.MovingObject = this;
FirstXPos = e.GetCurrentPoint(MovingObject as Control).Position.X;
FirstYPos = e.GetCurrentPoint(MovingObject as Control).Position.Y;
Canvas canvas = this.Parent as Canvas;
if (canvas != null)
{
canvas.PointerMoved += Canvas_PointerMoved;
}
}
private void Canvas_PointerMoved(object sender, PointerRoutedEventArgs e)
{
if (MovingObject != null)
{
Canvas canvas = sender as Canvas;
Point canvasPoint = e.GetCurrentPoint(canvas).Position;
Point objPosition = e.GetCurrentPoint((MovingObject as FrameworkElement)).Position;
if (e.GetCurrentPoint(MovingObject as Control).Properties.IsLeftButtonPressed) //e.Pointer.IsInContact ==true)
{
//This condition will take care that control should not go outside the canvas
if ((e.GetCurrentPoint((MovingObject as FrameworkElement).Parent as FrameworkElement).Position.X - FirstXPos > 0) && (e.GetCurrentPoint((MovingObject as FrameworkElement).Parent as FrameworkElement).Position.X - FirstXPos < canvas.ActualWidth - (MovingObject as FrameworkElement).ActualWidth))
{
(MovingObject as FrameworkElement).SetValue(Canvas.LeftProperty, e.GetCurrentPoint((MovingObject as FrameworkElement).Parent as FrameworkElement).Position.X - FirstXPos);
}
//This condition will take care that control should not go outside the canvas
if ((e.GetCurrentPoint((MovingObject as FrameworkElement).Parent as FrameworkElement).Position.Y - FirstYPos > 0) && (e.GetCurrentPoint((MovingObject as FrameworkElement).Parent as FrameworkElement).Position.Y - FirstYPos < canvas.ActualHeight - (MovingObject as FrameworkElement).ActualHeight))
{
(MovingObject as FrameworkElement).SetValue(Canvas.TopProperty, e.GetCurrentPoint((MovingObject as FrameworkElement).Parent as FrameworkElement).Position.Y - FirstYPos);
}
}
}
}
private void Ellipse_PointerReleased(object sender, PointerRoutedEventArgs e)
{
MovingObject = null;
}
}
Ellipse_PointerPressed是希望通过其拖动控制椭圆的单击事件。