Click here to Skip to main content
15,885,278 members
Articles / Programming Languages / C#

Extended Cursors for .Net

Rate me:
Please Sign up or sign in to vote.
4.83/5 (25 votes)
2 Feb 2009CPOL13 min read 51.4K   2.8K   69   10
A design-time component to make use of animated/multi-coloured cursors

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:-

  • the Cursor class cannot cope with animated/multi-coloured cursors - unless the Cursor(IntPtr handle) constructor is used (in which case, destroying the handle becomes a responsibility);
  • the Cursor class is sealed - so inheriting and overidding any limitations is out of the question;
  • the Cursor class is just a class rather than a component - so loading the cursor 'data' (stream, file, resource etc.) needs to be done at runtime using the Cusor constructors;
  • the Cursor(Type type, string resource) constructor doesn't, that I could see, support loading of the cursor data from resources compiled into any of the projects .resx files (.cur and .ani cursors can be added to project .resx's as binary files);
  • at design-time, the Control.Cursor property of controls can only be selected from the standard list of cursors supplied by the Cursors 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:-

  • a control libary that would contain all of the code and components required;
  • a component that would encapsulate the Cursor
    • the component would named 'ExtCursor' (extended cursor);
    • the cursor source would be design-time specified from:-
      • a project resource (.resx);
      • a local (form) resource;
      • a filename (which may, at runtime, be local to the executable);
    • the component would manage the cursor handle created - destroying the handle when no longer used/required;
    • the underlying code for the component would use the LoadImage() function - and expose, as settable properties, the desired width/height and default sizing parameters;
    • the component would serialize itself into the form.resx and form.designer.cs;
    • the component would have property editors consistent with similar Visual Studio control/component property editors;
    • the cursor class would have constructors that facilitated loading of cursors from images and imagelists - with the option to add a crosshair to the cursor image;
  • at design-time, the ability to set a control or form's cursor to one of the animated/multi-coloured cursor components:-
    • an ancillary 'ExtCursorsLink' component would be required;
    • this ancillary component would need to implement the IExtenderProvider interface - and provide the GetXxx() and SetXxx() methods for the extended property;
  • that cursor handles created could be shared, if required, across forms within the same application:-
    • a non-visible singleton component, named 'ExtCursorsSharedRespository', would hold a dictionary of cursor handles shared and which ExtCursor components made use of those handles.

Using the components

Installing the components in Visual Studio

  • Open any windows form (so that the Toolbox is showing the control/component tabs)
  • Right-click anywhere in Toolbox and select the Add Tab context menu option
  • Enter the tab name as ExtCursors
  • Right-click on the newly created ExtCursors tab and select the Choose Items... context menu option
  • The Choose Toolbox Items dialog will appear
  • Select the .Net Framework Components tab and press the Browse... button
  • In the Open dialog select the ExtCursors.dll (from where you unzipped it)
  • Click OK in the Choose Toolbox Items dialog

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)

  • Open (or create) the project resource
  • Drop-down the resource type selector (at the top-left of the resource designer) and select the Files option
  • Press the Add Resource and select the .ani or .cur file

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

C#
public ExtCursor(
	Stream pCursorStream
)

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

public ExtCursor(
	byte[] pCursorData
)

public ExtCursor(
	byte[] pCursorData,
	bool pDefaultSize,
	Size pPreferredSize
)
  • The pCursorStream or pCursorData parameter specifies the stream/byte data contianing the cursor.
  • The pDefaultSize and pPreferredSize parameters determine which actual cursor will be loaded from cursor streams/data containing multiple size cursors.  These parameters are translated into the flags and parameters used by the LoadImage() function.  Specifically, the pPreferredSize parameter is used to set the cxDesired and cyDesired parameters, and the pDefaultSize is used to determine if the LR_DEFAULTSIZE flag is set on the fuLoad parameter.

Loading from file constructors

C#
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
)
  • The pFilename parameter specifies the file from which the cursor is to be loaded (which should be a correctly encoded .cur or .ani file)
  • The pDefaultSize and pPreferredSize parameters determine which actual cursor will be loaded from cursor streams/data containing multiple size cursors.  These parameters are translated into the flags and parameters used by the LoadImage() function.  Specifically, the pPreferredSize parameter is used to set the cxDesired and cyDesired parameters, and the pDefaultSize is used to determine if the LR_DEFAULTSIZE flag is set on the fuLoad parameter.
  • The pShareable parameter determines whether the cursor handle should be shared.  When this parameter is specified as true any ExtCursor objects that are also shared and load from the same file will use the same cursor handle.  If this paramater is unspecified then true is the default.

