Question

Je veux utiliser une image ou une icône de curseur personnalisé en application WPF.Quelle est la meilleure façon de le faire?

Était-ce utile?

La solution

Vous avez deux options de base:

  1. Lorsque le curseur de la souris est au-dessus de votre contrôle, de masquer le curseur système par la mise en this.Cursor = Cursors.None; et dessiner votre propre curseur à l'aide quelle que soit la technique que vous aimez.Ensuite, mettre à jour la position et l'apparence de votre curseur en répondant à des événements de souris.En voici deux exemples:

  2. Créer un nouveau Curseur de l'objet par le chargement d'une image à partir d'un .cur ou .ani fichier.Vous pouvez créer et modifier ces types de fichiers dans Visual Studio.Il y a aussi quelques gratuit utilitaires flottant autour pour traiter avec eux.Ce sont des images (ou des images animées) qui spécifient un "hot spot", indiquant ce point dans l'image, le curseur est positionné à.

Si vous choisissez de charger à partir d'un fichier, notez que vous avez besoin d'un absolu chemin du système de fichiers à utiliser le Cursor(string fileName) constructeur.Mollement conclu, un chemin d'accès relatif ou Pack URI ne fonctionnera pas. Si vous avez besoin de charger le curseur à partir d'un chemin d'accès relatif ou à partir d'une ressource emballé avec votre assemblée, vous aurez besoin d'obtenir un flux de données à partir du fichier et de le transmettre à la Cursor(Stream cursorStream) constructeur.Ennuyeux, mais vrai.

D'autre part, la spécification d'un curseur comme un chemin d'accès relatif lors du chargement à l'aide d'un attribut XAML n' de travail, un fait que vous pouvez utiliser pour obtenir votre curseur chargé sur un contrôle caché et ensuite copier la référence pour l'utiliser sur un autre contrôle.Je n'ai pas essayé, mais ça devrait fonctionner.

Autres conseils

Comme Peter mentionné ci-dessus, si vous en avez déjà un .cur de fichier, vous pouvez l'utiliser comme une ressource incorporée par la création d'un élément "factice" dans la section "ressources", puis de référencement sur le mannequin du curseur lorsque vous en avez besoin.

Par exemple, disons que vous voulez afficher la non-curseurs 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 incorporés curseur 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

Il y a un moyen plus simple que de gérer l'affichage du curseur de vous-même ou à l'aide de Visual Studio pour créer beaucoup de curseurs personnalisés.

Si vous avez un FrameworkElement vous pouvez construire un Curseur 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 votre FrameworkElement 's la taille doit être une norme sur la taille du curseur (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));

Bien évidemment votre FrameworkElement pourrait être un <Image> contrôler si vous avez une image existante, ou vous pouvez dessiner ce que vous voulez à l'aide de WPF est intégré dans les outils de dessin.

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

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

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

Utiliser le curseur juste référence à la ressource:

<StackPanel Cursor="{StaticResource OpenHandCursor}" />

Un moyen très simple est de créer le curseur dans Visual Studio comme .cur fichier, puis l'ajouter à la projets de Ressources.

Puis il suffit d'ajouter le code suivant lorsque vous souhaitez affecter le curseur:

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

Dans le cas où quelqu'un est à la recherche d'un UIElement 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);
    }

Je sais que ce sujet est un peu vieux maintenant, mais hier, j'ai voulu charger un curseur personnalisé fichier de ressources du projet, et a couru dans des problèmes similaires.J'ai cherché sur internet pour trouver une solution et de ne pas trouver ce dont j'avais besoin:pour définir le this.Cursor pour un curseur personnalisé stockées dans mon dossier de ressources dans mon projet à exécution.J'ai essayé de Ben xaml solution, mais n'ai pas trouvé cela assez élégant.PeterAllen a déclaré:

Mollement conclu, un chemin d'accès relatif ou Pack URI ne fonctionnera pas.Si vous avez besoin de charger le curseur à partir d'un chemin d'accès relatif ou à partir d'une ressource emballé avec votre assemblée, vous aurez besoin d'obtenir un flux de données à partir du fichier et de le transmettre à l'emplacement du Curseur(Flux cursorStream) constructeur.Ennuyeux, mais vrai.

Je suis tombé sur une belle façon de le 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); 

La solution quelque peu semblable à Ray, mais au lieu de lenteur et la lourdeur des pixels de la copie, il utilise certains Windows internals:

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 dans le 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'êtes pas besoin de spécifier votre propre point d'accès, vous pouvez même couper cette courte (vous n'avez pas besoin de la structure ou de la 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));
}

Vous pouvez essayer ce

<Window Cursor=""C:\WINDOWS\Cursors\dinosaur.ani"" />

Consultez également Scott, Hanselman de BabySmash (www.codeplex.com/babysmash).Il a utilisé un plus "brute force" la méthode de masquage de la windows curseur et montrant son nouveau curseur sur une toile, puis en déplaçant le curseur à l'étaient les "vrais" curseur aurait été

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

Assurez-vous que toutes les ressources GDI (par exemple bmp.GetHIcon) est disposé.Sinon vous vous retrouvez avec une fuite de mémoire.Le code suivant (méthode d'extension pour l'icône) fonctionne parfaitement pour WPF.Il crée le curseur en forme de flèche avec une petite icône sur le coin inférieur droit.

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

Si vous utilisez visual studio, vous pouvez

  1. Un nouveau fichier de curseur
  2. Copier/Coller l'image
  3. L'enregistrer .cur de fichier.

vous pouvez le faire par un Code comme

this.Cursor = new Cursor(@"<your address of icon>");

Elle peut avoir changé avec Visual Studio en 2017, mais j'ai été capable de faire référence à un .cur fichier en tant que ressource incorporée:

<Setter
    Property="Cursor"
    Value="/assembly-name;component/location-name/curser-name.cur" />
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top