Curseur personnalisé dans WPF?

je veux utiliser une image ou une icône comme curseur personnalisé dans l'application WPF. Quelle est la meilleure façon de le faire?

42
demandé sur Dave Clemmer 2008-09-06 00:13:48

15 réponses

vous avez deux options de base:

  1. lorsque le curseur de la souris est au-dessus de votre contrôle, masquez le curseur du système en paramétrant this.Cursor = Cursors.None; et dessinez votre propre curseur en utilisant n'importe quelle technique que vous aimez. Ensuite, mettez à jour la position et l'apparence de votre curseur en répondant aux événements de la souris. Voici deux exemples:

  2. créer un nouvel objet curseur en chargeant une image à partir d'un .cur ou .ani fichier. Vous pouvez créer et éditer ces types de fichiers dans Visual Studio. Il y a aussi quelques Utilités libres qui flottent autour pour les traiter. Essentiellement ce sont des images (ou des images animées) qui spécifient un "point chaud" indiquant quel point dans l'image le curseur est positionné à.

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 le constructeur Cursor(string fileName) . Lamely, un chemin relatif ou URI Pack ne fonctionnera pas. si vous avez besoin de 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) . Ennuyeux mais vrai.

d'autre part, spécifier un curseur comme chemin relatif lors du chargement en utilisant un attribut XAML fait travail, un fait que vous pourriez utiliser pour obtenir votre curseur chargé sur un contrôle caché et ensuite copier la référence à utiliser sur un autre contrôle. Je n'ai pas essayé, mais ça devrait fonctionner.

30
répondu PeterAllenWebb 2008-11-05 20:10:39

comme Peter mentionné ci-dessus, si vous avez déjà un .le fichier cur, vous pouvez l'utiliser comme une ressource intégrée en créant un élément fictif dans la section ressource, puis en référençant le curseur du mannequin lorsque vous en avez besoin.

par exemple, dites que vous vouliez Afficher des curseurs non standards en fonction de l'outil sélectionné.

ajouter à 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 intégré 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;

- Ben

29
répondu Ben McIntosh 2009-01-04 05:40:27

il y a un moyen plus simple que de gérer vous-même l'affichage du curseur ou d'utiliser Visual Studio pour construire beaucoup de curseurs personnalisés.

si vous avez un Frameworkkelement 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 Framework 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 Cadrekelement pourrait être un contrôle <Image> si vous avez une image existante, ou vous pouvez dessiner tout ce que vous voulez en utilisant les outils de dessin intégrés de WPF.

Note que les détails sur les .le format de fichier cur peut être trouvé à ICO (format de fichier) .

14
répondu Ray Burns 2013-07-08 10:59:26

Un moyen très simple est de créer le curseur dans Visual Studio comme .puis Ajouter cela aux ressources du projet.

alors il suffit d'ajouter le code suivant lorsque vous voulez assigner le curseur:

myCanvas.Cursor = new Cursor(new System.IO.MemoryStream(myNamespace.Properties.Resources.Cursor1));
9
répondu Greg 2012-01-03 16:46:00

pour utiliser un curseur personnalisé dans XAML j'ai modifié le code Ben McIntosh fourni légèrement:

<Window.Resources>    
 <Cursor x:Key="OpenHandCursor">Resources/openhand.cur</Cursor>
</Window.Resources>

pour utiliser le curseur référence à la ressource:

<StackPanel Cursor="{StaticResource OpenHandCursor}" />
9
répondu kkCosmo 2013-05-15 08:49:33

je sais que ce sujet a quelques années maintenant, mais hier je voulais charger un fichier de curseur personnalisé à partir des ressources du projet et j'ai rencontré des problèmes similaires. J'ai cherché sur internet une solution et je n'ai pas trouvé ce dont j'avais besoin: mettre le this.Cursor sur un curseur personnalisé stocké dans mon dossier Ressources dans mon projet à l'exécution. J'ai essayé la solution XAML de Ben, mais elle n'était pas assez élégante. PeterAllen a déclaré:

mollement conclu, un chemin d'accès relatif ou Pack URI ne marchera pas. Si vous avez besoin de 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 dans le curseur(Stream cursorStream) constructor. Ennuyeux mais vrai.

je suis tombé sur une bonne façon de faire et 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); 
8
répondu paulmcguinness 2011-12-01 19:54:29

