How to Use Custom Cursors in C#

By FoxLearn 1/16/2025 7:33:29 AM   75
While .NET offers default cursors, creating a custom cursor can be an exciting way to stand out or cater to specific needs.

Changing the cursor on a WinForms control is simple if you are using one of the standard cursors.

You can easily set the Cursor property to a value from the Cursors object.

However, using a custom cursor is more complex. One way to do this is by loading a custom cursor file (with a .cur extension).

You can create a new Cursor object by passing the file path of your custom cursor to the Cursor constructor.

Cursor myCursor = new Cursor("myCursor.cur");
myControl.Cursor = myCursor;

Using a custom cursor in WinForms becomes more challenging if you don't have a pre-made .cur file and want to create the cursor programmatically.

Since .NET doesn't provide all the necessary functionality, you'll need to use interop methods to achieve this.

The first step is to define the C# equivalent of the ICONINFO structure, which will store the necessary information about the cursor you're creating.

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

The key members of the ICONINFO structure are fIcon, xHotspot, and yHotspot. The fIcon member indicates whether the icon is a cursor (set to false for a cursor). The xHotspot and yHotspot define the "click point" of the cursor, which is the critical pixel for interactions, even though the cursor may be larger than 1x1 pixel.

In this example, we create a 140x25 pixel bitmap and draw some text on it.

Bitmap bitmap = new Bitmap(140, 25);
Graphics g = Graphics.FromImage(bitmap);
using (Font f = new Font(FontFamily.GenericSansSerif, 10))
{
    g.DrawString("{ } Switch On The Code", f, Brushes.Green, 0, 0);
}

This code initializes a Graphics object from the bitmap, allowing us to draw the string { } Switch On The Code in green text on the image.

Creating the Custom Cursor

Once the bitmap is ready, the next step is to create a custom cursor using this bitmap. A cursor in .NET can be created using the CreateCursor method, which requires a Bitmap object and the hot spot coordinates (the point on the cursor that will interact with the interface, typically the tip of the arrow).

this.Cursor = CreateCursor(bitmap, 3, 3);

Here, the custom cursor is set with the bitmap, and the hot spot is located at coordinates (3, 3).

To create a cursor from a bitmap, we must interact with the Windows API to handle the icon creation and hot spot settings. The following P/Invoke methods allow us to interface with the necessary Windows functions:

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

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

The core of creating a custom cursor is the CreateCursor method, which is responsible for constructing a Cursor object using the bitmap and the hot spot parameters.

public static Cursor CreateCursor(Bitmap bmp, int xHotSpot, int yHotSpot)
{
    IconInfo tmp = new IconInfo();
    GetIconInfo(bmp.GetHicon(), ref tmp);
    tmp.xHotspot = xHotSpot;
    tmp.yHotspot = yHotSpot;
    tmp.fIcon = false; // Indicates that it's a cursor, not an icon
    return new Cursor(CreateIconIndirect(ref tmp));
}

This function converts a bitmap into a cursor by first creating an IconInfo structure and populating it with icon data using the native GetIconInfo method. This method takes the bitmap's icon pointer (obtained via GetHicon()) and a reference to the IconInfo structure.

The hotspot coordinates are set, and fIcon is marked as false to indicate it's a cursor. The CreateIconIndirect function is then called to generate a new cursor icon, and a new Cursor is created from the returned pointer. The bitmap is not affected, as CreateIconIndirect creates a separate copy for the cursor.

After the cursor is set, it’s good practice to dispose of any resources you no longer need.

bitmap.Dispose();

By manipulating the bitmap, you can create complex and interactive cursors, enhancing the user interface.