Skip to main content
Email Password   helpLost your password?

Introduction

ExtCursor is a design-time component that can be dropped onto a .Net form to make use of animated (.ani) and multi-coloured (.cur) cursors on that form.
The libary also supplies an ancillary component, ExtCursorsLink, that provides an extended property on all controls on parent the form - which facilitates the setting of an extended cursor at design-time.  There is also an internal, singleton component that manages the sharing of cursor handles.

I'm not advocating the excessive use of animated and coloured cursors within applications - overuse can be just as detremental to UI consistency as too many colours or fonts etc.

Background

I was recently working on a project where we wanted to use a couple of animated cursors to indicate to the user that some specific background task was taking place - similar to AppStarting (or background busy).  We also needed to be able to load cursors from images and imagelists at runtime - the proposed application having a toolbox (similar to the VS IDE toolbox) where items in the toolbox could also be defined dynamically by the user.  When loading cursors from images or imagelists, the code needed to be able to automatically add the 'crosshair' to the cursor image (so the user did not have to create an image for the toolbox item both with and without the crosshair).

Seemed like an incidental part of the design at first glance without considering the limitations of the .Net Cursor class:-

I scouted around MSDN and CodeProject, even trawled through the .Net source to find a quick and easy solution to these limitations.  Most of these seemed to point to using the WinAPI LoadCursorFromFile() function, but I knew from previous experience that the LoadImage() function provided a slightly better alternative as it gave additional useful parameters - such as the desired height/width from cursor files containing multiple sizes.
Both functions were going to involve saving any internal cursor resources out to a temporary file, not something I was particularly keen on - but MS themselves actually do it in some circumstances within the .Net source.
I did explore utilising the HINSTANCE hinst parameter of the LoadImage() function to load the cursor data directly from an embedded resource, but when I started to explore the design ideas I decided against this because, as mentioned in the next section, I wanted to load the cursor data either from a .resx project resource or from a local resource stored within the form .resx.  Another hinderance to using the HINSTANCE would have been that .Net doesn't provide a native resource template for .ani animated cursor files (resource type ID 21) - so getting these into application resources was going to mean messing around editing .rc (resource script) files, compiling them into a .res (resource) file and finally getting that resource file into the compiled .exe - way, way too much like hard work!

If you've done a search on animated cursors and .Net to end up here then you're probably aware of much of the above!

Design

I decided that writing a component that could be easily used and re-used seemed the best, if far from economical, solution to the problems.  Therefore, the design requirements were:-

Using the components

Installing the components in Visual Studio

You should now see the two ExtCursors components in the Toolbox, e.g.

Toolbox example
[fig. 1] Toolbox example

Loading cursors into project resources

To load a cursor (.ani or .cur) into a project resource:-
(This only needs to be done if you want to load cursors from project resources - the ExtCursor component has other options for loading cursors at design-time)

Using the ExtCursor component

Open the form designer of the form you wish to use the cursor on, select the ExtCursor component from the Toolbox and drop it onto your form.  The properties for the newly created ExtCursor will look llike:-

ExtCursor properties
[fig. 2] ExtCursor properties

Setting the ExtCursor component properties

The usage of the ExtCursor component properties is as follows:-

Cursor - use this property to specify the local or project resource from which the cursor is to be loaded.  Local resources are stored in the .resx of the form.  Enter a filename into this property to specify a cursor file that is to be stored as a local resource.  Press the elipses button of this property to bring up the Select Resource dialog, as shown below:-

Select Resource example
[fig. 3] Select Resource dialog

Select the cursor from either a file (by checking the Local Resource radiobutton and pressing the Import... button under local resource) - the cursor being stored as a local resource.  Alternatively, check the Project resource file radiobutton and select the project resource file from the combo and then the cursor in that resource.
Note: The Import button for the project resource option does not work and is permanently disabled - as I have not discovered how to get this to work - any suggestions welcome!

CursorLocation - use this property to specify a file from which the cursor is to be loaded at runtime.
CursorLocationLocal - set this property to true if the file location specified by the CursorLocation property will be local to the executable at runtime.  In which case, the file path will be converted to a path relative to the folder containing the executable.

SharedHandle - set this property to true if you want this cursor to share (re-use) the handle of another ExtCursor loaded from the same resource.

