Custom cursor in WPF?

Viewed 64567

I want to use an image or icon as a custom cursor in WPF app. How can I do that?

15 Answers

You have two basic options:

  1. When the mouse cursor is over your control, hide the system cursor by setting this.Cursor = Cursors.None; and draw your own cursor using whatever technique you like. Then, update the position and appearance of your cursor by responding to mouse events. Here are two examples:
  1. Create a new Cursor object by loading an image from a .cur or .ani file. You can create and edit these kinds of files in Visual Studio. There are also some free utilites floating around for dealing with them. Basically they're images (or animated images) which specify a "hot spot" indicating what point in the image the cursor is positioned at.

If you choose to load from a file, note that you need an absolute file-system path to use the Cursor(string fileName) constructor. Lamely, a relative path or Pack URI will not work. If you need to load the cursor from a relative path or from a resource packed with your assembly, you will need to get a stream from the file and pass it in to the Cursor(Stream cursorStream) constructor. Annoying but true.

On the other hand, specifying a cursor as a relative path when loading it using a XAML attribute does work, a fact you could use to get your cursor loaded onto a hidden control and then copy the reference to use on another control. I haven't tried it, but it should work.

Like Peter mentioned, if you already have a .cur file, you can use it as an embedded resource by creating a dummy element in the resource section, and then referencing the dummy's cursor when you need it.

For example, say you wanted to display non-standard cursors depending on the selected tool.

Add to resources:

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

Example of embedded cursor referenced in 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;

You could try this

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

Also check out Scott Hanselman's BabySmash (www.codeplex.com/babysmash). He used a more "brute force" method of hiding the windows cursor and showing his new cursor on a canvas and then moving the cursor to were the "real" cursor would have been

Read more here: http://www.hanselman.com/blog/DeveloperDesigner.aspx

This will convert any image stored in your project to a cursor using an attached property. The image must be compiled as a resource!

Example

<Button MyLibrary:FrameworkElementExtensions.Cursor=""{MyLibrary:Uri MyAssembly, MyImageFolder/MyImage.png}""/>

FrameworkElementExtensions

using System;
using System.Windows;
using System.Windows.Media;

public static class FrameworkElementExtensions
{
    #region Cursor

    public static readonly DependencyProperty CursorProperty = DependencyProperty.RegisterAttached("Cursor", typeof(Uri), typeof(FrameworkElementExtensions), new UIPropertyMetadata(default(Uri), OnCursorChanged));
    public static Uri GetCursor(FrameworkElement i) => (Uri)i.GetValue(CursorProperty);
    public static void SetCursor(FrameworkElement i, Uri input) => i.SetValue(CursorProperty, input);
    static void OnCursorChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        if (sender is FrameworkElement frameworkElement)
        {
            if (GetCursor(frameworkElement) != null)
                frameworkElement.Cursor = new ImageSourceConverter().ConvertFromString(((Uri)e.NewValue).OriginalString).As<ImageSource>().Bitmap().Cursor(0, 0).Convert();
        }
    }

    #endregion
}

ImageSourceExtensions

using System.Drawing;
using System.Windows.Media;
using System.Windows.Media.Imaging;

public static class ImageSourceExtensions
{
    public static Bitmap Bitmap(this ImageSource input) => input.As<BitmapSource>().Bitmap();
}

BitmapSourceExtensions

using System.IO;
using System.Windows.Media.Imaging;

public static class BitmapSourceExtensions
{
    public static System.Drawing.Bitmap Bitmap(this BitmapSource input)
    {
        if (input == null)
            return null;

        System.Drawing.Bitmap result;
        using (var outStream = new MemoryStream())
        {
            var encoder = new PngBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(input));
            encoder.Save(outStream);
            result = new System.Drawing.Bitmap(outStream);
        }
        return result;
    }
}

BitmapExtensions

using System;
using System.Drawing;
using System.Runtime.InteropServices;

public static class BitmapExtensions
{

    [StructLayout(LayoutKind.Sequential)]
    public struct ICONINFO
    {
        /// <summary>
        /// Specifies whether this structure defines an icon or a cursor. A value of TRUE specifies an icon; FALSE specifies a cursor. 
        /// </summary>
        public bool fIcon;

        /// <summary>
        /// Specifies the x-coordinate of a cursor's hot spot. If this structure defines an icon, the hot spot is always in the center of the icon, and this member is ignored.
        /// </summary>
        public Int32 xHotspot;

        /// <summary>
        /// Specifies the y-coordinate of the cursor's hot spot. If this structure defines an icon, the hot spot is always in the center of the icon, and this member is ignored. 
        /// </summary>
        public Int32 yHotspot;

        /// <summary>
        /// (HBITMAP) Specifies the icon bitmask bitmap. If this structure defines a black and white icon, this bitmask is formatted so that the upper half is the icon AND bitmask and the lower half is the icon XOR bitmask. Under this condition, the height should be an even multiple of two. If this structure defines a color icon, this mask only defines the AND bitmask of the icon. 
        /// </summary>
        public IntPtr hbmMask;

        /// <summary>
        /// (HBITMAP) Handle to the icon color bitmap. This member can be optional if this structure defines a black and white icon. The AND bitmask of hbmMask is applied with the SRCAND flag to the destination; subsequently, the color bitmap is applied (using XOR) to the destination by using the SRCINVERT flag. 
        /// </summary>
        public IntPtr hbmColor;
    }

    [DllImport("user32.dll")]
    static extern IntPtr CreateIconIndirect([In] ref ICONINFO piconinfo);

    [DllImport("user32.dll")]
    static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo);

    [DllImport("user32.dll", SetLastError = true)]
    public static extern bool DestroyIcon(IntPtr hIcon);

    public static System.Windows.Forms.Cursor Cursor(this Bitmap input, int hotX, int hotY)
    {
        ICONINFO Info = new ICONINFO();
        IntPtr Handle = input.GetHicon();
        GetIconInfo(Handle, out Info);

        Info.xHotspot = hotX;
        Info.yHotspot = hotY;
        Info.fIcon = false;

        IntPtr h = CreateIconIndirect(ref Info);
        return new System.Windows.Forms.Cursor(h);
    }
}

CursorExtensions

using Microsoft.Win32.SafeHandles;

public static class CursorExtensions
{
    public static System.Windows.Input.Cursor Convert(this System.Windows.Forms.Cursor Cursor)
    {
        SafeFileHandle h = new SafeFileHandle(Cursor.Handle, false);
        return System.Windows.Interop.CursorInteropHelper.Create(h);
    }
}

As

public static Type As<Type>(this object input) => input is Type ? (Type)input : default;

Uri

using System;
using System.Windows.Markup;

public class Uri : MarkupExtension
{
    public string Assembly { get; set; } = null;

    public string RelativePath { get; set; }

    public Uri(string relativePath) : base()
    {
        RelativePath = relativePath;
    }

    public Uri(string assembly, string relativePath) : this(relativePath)
    {
        Assembly = assembly;
    }

    static Uri Get(string assemblyName, string relativePath) => new Uri($"pack://application:,,,/{assemblyName};component/{relativePath}", UriKind.Absolute);

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        if (Assembly == null)
            return new System.Uri(RelativePath, UriKind.Relative);

        return Get(Assembly, RelativePath);
    }
}
Related