53 votes

Curseur personnalisé dans WPF ?

Je veux utiliser une image ou une icône comme curseur personnalisé dans une application WPF. Comment puis-je le faire ?

38voto

PeterAllenWebb Points 4731

Vous avez deux options de base :

  1. Lorsque le curseur de la souris se trouve sur votre commande, masquez le curseur du système en réglant le paramètre this.Cursor = Cursors.None; et dessinez votre propre curseur en utilisant la technique de votre choix. Ensuite, mettez à jour la position et l'apparence de votre curseur en répondant aux événements de la souris. Voici deux exemples :
  1. Créez un nouvel objet Curseur en chargeant une image à partir d'un fichier .cur ou .ani. Vous pouvez créer et modifier ces types de fichiers dans Visual Studio. Il existe également quelques utilitaires gratuits pour les traiter. Il s'agit essentiellement d'images (ou d'images animées) qui spécifient un "point chaud" indiquant l'endroit de l'image où se trouve le curseur.

Si vous choisissez de charger à partir d'un fichier, notez que vous avez besoin d'un chemin absolu du système de fichiers pour utiliser la fonction Cursor(string fileName) constructeur. C'est dommage, un chemin relatif ou un Pack URI ne fonctionnera pas. Si vous devez charger le curseur à partir d'un chemin relatif ou d'une ressource empaquetée avec votre assemblage, vous devrez obtenir un flux à partir du fichier et le passer à la fonction Cursor(Stream cursorStream) constructeur. Agaçant mais vrai.

D'autre part, le fait de spécifier un curseur comme un chemin relatif lors de son chargement à l'aide d'un attribut XAML fait un fait que vous pourriez utiliser pour faire charger votre curseur sur un contrôle caché et ensuite copier la référence pour l'utiliser sur un autre contrôle. Je ne l'ai pas essayé, mais cela devrait fonctionner.

2 votes

Notez également que vous pouvez construire votre curseur à la volée à partir de n'importe quel contenu WPF. Voir stackoverflow.com/questions/2835502/ pour un exemple de la façon dont cela est fait.

0 votes

Le lien que j'ai posté dans le commentaire précédent traite de la rotation d'un curseur existant. Je viens de poster une nouvelle réponse à cette question (voir ci-dessous) qui explique comment convertir un visuel arbitraire en curseur.

35voto

Ben McIntosh Points 753

Comme Peter a mentionné Si vous disposez déjà d'un fichier .cur, vous pouvez l'utiliser comme ressource intégrée en créant un élément fictif dans la section des ressources, puis en faisant référence au curseur de l'élément fictif lorsque vous en avez besoin.

Par exemple, supposons que vous souhaitiez afficher des curseurs non standard en fonction de l'outil sélectionné.

Ajouter aux ressources :

<Window.Resources>
    <ResourceDictionary>
        <TextBlock x:Key="CursorGrab" Cursor="Resources/Cursors/grab.cur"/>
        <TextBlock x:Key="CursorMagnify" Cursor="Resources/Cursors/magnify.cur"/>
    </ResourceDictionary>
</Window.Resources>

Exemple de curseur incorporé référencé dans le code :

if (selectedTool == "Hand")
    myCanvas.Cursor = ((TextBlock)this.Resources["CursorGrab"]).Cursor;
else if (selectedTool == "Magnify")
    myCanvas.Cursor = ((TextBlock)this.Resources["CursorMagnify"]).Cursor;
else
    myCanvas.Cursor = Cursor.Arrow;

3 votes

Y a-t-il une raison pour laquelle vous avez utilisé un TextBlock pour mettre en cache les références du curseur par rapport au FrameworkElement, où la propriété Cursor est définie en premier ?

4 votes

Aucune raison ; FrameworkElement serait un meilleur choix. Merci !

17voto

Ray Burns Points 38537

Il existe un moyen plus simple que de gérer soi-même l'affichage du curseur ou d'utiliser Visual Studio pour construire de nombreux curseurs personnalisés.

Si vous avez un FrameworkElement, vous pouvez construire un curseur à partir de celui-ci en utilisant le code suivant :