SizeDefault & SizePreferred - these properties determine which actual cursor will be loaded from cursor files containing multiple size cursors.  These properties are translated into the flags and parameters used by the LoadImage() function.  Specifically, the SizePreferred property is used to set the cxDesired and cyDesired parameters, and the SizeDefault is used to determine if the LR_DEFAULTSIZE flag is set on the fuLoad parameter.

Size & HotSpot - these properties are informational only and are, therefore, read-only.
The hotspot can be set when loading cursors from images or imagelists using the constructors outlined below.

Using the ExtCursorsLinks component
(and setting cursors on form/controls at design-time)

The ExtCursorsLink component provides an extended property on all controls on the form (and the form itself) that allows an ExtCursor component to be selected as the cursor for that control.

Drop the ExtCursorsLink component onto the form and then all controls and the form itself will have an additional property of Cursor on extCursorsLink, as shown in the example below:-

ExtCursorsLink extended property example
[fig. 4] ExtCursorsLink extended property example

Select the ExtCursor from the dropdown list on this property to set the cursor required on the control.

Using the code

The ExtCursor class can also be instantiated in code (rather than as a drop-on component) using numerous overloaded constructors that allow the cursor to be loaded from file, stream, resource, image or imagelist.  These overloaded constructors are briefly outlined below:-

Loading from stream/byte data constructors

public ExtCursor(
	Stream pCursorStream
)

public ExtCursor(
	Stream pCursorStream,
	bool pDefaultSize,
	Size pPreferredSize
)

public ExtCursor(
	byte[] pCursorData
)

public ExtCursor(
	byte[] pCursorData,
	bool pDefaultSize,
	Size pPreferredSize
)

Loading from file constructors

public ExtCursor(
	string pFilename
)

public ExtCursor(
	string pFilename,
	bool pShareable
)

public ExtCursor(
	string pFilename,
	bool pDefaultSize,
	Size pPreferredSize
)

public ExtCursor(
	string pFilename,
	bool pDefaultSize,
	Size pPreferredSize,
	bool pShareable
)

Loading from resource constructors

public ExtCursor(
	Assembly pResourceAssembly,
	string pResXName,
	string pResourceName
)

public ExtCursor(
	Assembly pResourceAssembly,
	string pResXName,
	string pResourceName,
	bool pShareable
)

public ExtCursor(
	Assembly pResourceAssembly,
	string pResXName,
	string pResourceName,
	bool pDefaultSize,
	Size pPreferredSize
)

public ExtCursor(
	Assembly pResourceAssembly,
	string pResXName,
	string pResourceName,
	bool pDefaultSize,
	Size pPreferredSize,
	bool pShareable
)

Loading from image constructors

public ExtCursor(
	Bitmap pCursorImage,
	Point pHotSpot
)

public ExtCursor(
	Bitmap pCursorImage,
	Bitmap pCursorMaskImage,
	Point pHotSpot
)

public ExtCursor(
	Bitmap pCursorImage,
	Point pHotSpot,
	bool pWithCrossHairs
)

public ExtCursor(
	Bitmap pCursorImage,
	Color pTransparentColor,
	Point pHotSpot
)

public ExtCursor(
	Bitmap pCursorImage,
	Color pTransparentColor,
	Point pHotSpot,
	bool pWithCrossHairs
)

Loading from imagelist constructors

public ExtCursor(
	ImageList pImageList,
	int pImageIndex,
	Point pHotSpot
)

public ExtCursor(
	ImageList pImageList,
	int pImageIndex,
	Point pHotSpot,
	bool pWithCrossHairs
)

Points of Interest

Obtaining project resources at design time

One of the trickiest bits of coding was to populate the information in the Select Resource dialog (see Fig. 3 above) - in particular obtaining the list of resources within the 'host' project to populate the combobox with and the list of appropriate cursor (.cur and .ani files) within each project resource.  All of the code to do this in the file ExtCursorResourceEditorDialog.cs - and uses, as the starting point, the IServiceProvider and ITypeDescriptorContext objects that are passed to the UITypeEditor.EditValue override method (in ExtCursorEditor.cs).

Images, ImageLists and masks

When creating cursors from images (using the CreateIconIndirect() function) a mask image is required in the ICONINFO structure.  Writing a graphics routine to generate this mask is superfluous when the windows ImageList has just such a routine that can be borrowed.  Unfortunately the .Net ImageList class does not expose a method that makes use of the ImageList_Draw() function passing a parameter to denote that the mask is required (ILD_MASK flag set in the fStyle parameter).  The code to perform this can be found in ExtCursor.cs (in the private method CreateImageAndMask).

History