2009-06-07 7 views
7

Dans WPF/C#, comment faire pivoter un "graphique" pour faire face à la position actuelle de la souris?Faire pivoter le graphique vers la souris dans WPF (comme un cadran analogique)

Fondamentalement, ce que je veux, c'est un contrôle d'interface utilisateur "roue" (comme un analogique volume volume). Je veux être capable de cliquer et de faire glisser le cadran et il va tourner pour suivre la souris. Puis, quand je relâcherai la souris, elle arrêtera de suivre (évidemment!).

Comment pourrais-je en créer un? existe-t-il déjà quelque part?

Répondre

19

Je n'ai vu aucun contrôle comme celui-ci (bien que cela fait un moment que j'ai regardé tous les contrôles que les fournisseurs de contrôle WPF offraient), mais il est relativement simple d'en créer un.

Tout ce que vous avez à faire est de créer un contrôle personnalisé contenant une image (ou un dessin XAML) que vous pouvez faire pivoter pour suivre la souris. Ensuite, lier un RotateTransform à un DependencyProperty « Angle » sur votre contrôle personnalisé de sorte que lorsque « angle » est mis à jour, l'image/dessin tourne pour correspondre:

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

Réglage RenderTransformOrigin « 0,5, 0,5 » assure que la le contrôle tourne autour de son centre, plutôt que de tourner autour du coin supérieur gauche; nous devrons compenser cela dans le calcul de l'angle aussi.

Dans le fichier code-behind pour votre contrôle, ajouter des gestionnaires pour la souris et l'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; 
      } 
     } 
    } 
} 

La capture de la souris assure que votre contrôle continuera d'obtenir des mises à jour de souris même lorsque l'utilisateur mouses hors de le contrôle (jusqu'à ce qu'ils relâchent le clic), et en obtenant la position de la souris par rapport à l'élément courant (le contrôle), votre calcul devrait toujours être le même quel que soit le rendu du contrôle à l'écran.

Dans cet exemple, lorsque la souris se déplace, nous calculons l'angle entre celle-ci et le centre du contrôle, puis définissons cet angle sur Angle DependencyProperty que nous avons créé. Étant donné que l'image que nous affichons est liée à cette propriété d'angle, WPF applique automatiquement la nouvelle valeur, ce qui entraîne la rotation du bouton en combinaison avec le déplacement de la souris. L'utilisation du contrôle dans votre solution est facile; il suffit d'ajouter:

<local:VolumeControl /> 

Vous lierait à la propriété Angle sur VolumeControl si vous voulez lier la valeur du bouton à quelque chose dans votre application; cette valeur est actuellement en degrés, mais pourrait ajouter une propriété supplémentaire pour convertir entre un angle en degrés et une valeur qui a du sens pour vous (par exemple, une valeur de 0 à 10).

+0

Je ne sais pas trop où mettre chaque bit de code. J'ai particulièrement du mal à faire fonctionner la partie DependencyProperty. –

+1

OK, j'ai mis tout le code dans un contrôle utilisateur et mis à jour le poste; vous devriez être capable de copier et coller le code dans votre solution. –

+2

Belle solution - si vous vouliez aller plus simple, beaucoup de contrôles prennent juste le dY du mouvement de la souris et traduisent cela à la valeur du cadran. –

3

Pour ajouter à ce poste, l'angle entre le point de la souris et le point d'objet est calculé comme:

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

Dans mon cas, j'ai créé dynamiquement des formes qui sont mis en rotation vers la direction de la souris. Pour résoudre ce problème, j'ai utilisé une fonction légère.Tout ce que je besoin est le suivant:

  • Le Centerpoint de la forme actuelle sélectionnée
  • Le point de la dernière étape mouseover
  • Et le point de l'étape mouseover actuelle

Il n'est pas nécessaire utiliser des méthodes de la bibliothèque Math. Je calcule l'angle qui dépend de la différence entre le point de souris actuel et le point de souris précédent et la position par rapport au point central. Enfin j'ajoute l'angle sur l'angle existant de l'objet courant.

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); 
} 
Questions connexes