Click here to Skip to main content
15,883,901 members
Articles / Desktop Programming / Windows Forms
Article

TdhCursorFactory - A Class to Provide Animated and Color Cursors to DotNET Applications

Rate me:
Please Sign up or sign in to vote.
4.65/5 (16 votes)
1 Nov 2007CPOL9 min read 58K   649   38   23
This article describes a class to manage animated and color cursors and make them available for use in .NET applications. The cursors may derive from embedded resources, from standard cursors, or from files read from disk.
Screenshot - TdhCursorFactory.gif

Introduction

The .NET System.Windows.Forms.Cursor class doesn't natively support animated or color cursors. (And, if the constructor doesn't throw an exception when passed a color cursor, the colors vanish). But, one can use the LoadCursorFromFileW function of "user32.dll" to get around this constraint.

The 'TdhCursorFactory' Class

The TdhCursorFactory class is built on a basis primarily of code published in this MSDN article: Using Colored and Animated Cursors; and secondarily Bingzhe Quan's Code Project article: A scrollable, zoomable, and scalable picture box. But, then it takes off into parts unknown.

The class is static; its primary purpose is to create .NET usable color and animated cursors. As an added bonus, it can maintain an array of Cursors for easy access by the program making use of the library. The Cursors stored in this array may be used to "override" the standard Cursors of the System.Windows.Forms.Cursors class.

A Note On The LoadCursorFromFileW Function

The LoadCursorFromFileW function of "user32.dll" requires its argument to be the name of an actual file on disk (I was unable to find if there is a similar function which can take a memory stream). Thus, when the methods of TdhCursorFactory must make use of the LoadCursorFromFileW function as part of the process of extracting a Cursor object from the embedded resources (i.e. for animated cursors or color non-animated cursors), the resource is first extracted into a memory stream, which is written to a temporary file. This temporary file is then passed as the argument to the LoadCursorFromFileW function (and deleted afterward).

On the other hand, a black-and-white non-animated cursor may be passed as a memory stream directly to the [new System.Windows.Forms.Cursor(stream)] constructor. By default, the TdhCursorFactory class attempts this with cursors having the ".CUR" extension.

So, effectively, the only real advantage to making animated cursors or color non-animated cursors embedded resources of your project is that since they are "inside" the assembly, they won't get lost.

Using The Class

To use TdhCursorFactory as is, add a reference in your project to the class library 'TDHCursorFactory.dll.' The namespace used in this library is:

C#
using TDHControls.TDHCursorFactory;
//
{
    // Sample usage:
    TdhCursorFactory.SetCursor(TdhCursorFactory.Cursors._Busy, 
        @"C:\SomePath\SomeCursor.ani");
    TdhCursorFactory.SetCursor(TdhCursorFactory.Cursors.WaitCursor, true,
        "Cursors.Cow_chewing.ani", true);
    //  
    // The following statement will return the Cursor loaded from
    // "C:\SomePath\SomeCursor.ani"  
    this.Cursor = TdhCursorFactory.GetCursor(TdhCursorFactory.Cursors._Busy);
    // 
    // At the moment, the following statement will return the 
    // "Cursors.Cow_chewing.ani" animated cursor 

    // (from the library's embedded resources)
    this.Cursor = TdhCursorFactory.GetCursor(
        TdhCursorFactory.Cursors.WaitCursor);
    //  
    TdhCursorFactory.UnsetCursor(TdhCursorFactory.Cursors.WaitCursor);
    // The following statement will now return the standard  
    // System.Windows.Forms.Cursors.WaitCursor Cursor object 
    this.Cursor = TdhCursorFactory.GetCursor(
        TdhCursorFactory.Cursors.WaitCursor);
    //   
    // There is no "EarlyBird.ani" in the Demo project's embedded resources  
    // ("Demo_Cursors" is the name of a folder under the Demo project)  
    // Thus, the following statement will have no effect upon the Cursor  
    // to which the enum value TdhCursorFactory.Cursors._Busy refers 
    TdhCursorFactory.SetCursor(TdhCursorFactory.Cursors._Busy, true,
        "Demo_Cursors.EarlyBird.ani", true);
    //  
    // There is a "Drum.ani" in the Demo project's embedded resources.  
    // However, this statement will not find it (the correct full-name is
    // "Demo_Cursors.Drum.ani")  
    TdhCursorFactory.SetCursor(TdhCursorFactory.Cursors._Busy, true,
        "Drum.ani", true);
    // The following statement will find the "Cursors.Drum.ani"
    // embedded resource  
    // in the Demo project's resources manifest.  
    TdhCursorFactory.SetCursor(TdhCursorFactory.Cursors._Busy, true,
        "Drum.ani", false);
    //   
    // The .FromFile() method always makes use of the LoadCursorFromFileW 

    // function of "user32.dll"  
    this.Cursor = TdhCursorFactory.FromFile(@"C:\SomePath\SomeCursor.cur",
        false);
    //   
    // The .FromResource() method makes use of   
    // * either the LoadCursorFromFileW function of "user32.dll"   
    // * or the the [new System.Windows.Forms.Cursor(stream)] constructor.   
    // If the extension is ".ANI," or if the first argument is 'true,'   
    // the LoadCursorFromFileW function will be used.   
    // Otherwise, an attempt is first made to use  
    // the [new System.Windows.Forms.Cursor(stream)] constructor.   
    // If that attempt fails, the LoadCursorFromFileW function is used.   
    this.Cursor = TdhCursorFactory.FromResource(false, 
        "Demo_Cursors.SomeCursor.cur", true, false);
}

