C# 在WPF中向鼠标旋转图形(如模拟拨号盘)

C# 在WPF中向鼠标旋转图形(如模拟拨号盘),c#,wpf,user-interface,controls,C#,Wpf,User Interface,Controls,在WPF/C中,如何旋转“图形”以面对当前鼠标位置 基本上,我想要的是一个“轮子”UI控件(如模拟音量拨号盘)。我想能够点击和拖动拨号盘,它会随着鼠标旋转。然后,当我释放鼠标时,它将停止跟随(显然!) 我将如何创建其中一个?某个地方已经存在这样的控件了吗?我还没有见过这样的控件(虽然我已经有一段时间没有看过WPF控件供应商提供的所有控件了),但是创建这样的控件相对简单 您所要做的就是创建一个自定义控件,其中包含一个图像(或XAML图形),您可以旋转该图像以跟随鼠标。然后,将RotateTrans

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

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


我将如何创建其中一个?某个地方已经存在这样的控件了吗?

我还没有见过这样的控件(虽然我已经有一段时间没有看过WPF控件供应商提供的所有控件了),但是创建这样的控件相对简单

您所要做的就是创建一个自定义控件,其中包含一个图像(或XAML图形),您可以旋转该图像以跟随鼠标。然后,将RotateTransform绑定到自定义控件上的“角度”从属属性,以便在更新“角度”时,图像/图形旋转以匹配:

<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>

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

在控件的代码隐藏文件中,添加鼠标和角度相关属性的处理程序:

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;
            }
        }
    }
}
公共部分类VolumeControl:UserControl
{
//对角度使用DependencyProperty后台存储。
公共静态只读从属属性AngleProperty=
DependencyProperty.Register(“角度”、typeof(双精度)、typeof(VolumeControl)、新UIPropertyMetadata(0.0));
公共双角度
{
获取{return(double)GetValue(AngleProperty);}
set{SetValue(AngleProperty,value);}
}
公共卷控制()
{
初始化组件();
this.MouseLeftButtonDown+=新的MouseButtonEventHandler(OnMouseLeftButtonDown);
this.MouseUp+=新的MouseButtonEventHandler(OnMouseUp);
this.MouseMove+=新的MouseEventHandler(OnMouseMove);
}
MouseLeftButtonDown上的私有void(对象发送器,MouseButtonEventArgs e)
{
鼠标。捕捉(这个);
}
MouseUp上的私有void(对象发送器,鼠标按钮ventargs e)
{
鼠标捕捉(空);
}
MouseMove上的私有void(对象发送方,MouseEventArgs e)
{
if(Mouse.Captured==此)
{
//获取相对于音量控件的当前鼠标位置
Point currentLocation=Mouse.GetPosition(此);
//我们想绕着旋钮的中心旋转,而不是上角
点旋钮中心=新点(this.ActualHeight/2,this.ActualWidth/2);
//计算角度
双弧度=Math.Atan((currentLocation.Y-knobCenter.Y)/
(currentLocation.X-knobCenter.X));
角度=弧度*180/Math.PI;
//当X为负时,应用180度移动,以便我们可以旋转
//一路走来
if(currentLocation.X-knobCenter.X<0)
{
该角度+=180;
}
}
}
}
捕获鼠标可确保即使用户将鼠标从控件上移开(直到他们放开鼠标),并且通过获取鼠标相对于当前元素(控件)的位置,控件也会继续获得鼠标更新,无论控件在屏幕上的实际渲染位置如何,您的计算应该始终相同

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

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

<local:VolumeControl />


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

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

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

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

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

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);
}
private void HandleLeftMouseDown(鼠标按钮ventargs事件参数)
{
//计算选定对象的中心点
//...
//假设点1是左上角的点
var xCenter=(_selectedObject.Point2.X-_selectedObject.Point1.X)/2+_selectedObject.Point1.X
变量yCenter=(_selectedObject.Point2.Y-_selectedObject.Point1.Y)/2+_selectedObject.Point1.Y
_选择对象中心点=新点((双)X中心,(双)Y中心);
//用鼠标点击点初始化最后一步的鼠标集
var clickPoint=eventargs.GetPosition(源);
_lastMouseOverPoint=新点(clickPoint.X,clickPoint.Y);
}
私有void HandleMouseMove(MouseEventArgs eventArgs)
{
Point pointMouseOver=eventArgs.GetPosition(_source);
//获得鼠标在最后一点上的差值
var xd=pointMouseOver.X-\u lastMouseOverPoint.X;
var yd=鼠标指针在.Y-\u lastMo上