Поворот графики в направлении мыши в WPF (как аналоговый диск) - PullRequest
7 голосов
/ 08 июня 2009

В WPF / C # как мне повернуть «графику» для отображения текущей позиции мыши?

По сути, мне нужен элемент управления «колесом» (например, аналоговый регулятор громкости ). Я хочу иметь возможность щелкнуть и перетащить диск, и он будет вращаться, чтобы следовать за мышью. Затем, когда я отпущу мышь, она перестанет следовать (очевидно!).

Как бы я создал один из них? кто-то уже существует?

Ответы [ 3 ]

19 голосов
/ 08 июня 2009

Я не видел ни одного подобного элемента управления (хотя я давно изучал все элементы управления, предлагаемые поставщиками элементов управления WPF), но создать его довольно просто.

Все, что вам нужно сделать, это создать собственный элемент управления, содержащий изображение (или чертеж XAML), который вы можете вращать, следуя за мышью. Затем привяжите RotateTransform к свойству DependencyProperty «Angle» в вашем пользовательском элементе управления, чтобы при обновлении «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;
            }
        }
    }
}

Захват мыши гарантирует, что ваш элемент управления продолжит получать обновления мыши, даже когда пользователь отключается от элемента управления (пока он не отпустит щелчок), и путем определения положения мыши относительно текущего элемента ( control), ваши расчеты всегда должны быть одинаковыми независимо от того, где элемент управления отображается на экране.

В этом примере, когда мышь перемещается, мы вычисляем угол между ней и центром элемента управления, а затем устанавливаем этот угол равным Angle DependencyProperty, который мы создали. Поскольку изображение, которое мы показываем, привязано к этому свойству угла, WPF автоматически применяет новое значение, что приводит к вращению ручки в сочетании с движением мыши.

Использовать элемент управления в вашем решении легко; просто добавьте:

<local:VolumeControl />

Вы бы привязали к свойству Angle на VolumeControl, если бы вы хотели привязать значение ручки к чему-либо в вашем приложении; это значение в настоящее время в градусах, но может добавить дополнительное свойство для преобразования между углом в градусах и значением, которое имеет смысл для вас (скажем, значение от 0 до 10).

3 голосов
/ 08 июня 2009

Чтобы добавить к этому сообщению, угол между точкой мыши и точкой объекта вычисляется как:

dot = currentLocation.X * objectPosition.X + currentLocation.Y * objectPosition.Y;
angle = Math.Acos(dot);
0 голосов
/ 03 сентября 2015

В моем случае у меня есть динамически созданные фигуры, которые должны вращаться в направлении мыши. Чтобы решить эту проблему, я использовал легкую функцию. Все, что мне нужно, это следующее:

  • Центральная точка текущей выбранной фигуры
  • Точка последнего шага наведения мыши
  • И точка текущего шага наведения мыши

Нет необходимости использовать методы из библиотеки Math. Я вычисляю угол, который зависит от разности текущей мыши над точкой и предыдущей мыши над точкой и положения относительно центральной точки. Наконец, я добавляю угол к существующему углу текущего объекта.

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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...