The TdhCursorFactory class was written (and compiled) using VS2002 (.NET 1.0) with the intention that the source code be readily available to other developers regardless of the .NET version they are using.

The members of TdhCursorFactory's interface are:

  • Cursors — The items in the array of Cursors managed by the class are accessed via the TdhCursorFactory.Cursors enum (see below for a list of members).

  • public static System.Windows.Forms.Cursor GetCursor(TdhCursorFactory.Cursors whichCursor)
    This method returns a System.Windows.Forms.Cursor object from the array of cursors managed by the class, as named by the 'whichCursor' value. If that particular cursor has not been set, the method returns the standard System.Windows.Forms.Cursors.Default object.

  • public static void InitCursor_X(TdhCursorFactory.Cursors whichCursor)
    This method initializes the "sub-array" of Cursors referenced by the 'whichCursor' enum value (at this time, only the TdhCursorFactory.Cursors._X_Random_Busy enum member is so defined) to a set of pre-defined Cursor objects obtained from the class's resources manifest. The method is not executed automatically by the class's constructor, but rather if it is desired that these cursors be loaded, the method must be explicitly called by the main program.

  • public static void SetCursor(TdhCursorFactory.Cursors whichCursor, bool forceAsColor, string cursorName, bool caseSensitive)
    This method sets the managed Cursor named by 'whichCursor' to a System.Windows.Forms.Cursor object obtained from the embedded Resources of either: 1) the calling assembly, or 2) the TdhCursorFactory class' assembly. If the 'cursorName' value cannot be found in either assembly (or if it is not a valid Cursor), the method will leave the managed Cursor as is. The 'caseSensitive' value determines whether the 'cursorName' value must be given exactly. When this value is false, the class will examine all the object names contained in the assembly's resources manifest. The value of 'forceAsColor' allows the developer using the class to force ".CUR" files to be loaded via the LoadCursorFromFileW function of "user32.dll." (Cursors with the ".ANI" extension are always loaded via this function).

  • public static void SetCursor(TdhCursorFactory.Cursors whichCursor, string fileName)
    This method sets the managed Cursor named by 'whichCursor' to the System.Windows.Forms.Cursor object read from the file with the path given in 'fileName.' If the given path does not resolve to a valid Cursor, the method will leave the managed Cursor as is.

  • public static void SetCursor(TdhCursorFactory.Cursors whichCursor, System.Windows.Forms.Cursor theCursor)
    This method sets the managed Cursor named by 'whichCursor' to the System.Windows.Forms.Cursor object passed as 'theCursor.'

  • public static void UnsetCursor(TdhCursorFactory.Cursors whichCursor)
    This method releases the managed Cursor named by 'whichCursor.'

  • public static System.Windows.Forms.Cursor FromFile(string fileName, bool allowNull)
    This method returns a System.Windows.Forms.Cursor object read from the file with the path given in 'fileName.' If the given path does not resolve to a valid Cursor, the method will return either a null object or the System.Windows.Forms.Cursors.Default object, depending on the value of 'allowNull.'

  • public static System.Windows.Forms.Cursor FromResource(bool forceAsColor, string cursorName, bool caseSensitive, bool allowNull)
    This method returns a System.Windows.Forms.Cursor object obtained from the embedded Resources of either: 1) the calling assembly, or 2) the TdhCursorFactory class' assembly. If the 'cursorName' value cannot be found in either assembly (or if it is not a valid Cursor), the method will return either a null object or the System.Windows.Forms.Cursors.Default object, depending on the value of 'allowNull.' The 'caseSensitive' value determines whether the 'cursorName' value must be given exactly. When this value is false, the class will examine all the object names contained in the assembly's resources manifest. The value of 'forceAsColor' allows the developer using the class to force ".CUR" files to be loaded via the LoadCursorFromFileW function of "user32.dll." (Cursors with the ".ANI" extension are always loaded via this function).

  • public static System.Collections.ArrayList ResourcesManifest(bool forCallingAssembly)
    This method returns an ArrayList (of string objects) containing the fully qualified names of the ".ANI" and ".CUR" items in the resources manifest of either the calling assembly or the 'TDHCursorFactory.dll' assembly.