public Cursor ConvertToCursor(FrameworkElement visual, Point hotSpot)
{
  int width = (int)visual.Width;
  int height = (int)visual.Height;

  // Render to a bitmap
  var bitmapSource = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
  bitmapSource.Render(visual);

  // Convert to System.Drawing.Bitmap
  var pixels = new int[width*height];
  bitmapSource.CopyPixels(pixels, width, 0);
  var bitmap = new System.Drawing.Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
  for(int y=0; y<height; y++)
    for(int x=0; x<width; x++)
      bitmap.SetPixel(x, y, Color.FromArgb(pixels[y*width+x]));

  // Save to .ico format
  var stream = new MemoryStream();
  System.Drawing.Icon.FromHandle(resultBitmap.GetHicon()).Save(stream);

  // Convert saved file into .cur format
  stream.Seek(2, SeekOrigin.Begin);
  stream.WriteByte(2);
  stream.Seek(10, SeekOrigin.Begin);
  stream.WriteByte((byte)(int)(hotSpot.X * width));
  stream.WriteByte((byte)(int)(hotSpot.Y * height));
  stream.Seek(0, SeekOrigin.Begin);

  // Construct Cursor
  return new Cursor(stream);
}

Notez que la taille de votre FrameworkElement doit être une taille de curseur standard (par exemple 16x16 ou 32x32), par exemple :

<Grid x:Name="customCursor" Width="32" Height="32">
  ...
</Grid>

Il serait utilisé comme ceci :

someControl.Cursor = ConvertToCursor(customCursor, new Point(0.5, 0.5));

Évidemment, votre FrameworkElement pourrait être un <Image> si vous avez une image existante, ou vous pouvez dessiner ce que vous voulez en utilisant les outils de dessin intégrés de WPF.

Veuillez noter que des détails sur le format de fichier .cur peuvent être trouvés à l'adresse suivante ICO (format de fichier) .

3 votes

J'ai essayé d'utiliser cet extrait de code pour définir un curseur personnalisé avec xaml. Malheureusement, il n'affiche rien au lieu de l'icône <Image /> -élément que j'ai défini. En déboguant le code, je me suis rendu compte que l'élément var pixels -Le tableau contient juste 0 pour chaque pixel après l'élément CopyPixels() -méthode run. J'ai obtenu une erreur pour le stride -pour l'option CopyPixels() -J'ai donc modifié un peu le code en fonction d'autres extraits que j'ai trouvés : int stride = width * ((bitmapSource.Format.BitsPerPixel + 7) / 8); Sauf que le code est le même que ci-dessus. Le site visual est : <Image Height="32" Width="32"/>

9voto

paulmcguinness Points 51

J'ai voulu charger un fichier curseur personnalisé à partir des ressources du projet et j'ai rencontré des problèmes similaires. J'ai cherché une solution sur Internet et je n'ai pas trouvé ce dont j'avais besoin : définir le paramètre this.Cursor à un curseur personnalisé stocké dans le dossier des ressources de mon projet au moment de l'exécution. J'ai essayé la solution xaml de Ben, mais je ne l'ai pas trouvée assez élégante. PeterAllen a déclaré :

Un chemin d'accès relatif ou un Pack URI ne fonctionneront pas. Si vous devez charger le curseur à partir d'un chemin relatif ou d'une ressource empaquetée avec votre assemblage, vous devrez obtenir un flux à partir du fichier et le passer au constructeur Cursor(Stream cursorStream). C'est ennuyeux mais vrai.

Je suis tombé sur un moyen agréable de le faire et qui résout mon problème :

    System.Windows.Resources.StreamResourceInfo info = 
        Application.GetResourceStream(new 
        Uri("/MainApp;component/Resources/HandDown.cur", UriKind.Relative));

    this.Cursor = new System.Windows.Input.Cursor(info.Stream); 

MainApp doit être remplacé par le nom de votre application. Resources doit être remplacé par le chemin d'accès relatif au dossier de vos fichiers *.cur dans votre projet.

0 votes

"MainApp" doit être remplacé par le nom de votre demande. "Ressources" doit être remplacé par le chemin de dossier relatif à vos fichiers *.cur à l'intérieur de votre projet.

9voto

Greg Points 333

Une méthode très simple consiste à créer le curseur dans Visual Studio sous la forme d'un fichier .cur, puis à l'ajouter aux ressources du projet.

Il suffit ensuite d'ajouter le code suivant lorsque vous souhaitez affecter le curseur :

myCanvas.Cursor = new Cursor(new System.IO.MemoryStream(myNamespace.Properties.Resources.Cursor1));

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X