WPF使用鼠标移动元素

4

我将尝试在WPF应用程序中使用鼠标移动按钮。

有一个XAML网格,它是结构的根:

<Grid Name="MyGrid"            >
    <Button Name="Samplebutton"
            PreviewMouseDown="Samplebutton_PreviewMouseDown"
            PreviewMouseUp="Samplebutton_PreviewMouseUp"
            PreviewMouseMove="Samplebutton_PreviewMouseMove"
            Content="Moving" Width="100" Height="35"/>
</Grid>

代码后台:

private bool _isMoving;


private void Samplebutton_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    _isMoving = true;
}

private void Samplebutton_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
    _isMoving = false;
}

private void Samplebutton_PreviewMouseMove(object sender, MouseEventArgs e)
{
    if (!_isMoving) return;

    TranslateTransform transform = new TranslateTransform();
    transform.X = Mouse.GetPosition(MyGrid).X;
    transform.Y = Mouse.GetPosition(MyGrid).Y;
    this.Samplebutton.RenderTransform = transform;
}

首先点击按钮会将其移动到远离的位置,然后我可以移动按钮,但第一次移动时按钮会被移开。我错过了什么?


TranslateTransform是按钮相对于原始位置的相对位移。鼠标位置在网格元素内是绝对位置。我认为您需要进行一些数学计算,减去按钮的原始位置,以将绝对值转换为相对距离。 - Auskennfuchs
4个回答

9

这是一篇老文章,但我认为以下是更好的方法:

我认为您不应该使用布尔值来检查鼠标是否拖动当前控件。此外,使用PreviewMouse....无法解决获取鼠标位置的问题(当鼠标不在控件上方时)

您应该捕获鼠标。当您捕获了鼠标后,即使鼠标不在控件上方(甚至当鼠标离开当前窗口时),鼠标移动事件仍然会被触发。我认为这是正确的方法。它更简单易读。

以下是代码:

public partial class VisualBlock : UserControl
{
    private Point _positionInBlock;

    public VisualBlock()
    {
        InitializeComponent();
    }

    private void UserControl_MouseDown(object sender, MouseButtonEventArgs e)
    {
        // when the mouse is down, get the position within the current control. (so the control top/left doesn't move to the mouse position)
        _positionInBlock = Mouse.GetPosition(this);

        // capture the mouse (so the mouse move events are still triggered (even when the mouse is not above the control)
        this.CaptureMouse();
    }

    private void UserControl_MouseMove(object sender, MouseEventArgs e)
    {
        // if the mouse is captured. you are moving it. (there is your 'real' boolean)
        if (this.IsMouseCaptured)
        {
            // get the parent container
            var container = VisualTreeHelper.GetParent(this) as UIElement;

            if(container == null)
                return;
            
            // get the position within the container
            var mousePosition = e.GetPosition(container);

            // move the usercontrol.
            this.RenderTransform = new TranslateTransform(mousePosition.X - _positionInBlock.X, mousePosition.Y - _positionInBlock.Y);
        }
    }

    private void UserControl_MouseUp(object sender, MouseButtonEventArgs e)
    {
        // release this control.
        this.ReleaseMouseCapture();
    }
}

更新:
如果你有一个窗口,比如一个画布,你可以将这些用户控件添加到画布中。
以下是它的要点:WPF移动控件
<Window x:Class="WPFTestControlMoveMouse.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPFTestControlMoveMouse"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30" />
            <RowDefinition />
        </Grid.RowDefinitions>

        <Button Grid.Row="0" Click="Button_Click">New</Button>
        <Canvas x:Name="MyCanvas" Grid.Row="1"></Canvas>
    </Grid>
</Window>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WPFTestControlMoveMouse
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            MyCanvas.Children.Add(new VisualBlock());
        }
    }
}

<UserControl x:Class="WPFTestControlMoveMouse.VisualBlock"
             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:WPFTestControlMoveMouse"
             mc:Ignorable="d" 
             Width="300" Height="200" MouseDown="UserControl_MouseDown" MouseUp="UserControl_MouseUp" MouseMove="UserControl_MouseMove">
    <Grid>
        <Border BorderBrush="Black" BorderThickness="1" Background="Silver" />
        <TextBlock FontSize="48" HorizontalAlignment="Center" VerticalAlignment="Center">Hi there!!</TextBlock>
    </Grid>