As written/assembled, the 'TDHCursorFactory.dll' assembly contains a number of cursors as embedded resources. Since the class as written is geared towards my overall project, you may well want to eliminate many (or all) of these; and you may want to add your own cursors as resources ... aren't you glad you have the source code?

  • Cursors.arrow_l.cur
  • Cursors.arrow_l_blue.cur
  • Cursors.arrow_l_red-outline.cur
  • Cursors.BlindMouse.ani
  • Cursors.busy_l.cur
  • Cursors.counter.ani
  • Cursors.Cow_chewing.ani
  • Cursors.EarlyBird.ani
  • Cursors.FlyingPig.ani
  • Cursors.HamsterWheel.ani
  • Cursors.HamtonJPig3.cur
  • Cursors.HorseRider.ani
  • Cursors.PanToolCursor.cur
  • Cursors.PanToolCursorMouseDown.cur
  • Cursors.PorkyPig.cur


The TdhCursorFactory.Cursors enum is the means by which the array of Cursors internally managed by the TdhCursorFactory class are accessed. This has both advantages and disadvantages. The main disadvantage is that the only way to add items to the enum is to recompile the class. The advantages are legion, including: the TdhCursorFactory class manages the set of cursors your program is using for you, all you need do is initially set them; you may "override" (or not, as you wish) the standard cursors of the System.Windows.Forms.Cursors class -- your main program's code will be given a valid cursor object in either case; once you are satisfied with *your* version of the TdhCursorFactory.Cursors enum, your version of the TdhCursorFactory class will be both flexible enough to be useful for any program needing color and/or animated cursors and simultaneously specific to your own needs.

The members of the TdhCursorFactory.Cursors enum are :

  • There are a number of members for custom-defined Cursors. By default, the class constructor extracts embedded resources from its assembly for these items, though the main program can replace (or release) any of them at any time. These custom definitions are, of course, geared towards my overall project; you may want to add or remove custom definitions.
    • Cursors._Busy
    • Cursors._Grab
    • Cursors._GrabRelease
    • Cursors._Normal
    • Cursors._X_Random_Busy - a special member; it (randomly) accesses the elements of a "sub-array" of Cursors
  • The remainder of members may be used to "override" the standard Cursors of the System.Windows.Forms.Cursors class. Any to which a Cursor has not been explicitly set (via the [ SetCursor() ] methods) will return (via the [ GetCursor() ] method) the corresponding standard Cursor:
    • Cursors.AppStarting
    • Cursors.Arrow
    • Cursors.Cross
    • Cursors.Default
    • Cursors.Hand
    • Cursors.Help
    • Cursors.HSplit
    • Cursors.IBeam
    • Cursors.No
    • Cursors.NoMove2D
    • Cursors.NoMoveHoriz
    • Cursors.NoMoveVert
    • Cursors.PanEast
    • Cursors.PanNE
    • Cursors.PanNorth
    • Cursors.PanNW
    • Cursors.PanSE
    • Cursors.PanSouth
    • Cursors.PanSW
    • Cursors.PanWest
    • Cursors.SizeAll
    • Cursors.SizeNESW
    • Cursors.SizeNS
    • Cursors.SizeNWSE
    • Cursors.SizeWE
    • Cursors.UpArrow
    • Cursors.VSplit
    • Cursors.WaitCursor

