WPF C#如何使用鼠标使控件可拖动
Posted
技术标签:
【中文标题】WPF C#如何使用鼠标使控件可拖动【英文标题】:WPF C# How to Make Control Draggable with Mouse 【发布时间】:2019-09-11 06:06:44 【问题描述】:我正在尝试制作一个在画布上拖动的 UserControl。我正在使用 C# 和 WPF。我在网上看到很多例子,但我只需要最低限度的。
找到一篇文章:《WPF中的可拖动控件》
有人回复:
如果您想手动操作,请使用以下算法:
在 MouseDown 事件中:保存鼠标位置、控件的 TopLeft 位置和这些坐标的 delta(offset),并设置一些布尔字段标志,例如。 IsDragStartted 为真。 在 MouseMove 上检查是否开始拖动并使用鼠标位置和偏移量来计算控件的 TopLeft 位置的新值
在 MouseUp 事件中将 IsDragStarted 设置为 false
我无法应用这个。
公共部分类 UserControl1:UserControl
private Point startingMousePosition;
private Point endingMousePosition;
private Point startingControlPosition;
bool isDragStarted;
public UserControl1()
InitializeComponent();
private void Grid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
if(!isDragStarted)
startingControlPosition.X = Canvas.GetLeft(this);
startingControlPosition.Y = Canvas.GetTop(this);
startingMousePosition.X = e.GetPosition(this.Parent as Canvas).X;
startingMousePosition.Y = e.GetPosition(this.Parent as Canvas).Y;
private void Grid_PreviewMouseMove(object sender, MouseEventArgs e)
if (e.LeftButton == MouseButtonState.Pressed)
isDragStarted = true;
if (isDragStarted)
endingMousePosition.X = e.GetPosition(this.Parent as Canvas).X;
endingMousePosition.Y = e.GetPosition(this.Parent as Canvas).Y;
Canvas.SetLeft(this, endingMousePosition.X - startingControlPosition.X);
Canvas.SetTop(this, endingMousePosition.Y - startingControlPosition.Y);
private void Grid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
isDragStarted = false;
这是我的主窗口 WPF 表单代码:
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
UserControl1 userCTL;
public MainWindow()
InitializeComponent();
userCTL = new UserControl1();
userCTL.Width = 50;
userCTL.Height = 100;
Canvas.SetTop(userCTL,20);
Canvas.SetLeft(userCTL, 20);
CanvasMain.Children.Add(userCTL);
private void Window_Loaded(object sender, RoutedEventArgs e)
AdornerLayer myAdornerLayer = AdornerLayer.GetAdornerLayer(userCTL);
if (myAdornerLayer != null)
myAdornerLayer.Add(new SimpleCircleAdorner(userCTL));
这是我的主窗口的 WPF 代码:
<Window x:Class="WpfApplicationEvent.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="374.306" Width="594.271" Loaded="Window_Loaded">
<Grid>
<ScrollViewer Margin="46,23,74,33" PanningMode="Both" HorizontalScrollBarVisibility="Visible">
<Canvas x:Name="CanvasMain" Height="395" Width="506">
</Canvas>
</ScrollViewer>
</Grid>
</Window>
我还尝试放置装饰器,以便最终调整控件的大小。与装饰者一样无处可去,但他们什么也没做。
我在我创建的 UserControl1 中有我所有的拖动控件,我可以拖动它,但是当我再次单击 UserControl1 实例以再次拖动它时,它会重置为 SetTop(0) 和 SetLeft (0) 位置。它跳到那里太奇怪了!我期待将 UserControl1 实例拖到光标的位置。它在第一次尝试时执行,但随后我单击 UserControl1 再次拖动它,它会跳转到 (0,0) 或接近它。
【问题讨论】:
【参考方案1】:这里有几个问题...
-
PreviewMouseDown 事件处理程序应该添加到您的子控件中,否则您将不知道您正在拖动哪个对象。
您将在父 Canvas 上拖动,因此请将 MouseMove 处理程序添加到其中。
再次在 Canvas 控件上调用 CaptureMouse(),并在拖动过程中保持捕获。
计算鼠标相对于您拖动的控件左上角的位置,然后在每次设置位置时反向应用该偏移量。这会使您在拖动过程中单击鼠标光标的点保持不变,并阻止它“跳”到新位置,这对用户来说很烦人。
所以您的画布 XAML 现在应该如下所示:
<Canvas x:Name="CanvasMain" Height="395" Width="506"
PreviewMouseMove="CanvasMain_PreviewMouseMove"
PreviewMouseUp="CanvasMain_PreviewMouseUp" />
您的代码隐藏应该如下所示:
public MainWindow()
InitializeComponent();
var userCTL = new UserControl(); // <-- replace with your own control
userCTL.Background = Brushes.Blue; // <-- added this so I can see it
userCTL.Width = 50;
userCTL.Height = 100;
Canvas.SetTop(userCTL, 20);
Canvas.SetLeft(userCTL, 20);
userCTL.PreviewMouseDown += UserCTL_PreviewMouseDown;
CanvasMain.Children.Add(userCTL);
UIElement dragObject = null;
Point offset;
private void UserCTL_PreviewMouseDown(object sender, MouseButtonEventArgs e)
this.dragObject = sender as UIElement;
this.offset = e.GetPosition(this.CanvasMain);
this.offset.Y -= Canvas.GetTop(this.dragObject);
this.offset.X -= Canvas.GetLeft(this.dragObject);
this.CanvasMain.CaptureMouse();
private void CanvasMain_PreviewMouseMove(object sender, MouseEventArgs e)
if (this.dragObject == null)
return;
var position = e.GetPosition(sender as IInputElement);
Canvas.SetTop(this.dragObject, position.Y - this.offset.Y);
Canvas.SetLeft(this.dragObject, position.X - this.offset.X);
private void CanvasMain_PreviewMouseUp(object sender, MouseButtonEventArgs e)
this.dragObject = null;
this.CanvasMain.ReleaseMouseCapture();
您可能还想向 Canvas 添加一个 MouseLeave 处理程序,以阻止用户将控件拖动到可见客户区之外。
【讨论】:
非常感谢。我只有几个问题。这个 dragObject 是什么,为什么要使用它? 另外你怎么把对象转换成“FrameworkElement”? CanvasMain.CaptureMouse() 做什么以及为什么使用它?还有“ReleaseMouseCapture() 是做什么的? dragObject 只是指向您当前正在拖动的对象,它与使用布尔值基本相同。当您稍后扩展拖动功能时,保留对实际对象本身的引用会派上用场。我最初确实将其设为 FrameworkElement,但将其更改为 IInputElement,因为这是 Canvas.SetTop 等所期望的。如果需要,您可以使用 bool 或将其设为您的自定义控件类型,但通常最好的编程习惯是引用最“基”的类或接口。 捕获鼠标会导致所有鼠标事件消息被定向到捕获它的窗口,即使用户将光标移到窗口外也是如此。 ReleaseMouseCapture 只是将其恢复为正常状态,以便用户可以恢复与其他窗口等交互。尝试将这两行去掉,您会看到拖动行为有多么不同。以上是关于WPF C#如何使用鼠标使控件可拖动的主要内容,如果未能解决你的问题,请参考以下文章