dans le cas où quelqu'un est à la recherche D'un élément lui-même comme un curseur, j'ai combiné les solutions de Ray et Arcturus :

    public Cursor ConvertToCursor(UIElement control, Point hotSpot)
    {
        // convert FrameworkElement to PNG stream
        var pngStream = new MemoryStream();
        control.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
        Rect rect = new Rect(0, 0, control.DesiredSize.Width, control.DesiredSize.Height);
        RenderTargetBitmap rtb = new RenderTargetBitmap((int)control.DesiredSize.Width, (int)control.DesiredSize.Height, 96, 96, PixelFormats.Pbgra32);

        control.Arrange(rect);
        rtb.Render(control);

        PngBitmapEncoder png = new PngBitmapEncoder();
        png.Frames.Add(BitmapFrame.Create(rtb));
        png.Save(pngStream);

        // write cursor header info
        var cursorStream = new MemoryStream();
        cursorStream.Write(new byte[2] { 0x00, 0x00 }, 0, 2);                               // ICONDIR: Reserved. Must always be 0.
        cursorStream.Write(new byte[2] { 0x02, 0x00 }, 0, 2);                               // ICONDIR: Specifies image type: 1 for icon (.ICO) image, 2 for cursor (.CUR) image. Other values are invalid
        cursorStream.Write(new byte[2] { 0x01, 0x00 }, 0, 2);                               // ICONDIR: Specifies number of images in the file.
        cursorStream.Write(new byte[1] { (byte)control.DesiredSize.Width }, 0, 1);          // ICONDIRENTRY: Specifies image width in pixels. Can be any number between 0 and 255. Value 0 means image width is 256 pixels.
        cursorStream.Write(new byte[1] { (byte)control.DesiredSize.Height }, 0, 1);         // ICONDIRENTRY: Specifies image height in pixels. Can be any number between 0 and 255. Value 0 means image height is 256 pixels.
        cursorStream.Write(new byte[1] { 0x00 }, 0, 1);                                     // ICONDIRENTRY: Specifies number of colors in the color palette. Should be 0 if the image does not use a color palette.
        cursorStream.Write(new byte[1] { 0x00 }, 0, 1);                                     // ICONDIRENTRY: Reserved. Should be 0.
        cursorStream.Write(new byte[2] { (byte)hotSpot.X, 0x00 }, 0, 2);                    // ICONDIRENTRY: Specifies the horizontal coordinates of the hotspot in number of pixels from the left.
        cursorStream.Write(new byte[2] { (byte)hotSpot.Y, 0x00 }, 0, 2);                    // ICONDIRENTRY: Specifies the vertical coordinates of the hotspot in number of pixels from the top.
        cursorStream.Write(new byte[4] {                                                    // ICONDIRENTRY: Specifies the size of the image's data in bytes
                                          (byte)((pngStream.Length & 0x000000FF)),
                                          (byte)((pngStream.Length & 0x0000FF00) >> 0x08),
                                          (byte)((pngStream.Length & 0x00FF0000) >> 0x10),
                                          (byte)((pngStream.Length & 0xFF000000) >> 0x18)
                                       }, 0, 4);
        cursorStream.Write(new byte[4] {                                                    // ICONDIRENTRY: Specifies the offset of BMP or PNG data from the beginning of the ICO/CUR file
                                          (byte)0x16,
                                          (byte)0x00,
                                          (byte)0x00,
                                          (byte)0x00,
                                       }, 0, 4);

        // copy PNG stream to cursor stream
        pngStream.Seek(0, SeekOrigin.Begin);
        pngStream.CopyTo(cursorStream);

        // return cursor stream
        cursorStream.Seek(0, SeekOrigin.Begin);
        return new Cursor(cursorStream);
    }
8
répondu gjacobs 2017-05-23 12:02:48

une solution de plus un peu similaire à Ray's mais au lieu de la copie de pixel lente et encombrante, cela utilise quelques fenêtres internes:

private struct IconInfo {
  public bool fIcon;
  public int xHotspot;
  public int yHotspot;
  public IntPtr hbmMask;
  public IntPtr hbmColor;
}

[DllImport("user32.dll")]
private static extern IntPtr CreateIconIndirect(ref IconInfo icon);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);

public Cursor ConvertToCursor(FrameworkElement cursor, Point HotSpot) {
  cursor.Arrange(new Rect(new Size(cursor.Width, cursor.Height)));
  var bitmap = new RenderTargetBitmap((int)cursor.Width, (int)cursor.Height, 96, 96, PixelFormats.Pbgra32);
  bitmap.Render(cursor);

  var info = new IconInfo();
  GetIconInfo(bitmap.ToBitmap().GetHicon(), ref info);
  info.fIcon = false;
  info.xHotspot = (byte)(HotSpot.X * cursor.Width);
  info.yHotspot = (byte)(HotSpot.Y * cursor.Height);

  return CursorInteropHelper.Create(new SafeFileHandle(CreateIconIndirect(ref info), true));
}

il y a une méthode d'extension au milieu que je préfère avoir dans une classe d'extension pour de tels cas:

using DW = System.Drawing;

public static DW.Bitmap ToBitmap(this BitmapSource bitmapSource) {
  var bitmap = new DW.Bitmap(bitmapSource.PixelWidth, bitmapSource.PixelHeight, DW.Imaging.PixelFormat.Format32bppPArgb);
  var data = bitmap.LockBits(new DW.Rectangle(DW.Point.Empty, bitmap.Size), DW.Imaging.ImageLockMode.WriteOnly, DW.Imaging.PixelFormat.Format32bppPArgb);
  bitmapSource.CopyPixels(Int32Rect.Empty, data.Scan0, data.Height * data.Stride, data.Stride);
  bitmap.UnlockBits(data);
  return bitmap;
}

Avec tout cela, c'est assez simple et direct.

