2009-06-07 15 views
7

En WPF/C# ¿cómo iba a girar un "gráfico" para enfrentar la posición actual del ratón?gráfico del ratón en WPF (como una línea analógica)

Básicamente lo que quiero es un control de UI "rueda" (como un dial de volumen analógico). Quiero poder hacer clic y arrastrar el dial y rotará para seguir el mouse. Luego, cuando suelte el mouse, dejará de seguirlo (¡obviamente!).

¿Cómo puedo crear uno de estos? ¿Ya existe uno en alguna parte?

Respuesta

19

no he visto ningún tipo de control como éste en todo (a pesar de que ha pasado un tiempo desde que miraba todos los controles que los proveedores de control de WPF estaban ofreciendo), pero es relativamente sencillo para crear una.

Todo lo que tendría que hacer es crear un control personalizado que contiene una imagen (o dibujo XAML) que se puede girar para seguir al ratón. A continuación, enlazar un RotateTransform a un DependencyProperty 'Angle' en el control personalizado de manera que cuando se actualiza 'ángulo', la imagen/dibujo gira para que coincida con:

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

Configuración RenderTransformOrigin a "0,5, 0,5" garantiza que la el control gira alrededor de su centro, en lugar de girar alrededor de la esquina superior izquierda; también tendremos que compensar esto en el cálculo del ángulo.

En el código detrás de archivo para su control, agregar controladores para el ratón y el DependencyProperty Ángulo:

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

captura el ratón asegura que su control continuará recibiendo actualizaciones del ratón, incluso cuando los ratones de usuario fuera de el control (hasta que suelten el clic), y al obtener la posición del mouse relativa al elemento actual (el control), su cálculo siempre debe ser el mismo independientemente de dónde se represente realmente el control en la pantalla.

En este ejemplo, cuando se mueve el ratón se calcula el ángulo entre ella y el centro del control, y a continuación, ajuste este ángulo a la DependencyProperty ángulo que hemos creado. Como la imagen que mostramos está vinculada a esta propiedad de ángulo, WPF aplica automáticamente el nuevo valor, lo que hace que la perilla gire en combinación con el movimiento del mouse.

Uso del control en su solución es fácil; sólo tiene que añadir:

<local:VolumeControl /> 

Se podría obligar a la propiedad de ángulo de VolumeControl si desea enlazar el valor de la perilla para algo en su aplicación; ese valor está actualmente en grados, pero podría agregar una propiedad adicional para convertir un ángulo en grados y un valor que tenga sentido para usted (por ejemplo, un valor de 0 a 10).

+0

No puedo entender dónde poner cada bit de código. Estoy teniendo problemas especialmente para hacer funcionar la parte DependencyProperty. –

+1

Bien, puse todo el código en un control de usuario y actualicé la publicación; debería poder copiar y pegar el código en su solución. –

+2

Buena solución: si quieres ir más simple, muchos controles simplemente toman el dY del movimiento del mouse y lo traducen al valor del dial. –

3

para añadir a dicho puesto, el ángulo entre el punto de ratón y el punto del objeto se calcula como:

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

En mi caso he creado dinámicamente formas que serán giradas hacia la dirección del ratón. Para resolver esto, utilicé una función ligera.Todo lo que necesito es el siguiente:

  • El punto central de la forma seleccionada actual
  • El punto de la última etapa al pasar el ratón
  • Y el punto del paso encima del ratón actual

No es necesario usar métodos de la biblioteca Math. Calculo el ángulo que depende de la diferencia entre el punto actual del mouse y el mouse anterior y la posición en relación con el punto central. Finalmente agrego el ángulo en el ángulo existente del objeto actual.

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); 
} 
Cuestiones relacionadas