在 WPF 中向鼠标旋转图形(如模拟表盘)

Posted

技术标签:

【中文标题】在 WPF 中向鼠标旋转图形(如模拟表盘)【英文标题】:Rotate graphic towards mouse in WPF (like an analog dial) 【发布时间】:2010-11-01 01:39:35 【问题描述】:

在 WPF/C# 中,如何旋转“图形”以面对当前鼠标位置?

基本上我想要的是一个“滚轮”UI 控件(如模拟音量转盘)。我希望能够单击并拖动转盘,它会随着鼠标旋转。然后当我释放鼠标时,它会停止跟随(显然!)。

我将如何创建其中之一?是否已经存在于某处?

【问题讨论】:

【参考方案1】:

我还没有看到任何这样的控件(尽管我已经有一段时间没有查看 WPF 控件供应商提供的所有控件了),但创建一个相对简单。

您所要做的就是创建一个自定义控件,其中包含您可以旋转以跟随鼠标的图像(或 XAML 绘图)。然后,将 RotateTransform 绑定到自定义控件上的 'Angle' DependencyProperty,以便在更新 'angle' 时,图像/绘图会旋转以匹配:

<UserControl x:Class="VolumeControlLibrary.VolumeControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:VolumeControlLibrary"
             Height="60" Width="60">
    <Image Source="/VolumeControl;component/knob.png" RenderTransformOrigin="0.5,0.5" >
        <Image.RenderTransform>
            <RotateTransform Angle="Binding RelativeSource=RelativeSource FindAncestor, AncestorType=x:Type local:VolumeControl, Path=Angle"/>
        </Image.RenderTransform>
    </Image>
</UserControl>

将 RenderTransformOrigin 设置为 "0.5, 0.5" 可确保控件围绕其中心旋转,而不是围绕左上角旋转;我们也必须在角度计算中对此进行补偿。

在您的控件的代码隐藏文件中,为鼠标和 Angle DependencyProperty 添加处理程序:

public partial class VolumeControl : UserControl

    // Using a DependencyProperty backing store for Angle.
    public static readonly DependencyProperty AngleProperty =
        DependencyProperty.Register("Angle", typeof(double), typeof(VolumeControl), new UIPropertyMetadata(0.0));

    public double Angle
    
        get  return (double)GetValue(AngleProperty); 
        set  SetValue(AngleProperty, value); 
    

    public VolumeControl()
    
        InitializeComponent();
        this.MouseLeftButtonDown += new MouseButtonEventHandler(OnMouseLeftButtonDown);
        this.MouseUp += new MouseButtonEventHandler(OnMouseUp);
        this.MouseMove += new MouseEventHandler(OnMouseMove);
    

    private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    
        Mouse.Capture(this);
    

    private void OnMouseUp(object sender, MouseButtonEventArgs e)
    
        Mouse.Capture(null);
    

    private void OnMouseMove(object sender, MouseEventArgs e)
    
        if (Mouse.Captured == this)
        
            // Get the current mouse position relative to the volume control
            Point currentLocation = Mouse.GetPosition(this);

            // We want to rotate around the center of the knob, not the top corner
            Point knobCenter = new Point(this.ActualHeight / 2, this.ActualWidth / 2);

            // Calculate an angle
            double radians = Math.Atan((currentLocation.Y - knobCenter.Y) / 
                                       (currentLocation.X - knobCenter.X));
            this.Angle = radians * 180 / Math.PI;

            // Apply a 180 degree shift when X is negative so that we can rotate
            // all of the way around
            if (currentLocation.X - knobCenter.X < 0)
            
                this.Angle += 180;
            
        
    

捕获鼠标确保您的控件将继续获得鼠标更新,即使用户将鼠标从控件上移开(直到他们松开单击),并通过获取鼠标相对于当前元素的位置(控件),无论控件在屏幕上实际呈现的位置如何,您的计算都应该始终相同。

在本例中,当鼠标移动时,我们计算它与控件中心之间的角度,然后将此角度设置为我们创建的 Angle DependencyProperty。由于我们正在显示的图像绑定到此角度属性,因此 WPF 会自动应用新值,这会导致旋钮随着鼠标移动而旋转。