Loading from resource constructors

C#
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
)
  • The pResourceAssembly parameter is the assembly containing the project resource.
  • The pResXName parameter is the name of the project resource.  For example, if the project is named 'Demo' and the Properties folder of the project contains Resources.resx then this parameter would be specified by the string "Demo.Properties.Resources".
  • The pDefaultSize and pPreferredSize parameters determine which actual cursor will be loaded from cursor streams/data containing multiple size cursors.  These parameters are translated into the flags and parameters used by the LoadImage() function.  Specifically, the pPreferredSize parameter is used to set the cxDesired and cyDesired parameters, and the pDefaultSize is used to determine if the LR_DEFAULTSIZE flag is set on the fuLoad parameter.
  • The pShareable parameter determines whether the cursor handle should be shared.  When this parameter is specified as true any ExtCursor objects that are also shared and load from the same file will use the same cursor handle.  If this paramater is unspecified then true is the default.

Loading from image constructors

C#
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
)
  • The pCursorImage parameter specifies the bitmap from which the cursor is to be created.
  • The pHotSpot parameter specifies the point in the image to be used as the cursor hot spot.  If the pHotSpot parameter is not specified then a default of 0,0 is used (unless the pWithCrossHairs parameter is specified as true - in which case the hot spot defaults to 5,5).
  • The pTransparentColor is the colour in the image to be treated as the transparent mask colour.  If this parameter is unspecified (and the pCursorMaskImage parameter is also not passed) then the transparent colour is defaulted to the colour of the pixel at the bottom-left of the image.
  • If the pWithCrossHairs parameter is specified with a value of true then cross-hairs are added to the created cursor (and the cursor size is increased accordingly).
  • The pCursorMaskImage parameter specifies a bitmap to be used as the transparent mask for the cursor.  (For more information on using this parameter consult the documentation on the ICONINFO structure). 

Loading from imagelist constructors

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

public ExtCursor(
	ImageList pImageList,
	int pImageIndex,
	Point pHotSpot,
	bool pWithCrossHairs
)
  • The pImageList and pImageIndex parameters specify the imagelist and index of the image within that list from which the cursor is to be created.
  • The pHotSpot parameter specifies the point in the image to be used as the cursor hot spot.  If the pHotSpot parameter is not specified then a default of 0,0 is used (unless the pWithCrossHairs parameter is specified as true - in which case the hot spot defaults to 5,5).
  • If the pWithCrossHairs parameter is specified with a value of true then cross-hairs are added to the created cursor (and the cursor size is increased accordingly).

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

  • Version 1.0.16 [2009-02-02] - public release to CodeProject 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United Kingdom United Kingdom
I've been involved in software design and development for more years than I care to remember. Started out in Engineering and drifted into computing as a real-time software engineer (assembler) working on industrial robotic systems.
Moved into application development (in Clipper, Delphi and, more recently, .Net) in a wide variety of industries including banking, manufacturing, betting & gaming, travel industry, sport (Formula 1), public sector and a few others.
Spent many years involved in designing and developing commercial websites, becoming a specialist in XML and XSLT. Designed and developed an XSLT IDE (Xselerator), which is now available free on SourceForge.

Comments and Discussions

 
QuestionNice Pin
sarode12346-Feb-13 1:11
sarode12346-Feb-13 1:11 
GeneralMy vote of 5 Pin
Vladimir Svyatski11-Mar-11 9:06
professionalVladimir Svyatski11-Mar-11 9:06 
GeneralBrilliant! Pin
MaximilianReisch14-Oct-10 1:53
MaximilianReisch14-Oct-10 1:53 
GeneralThank you! Pin
romanprv6-Mar-10 14:34
romanprv6-Mar-10 14:34 
GeneralGreat work, helps me a lot!! Pin
gary.flu91-Jul-09 0:52
gary.flu91-Jul-09 0:52 
GeneralGreat Pin
richardw489-Feb-09 23:56
richardw489-Feb-09 23:56 
QuestionHow I did it Pin
Reelix9-Feb-09 19:11
Reelix9-Feb-09 19:11 
GeneralThank You Pin
redjes3-Feb-09 13:41
redjes3-Feb-09 13:41 
QuestionHuh? Pin
PIEBALDconsult2-Feb-09 7:58
mvePIEBALDconsult2-Feb-09 7:58 
AnswerRe: Huh? Pin
JoseMenendez2-Feb-09 8:33
JoseMenendez2-Feb-09 8:33 
lol Laugh | :laugh:

Jm
www.menendezpoo.com

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.