et, si vous n'avez pas besoin de spécifier votre propre hotspot, vous pouvez même couper ceci plus court (vous n'avez pas besoin de la structure ou du p/invoque, soit):

public Cursor ConvertToCursor(FrameworkElement cursor, Point HotSpot) {
  cursor.Arrange(new Rect(new Size(cursor.Width, cursor.Height)));
  var bitmap = new RenderTargetBitmap((int)cursor.Width, (int)cursor.Height, 96, 96, PixelFormats.Pbgra32);
  bitmap.Render(cursor);
  var icon = System.Drawing.Icon.FromHandle(bitmap.ToBitmap().GetHicon());
  return CursorInteropHelper.Create(new SafeFileHandle(icon.Handle, true));
}
4
répondu Gábor 2017-07-23 08:59:55

, Vous pouvez essayer ce

<Window Cursor=""C:\WINDOWS\Cursors\dinosaur.ani"" />
2
répondu palehorse 2009-09-09 15:16:43

également vérifier la baby-sitter de Scott Hanselman (www.codeplex.com/babysmash). Il a utilisé une méthode plus "brute force" de cacher le curseur de fenêtres et de montrer son nouveau curseur sur une toile, puis le déplacement du curseur pour être le" vrai "curseur aurait été

lire la suite ici: http://www.hanselman.com/blog/DeveloperDesigner.aspx

1
répondu rudigrobler 2008-09-08 08:57:03

s'assurer que toute ressource GDI (par exemple bmp.GetHIcon) est éliminé. Sinon vous vous retrouvez avec une fuite de mémoire. Le code suivant (méthode d'extension pour icon) fonctionne parfaitement pour WPF. Il crée le curseur flèche avec une petite icône en bas à droite.

Remarque: Ce code utilise une icône pour créer le curseur. Il n'utilise pas d'INTERFACE utilisateur actuelle de contrôle.

Matthias

    public static Cursor CreateCursor(this Icon icon, bool includeCrossHair, System.Drawing.Color crossHairColor)
    {
        if (icon == null)
            return Cursors.Arrow;

        // create an empty image
        int width = icon.Width;
        int height = icon.Height;

        using (var cursor = new Bitmap(width * 2, height * 2))
        {
            // create a graphics context, so that we can draw our own cursor
            using (var gr = System.Drawing.Graphics.FromImage(cursor))
            {
                // a cursor is usually 32x32 pixel so we need our icon in the lower right part of it
                gr.DrawIcon(icon, new Rectangle(width, height, width, height));

                if (includeCrossHair)
                {
                    using (var pen = new System.Drawing.Pen(crossHairColor))
                    {
                        // draw the cross-hair
                        gr.DrawLine(pen, width - 3, height, width + 3, height);
                        gr.DrawLine(pen, width, height - 3, width, height + 3);
                    }
                }
            }

            try
            {
                using (var stream = new MemoryStream())
                {
                    // Save to .ico format
                    var ptr = cursor.GetHicon();
                    var tempIcon = Icon.FromHandle(ptr);
                    tempIcon.Save(stream);

                    int x = cursor.Width/2;
                    int y = cursor.Height/2;

                    #region Convert saved stream into .cur format

                    // set as .cur file format
                    stream.Seek(2, SeekOrigin.Begin);
                    stream.WriteByte(2);

                    // write the hotspot information
                    stream.Seek(10, SeekOrigin.Begin);
                    stream.WriteByte((byte)(width));
                    stream.Seek(12, SeekOrigin.Begin);
                    stream.WriteByte((byte)(height));

                    // reset to initial position
                    stream.Seek(0, SeekOrigin.Begin);

                    #endregion


                    DestroyIcon(tempIcon.Handle);  // destroy GDI resource

                    return new Cursor(stream);
                }
            }
            catch (Exception)
            {
                return Cursors.Arrow;
            }
        }
    }

    /// <summary>
    /// Destroys the icon.
    /// </summary>
    /// <param name="handle">The handle.</param>
    /// <returns></returns>
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public extern static Boolean DestroyIcon(IntPtr handle);
1
répondu Matthias 2012-11-15 11:48:44

si vous utilisez visual studio, vous pouvez ""

  1. nouveau fichier de curseur
  2. copier / coller l'image
  3. .cur de fichier.
1
répondu Alex Cube 2013-08-16 07:42:50

cela a peut-être changé avec Visual Studio 2017, mais j'ai pu faire référence à A.le fichier cur en tant que ressource intégrée:

<Setter
    Property="Cursor"
    Value="/assembly-name;component/location-name/curser-name.cur" />
1
répondu CIMframe 2017-05-21 19:29:42

Voici une fonctionnalité riche, utilitaire gratuit qui vous permet de créer un fichier cur à partir de n'importe quelle image: http://www.rw-designer.com/cursor-maker

ça s'appelle L'éditeur de curseur de RealWorld.

Et voici un lien sur la façon d'intégrer un curseur dans un projet:

http://wpf.2000things.com/tag/cursor/

0
répondu Eternal21 2014-10-16 15:13:59

vous pouvez le faire par Code comme

this.Cursor = new Cursor(@"<your address of icon>");
0
répondu Ahmad 2017-04-25 07:43:36