</UserControl>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WPFTestControlMoveMouse
{
    /// <summary>
    /// Interaction logic for VisualBlock.xaml
    /// </summary>
    public partial class VisualBlock : UserControl
    {
        private Point _positionInBlock;

        public VisualBlock()
        {
            InitializeComponent();
        }

        private void UserControl_MouseDown(object sender, MouseButtonEventArgs e)
        {
            // when the mouse is down, get the position within the current control. (so the control top/left doesn't move to the mouse position)
            _positionInBlock = Mouse.GetPosition(this);

            // capture the mouse (so the mouse move events are still triggered (even when the mouse is not above the control)
            this.CaptureMouse();
        }

        private void UserControl_MouseMove(object sender, MouseEventArgs e)
        {
            // if the mouse is captured. you are moving it. (there is your 'real' boolean)
            if (this.IsMouseCaptured)
            {
                // get the parent container
                var container = VisualTreeHelper.GetParent(this) as UIElement;

                // get the position within the container
                var mousePosition = e.GetPosition(container);

                // move the usercontrol.
                this.RenderTransform = new TranslateTransform(mousePosition.X - _positionInBlock.X, mousePosition.Y - _positionInBlock.Y);
            }
        }

        private void UserControl_MouseUp(object sender, MouseButtonEventArgs e)
        {
            // release this control.
            this.ReleaseMouseCapture();
        }
    }
}

通过从标签控件派生来使用鼠标移动标签:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace WPFTestControlMoveMouse
{
    public class MyLabel : Label
    {
        private Point _positionInBlock;

        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            // when the mouse is down, get the position within the current control. (so the control top/left doesn't move to the mouse position)
            _positionInBlock = Mouse.GetPosition(this);

            // capture the mouse (so the mouse move events are still triggered (even when the mouse is not above the control)
            this.CaptureMouse();
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            // if the mouse is captured. you are moving it. (there is your 'real' boolean)
            if (this.IsMouseCaptured)
            {
                // get the parent container
                var container = VisualTreeHelper.GetParent(this) as UIElement;

                // get the position within the container
                var mousePosition = e.GetPosition(container);

                // move the usercontrol.
                this.RenderTransform = new TranslateTransform(mousePosition.X - _positionInBlock.X, mousePosition.Y - _positionInBlock.Y);
            }
        }
        protected override void OnMouseUp(MouseButtonEventArgs e)
        {
            // release this control.
            this.ReleaseMouseCapture();
        }

    }
}

看起来不错,但你能添加关于如何实际使用它的信息吗?例如,如果窗口上有一个标签,如何使用它允许用户移动标签? - SezMe
非常感谢。我还有一个问题:我尝试在一个简单的控件(标签)上使用它,但它没有随鼠标移动。为什么这对用户控件有效,而对“标准”控件无效?或者是我做错了什么? - SezMe

8
以下是您问题的完整解决方案:
private bool _isMoving;
private Point? _buttonPosition;
private double deltaX;
private double deltaY;
private TranslateTransform _currentTT;

private void Samplebutton_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    if(_buttonPosition == null)
        _buttonPosition = Samplebutton.TransformToAncestor(MyGrid).Transform(new Point(0, 0));
    var mousePosition = Mouse.GetPosition(MyGrid);
    deltaX = mousePosition.X - _buttonPosition.Value.X;
    deltaY = mousePosition.Y - _buttonPosition.Value.Y;
    _isMoving = true;
}

private void Samplebutton_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
    _currentTT = Samplebutton.RenderTransform as TranslateTransform;
    _isMoving = false;
}

private void Samplebutton_PreviewMouseMove(object sender, MouseEventArgs e)
{
    if (!_isMoving) return;

    var mousePoint = Mouse.GetPosition(MyGrid);

    var offsetX = (_currentTT == null ?_buttonPosition.Value.X : _buttonPosition.Value.X - _currentTT.X) + deltaX - mousePoint.X;
    var offsetY = (_currentTT == null ? _buttonPosition.Value.Y : _buttonPosition.Value.Y - _currentTT.Y) + deltaY - mousePoint.Y;

    this.Samplebutton.RenderTransform = new TranslateTransform(-offsetX, -offsetY);
}

2

您需要在第一次记录鼠标位置,并始终使用“相对”X/Y位置

private double _startX; // Or whatever number type Mouse.GetPosition(MyGrid).X returns
private double _startY; // Or whatever number type Mouse.GetPosition(MyGrid).Y returns   

private void Samplebutton_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    _startX = Mouse.GetPosition(MyGrid).X;
    _startY = Mouse.GetPosition(MyGrid).Y;
    _isMoving = true;
}

private void Samplebutton_PreviewMouseMove(object sender, MouseEventArgs e)
{
    if (!_isMoving) return;

    TranslateTransform transform = new TranslateTransform();
    transform.X = Mouse.GetPosition(MyGrid).X - _startX;
    transform.Y = Mouse.GetPosition(MyGrid).Y - _startY;
    this.Samplebutton.RenderTransform = transform;
}

1

Samplebutton_PreviewMouseUp我认为有_isMoving = false就足够了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接