History

  • 2007 October 26: Submission of TdhCursorFactory ver 1.0.001 to The Code Project.
  • 2007 October 27: ver 1.0.002
    • Modified the SetCursor(TdhCursorFactory.Cursors whichCursor, string cursorName, bool caseSensitive) method.
      Previously, if the string cursorName value could not be found in the resources manifest (or did not resolve to a valid Cursor), the method would set the array item referenced by the TdhCursorFactory.Cursors whichCursor value to a null object. Now, there will be no effect.
    • Created the public static System.Collections.ArrayList ResourcesManifest(bool forCallingAssembly) method.
  • 2007 October 29: ver 1.0.003
    • Modified the methods: FromResource() and (one signature of) SetCursor() (sorry about that!)
      - Added the argument "bool forceAsColor" to allow the developer using the class to force ".CUR" files to be loaded via the LoadCursorFromFileW function of "user32.dll." The value of this argument is relevant only for Cursors with the ".CUR" extension; Cursors with the ".ANI" extension are always loaded via the LoadCursorFromFileW function of "user32.dll."
  • 2007 October 29: ver 1.1.000
    • Added the special '_X_Random_Busy' member to the TdhCursorFactory.Cursors enum.
      The '_X_Random_Busy' member may be used to access a "sub-array" of Cursors -- It randomly points to one of the Cursors which have been loaded into the "sub-array." Individual Cursors may be loaded into this "sub-array" via the existing [ SetCursor() ] methods; the [ GetCursor() ] method will randomly return one of the Cursors loaded into the "sub-array."
    • Created the [ InitCursor_X() ] method.
      This method initializes the "sub-array" of Cursors referenced by the enum value TdhCursorFactory.Cursors._X_Random_Busy to a set of pre-defined Cursor objects obtained from the class' resources manifest.

License

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


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionHow to embed the cursors in resources? Pin
mosquets21-Nov-07 7:53
mosquets21-Nov-07 7:53 
AnswerRe: How to embed the cursors in resources? Pin
Ilíon21-Nov-07 22:53
Ilíon21-Nov-07 22:53 
GeneralRe: How to embed the cursors in resources? Pin
mosquets22-Nov-07 12:14
mosquets22-Nov-07 12:14 
GeneralRe: How to embed the cursors in resources? Pin
Ilíon22-Nov-07 21:00
Ilíon22-Nov-07 21:00 
GeneralLoading from Embedded Resource Pin
Jakka Prihatna1-Nov-07 21:12
Jakka Prihatna1-Nov-07 21:12 
GeneralRe: Loading from Embedded Resource Pin
Ilíon2-Nov-07 2:04
Ilíon2-Nov-07 2:04 
GeneralRe: Loading from Embedded Resource Pin
Jakka Prihatna4-Nov-07 15:56
Jakka Prihatna4-Nov-07 15:56 
GeneralRe: Loading from Embedded Resource [modified] Pin
Ilíon5-Nov-07 17:18
Ilíon5-Nov-07 17:18 
GeneralRe: Loading from Embedded Resource Pin
Ilíon6-Nov-07 7:13
Ilíon6-Nov-07 7:13 
GeneralRe: Loading from Embedded Resource Pin
Jakka Prihatna6-Nov-07 22:49
Jakka Prihatna6-Nov-07 22:49 
GeneralGood But Not Stellar Pin
Mark Treadwell30-Oct-07 0:59
professionalMark Treadwell30-Oct-07 0:59 
GeneralRe: Good But Not Stellar Pin
Ilíon1-Nov-07 12:08
Ilíon1-Nov-07 12:08 
GeneralRe: Good But Not Stellar Pin
fwsouthern1-Nov-07 15:25
fwsouthern1-Nov-07 15:25 
GeneralRe: Good But Not Stellar Pin
Ilíon1-Nov-07 16:31
Ilíon1-Nov-07 16:31 
GeneralRe: Good But Not Stellar Pin
fwsouthern1-Nov-07 16:36
fwsouthern1-Nov-07 16:36 
GeneralRe: Good But Not Stellar Pin
Ilíon1-Nov-07 16:38
Ilíon1-Nov-07 16:38 
I know. But then, that makes it canny, after all, doesn't it? Poke tongue | ;-P
GeneralRe: Good But Not Stellar Pin
Mark Treadwell1-Nov-07 16:46
professionalMark Treadwell1-Nov-07 16:46 
AnswerRe: Good But Not Stellar Pin
Mark Treadwell3-Nov-07 16:01
professionalMark Treadwell3-Nov-07 16:01 
NewsRe: Good But Not Stellar Pin
Ilíon3-Nov-07 18:18
Ilíon3-Nov-07 18:18 
GeneralRe: Good But Not Stellar Pin
Mark Treadwell3-Nov-07 19:18
professionalMark Treadwell3-Nov-07 19:18 
NewsRe: Good But Not Stellar Pin
Ilíon3-Nov-07 18:43
Ilíon3-Nov-07 18:43 
General5 Thumbs Up! Pin
Ri Qen-Sin27-Oct-07 3:35
Ri Qen-Sin27-Oct-07 3:35 
GeneralRe: 5 Thumbs Up! [modified] Pin
Ilíon27-Oct-07 6:30
Ilíon27-Oct-07 6:30 

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.