在您的解决方案中使用控件很容易;只需添加:

<local:VolumeControl />

如果您想将旋钮的值绑定到应用程序中的某些内容,您可以绑定到 VolumeControl 上的 Angle 属性;该值当前以度为单位,但可以添加一个附加属性以在以度为单位的角度和对您有意义的值之间进行转换(例如,从 0 到 10 的值)。

【讨论】:

我不太清楚将每一段代码放在哪里。我特别难以让 DependencyProperty 部分正常工作。 好的,我已将所有代码放入用户控件并更新了帖子;您应该能够将代码复制并粘贴到您的解决方案中。 不错的解决方案 - 如果您想更简单,许多控件只需获取鼠标移动的 dY 并将其转换为刻度盘的值。 Paul 提出了一个很好的观点 - 仅使用鼠标在 OnMouseLeftButtonDown 中捕获时的 Y 值差异对于用户来说比实际“扭曲”虚拟更容易控制像示例代码一样拨号。 是的,保罗确实提出了一个好观点!但我希望在触摸屏上使用这个控件,所以你的回答(尼古拉斯)是完美的。谢谢!【参考方案2】:

要添加到该帖子,鼠标点和对象点之间的角度计算如下:

dot = currentLocation.X * objectPosition.X + currentLocation.Y * objectPosition.Y;
angle = Math.Acos(dot);

【讨论】:

【参考方案3】:

在我的例子中,我已经动态创建了应该向鼠标方向旋转的形状。为了解决这个问题,我使用了一个轻量级函数。我只需要以下内容:

当前选定形状的中心点 最后一个鼠标悬停步骤的点 以及当前鼠标悬停步骤中的点

不必使用数学库中的方法。我计算的角度取决于当前鼠标悬停点和前一个鼠标悬停点的差异以及相对于中心点的位置。最后,我在当前对象的现有角度上添加角度。

private void HandleLeftMouseDown(MouseButtonEventArgs eventargs)

    //Calculate the center point of selected object
    //...
    //assuming Point1 is the top left point
    var xCenter = (_selectedObject.Point2.X - _selectedObject.Point1.X) / 2 + _selectedObject.Point1.X
    var yCenter = (_selectedObject.Point2.Y - _selectedObject.Point1.Y) / 2 + _selectedObject.Point1.Y
    _selectedObjectCenterPoint = new Point((double) xCenter, (double) yCenter);

    //init set of last mouse over step with the mouse click point
     var clickPoint = eventargs.GetPosition(source);
    _lastMouseOverPoint = new Point(clickPoint.X,clickPoint.Y);


private void HandleMouseMove(MouseEventArgs eventArgs)

    Point pointMouseOver = eventArgs.GetPosition(_source);                            

    //Get the difference to the last mouse over point
    var xd = pointMouseOver.X - _lastMouseOverPoint.X;
    var yd = pointMouseOver.Y - _lastMouseOverPoint.Y;

    // the direction depends on the current mouse over position in relation to the center point of the shape
    if (pointMouseOver.X < _selectedObjectCenterPoint.X)
        yd *= -1;
    if (pointMouseOver.Y > _selectedObjectCenterPoint.Y)
        xd *= -1;

    //add to the existing Angle   
    //not necessary to calculate the degree measure
    _selectedObject.Angle += (xd + yd);

    //save mouse over point            
    _lastMouseOverPoint = new Point(pointMouseOver.X, pointMouseOver.Y);

【讨论】:

以上是关于在 WPF 中向鼠标旋转图形(如模拟表盘)的主要内容,如果未能解决你的问题,请参考以下文章

如何在OpenGL中实现按住鼠标左键就可以旋转图形的功能

以有限度数旋转表盘

WPF中使用AxisAngleRotation3D实现CAD的2D旋转功能

计算机图形学课程二.MFC鼠标响应函数模拟画图软件

WPF的图形呈现

鼠标拖拽旋转js