Click here to Skip to main content
15,896,557 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.5K   2.8K   69  
A design-time component to make use of animated/multi-coloured cursors
using System;
using System.Collections;
using System.ComponentModel;
using System.Resources;
using System.Drawing;
using System.Drawing.Design;
using System.Windows.Forms;
using System.IO;
using System.Runtime.InteropServices;
using System.Reflection;
using ExtCursors.Design;

namespace ExtCursors
{
    #region "Internal enumerators"
    internal enum _DesignLoadCursorFrom
    {
        LocalResource,
        ResxResource,
        File,
        None,
        RuntimeCreated,
        RuntimeCreatedResxResource
    }
    #endregion //"Internal enumerators"

    #region "Class : ExtCursor"
    // ========================================================================
	// ========================================================================
    /// <summary>
    /// An extended cursor class that can load animated and multi-coloured cursors.
    /// </summary>
    [ToolboxItem(true)]
    [DesignTimeVisible(true)]
    [ToolboxBitmap(typeof(ExtCursor), "Images.ExtCursor.bmp")]
	[ToolboxItemFilter("System.Windows.Forms")]
	[DefaultBindingProperty("CursorFile")]
	[DefaultProperty("CursorFile")]
	public class ExtCursor : Component, ISupportInitialize
    {
        #region "Private/Internal fields"
        #region "Private fields - disposal"
        private bool _Disposed = false;
        #endregion //"Private fields - disposal"
        #region "Private fields - ISupportInitialize flags"
        internal bool _Initializing = false;
        #endregion //"Private fields - ISupportInitialize flags"
        internal MemoryStream _CursorStream = null;
        internal Cursor _Cursor = null;
        internal _DesignLoadCursorFrom _LoadFrom;
		private string _Filename = "";
		private string _CursorLocation = "";
		private bool _CursorFileLocal = false;
        internal string _ResourceName = "";
        internal string _ResXName = "";
        internal IntPtr _HCursor = IntPtr.Zero;
        private bool _SharedHandle = false;
        private bool _OwnedHandle = false;
        private Size _PreferredSize = new Size(0, 0);
        private bool _DefaultSize = true;
        #endregion //"Private/Internal fields"
        #region "Event delegate keys"
        internal static readonly object EventHandleCreated = new object();
        internal static readonly object EventHandleDestroyed = new object();
        #endregion //"Event delegate keys"
        #region "User32 Interop"
        [DllImport("user32.dll", EntryPoint = "LoadCursorFromFileW", CharSet = System.Runtime.InteropServices.CharSet.Unicode)]
		internal static extern IntPtr LoadCursorFromFile(string lpFilename);
        [DllImport("user32.dll", EntryPoint = "DestroyCursor", CharSet = CharSet.Auto)]
        internal static extern bool DestroyCursor(IntPtr hCursor);
        [DllImport("user32.dll", EntryPoint = "LoadImage", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
        internal static extern IntPtr LoadImage(IntPtr hinst, string stName, int nType, int cxDesired, int cyDesired, int nFlags);
        internal const int LR_DEFAULTCOLOR = 0x0000,
                         LR_MONOCHROME = 0x0001,
                         LR_COLOR = 0x0002,
                         LR_COPYRETURNORG = 0x0004,
                         LR_COPYDELETEORG = 0x0008,
                         LR_LOADFROMFILE = 0x0010,
                         LR_LOADTRANSPARENT = 0x0020,
                         LR_DEFAULTSIZE = 0x0040,
                         LR_VGACOLOR = 0x0080,
                         LR_LOADMAP3DCOLORS = 0x1000,
                         LR_CREATEDIBSECTION = 0x2000,
                         LR_COPYFROMRESOURCE = 0x4000,
                         LR_SHARED = unchecked((int)0x8000);
        internal const int IMAGE_CURSOR = 2;
        #endregion //"User32 Interop"

        #region "Constructors"
        // ========================================================================
        /// <summary>
        /// Initializes a new instance of the ExtCursor class.
        /// [for design time only]
        /// </summary>
        /// <param name="imageList">ImageList to use as source of images.</param>
        /// <param name="imageIndexEnabled">Index into list for enabled image.</param>
        public ExtCursor()
		{
            _LoadFrom = _DesignLoadCursorFrom.None;
		}

        // ========================================================================
        /// <summary>
        /// Initializes a new instance of the ExtCursor class from the specified file.
        /// </summary>
        /// <param name="pFilename">The cursor file to load.</param>
        public ExtCursor(string pFilename)
            : this(pFilename, true)
        {
        }

        // ========================================================================
        /// <summary>
        /// Initializes a new instance of the ExtCursor class from the specified file.
        /// </summary>
        /// <param name="pFilename">The cursor file to load.</param>
        /// <param name="pShareable">Whether the cursor handle is to be shared.</param>
        public ExtCursor(string pFilename, bool pShareable)
        {
            _LoadFrom = _DesignLoadCursorFrom.File;
            bool Loaded = false;
            if (pShareable)
            {
                _SharedHandle = true;
                _CursorLocation = pFilename;
                Loaded = ExtCursorsSharedRepository.Instance.AssignSharedHandle(this, _DefaultSize, _PreferredSize, null);
            }
            if (!Loaded)
            {
                _SharedHandle = false;
                FileStream LoadStream = null;
                try
                {
                    LoadStream = new FileStream(pFilename, FileMode.Open, FileAccess.Read);
                    LoadFromStream(LoadStream);
                }
                finally
                {
                    if (LoadStream != null)
                    {
                        LoadStream.Dispose();
                    }
                }
            }
        }

        // ========================================================================
        /// <summary>
        /// Initializes a new instance of the ExtCursor class from the supplied byte data.
        /// </summary>
        /// <param name="pCursorData">The byte data to load the cursor from.</param>
        public ExtCursor(byte[] pCursorData)
        {
            _LoadFrom = _DesignLoadCursorFrom.RuntimeCreated;
            LoadFromStream(pCursorData);
        }

        // ========================================================================
        /// <summary>
        /// Initializes a new instance of the ExtCursor class from the supplied stream.
        /// </summary>
        /// <param name="pCursorStream">The stream to load the cursor from.</param>
        public ExtCursor(Stream pCursorStream)
        {
            _LoadFrom = _DesignLoadCursorFrom.RuntimeCreated;
            LoadFromStream(pCursorStream);
        }

        // ========================================================================
        /// <summary>
        /// Initializes a new instance of the ExtCursor class from a resource.
        /// </summary>
        /// <param name="pResourceAssembly">The assembly containing the resource.</param>
        /// <param name="pResXName">The name of the compiled resource file, e.g. "MyApp.Properties.Resources".</param>
        /// <param name="pResourceName">The name of the resource.</param>
        public ExtCursor(Assembly pResourceAssembly, string pResXName, string pResourceName)
            : this(pResourceAssembly, pResXName, pResourceName, true)
        {
        }

        // ========================================================================
        /// <summary>
        /// Initializes a new instance of the ExtCursor class from a resource.
        /// </summary>
        /// <param name="pResourceAssembly">The assembly containing the resource.</param>
        /// <param name="pResXName">The name of the compiled resource file, e.g. "MyApp.Properties.Resources".</param>
        /// <param name="pResourceName">The name of the resource.</param>
        /// <param name="pShareable">Whether the cursor handle is to be shared.</param>
        public ExtCursor(Assembly pResourceAssembly, string pResXName, string pResourceName, bool pShareable)
        {
            _LoadFrom = _DesignLoadCursorFrom.RuntimeCreatedResxResource;
            bool Loaded = false;
            if (pShareable)
            {
                _SharedHandle = true;
                _ResXName = pResXName;
                _ResourceName = pResourceName;
                Loaded = ExtCursorsSharedRepository.Instance.AssignSharedHandle(this, _DefaultSize, _PreferredSize, pResourceAssembly);
            }
            if (!Loaded)
            {
                ResourceManager ResX = new ResourceManager(pResXName, pResourceAssembly);
                byte[] CursorData = (byte[])ResX.GetObject(pResourceName, null);
                LoadFromStream(CursorData);
            }
        }

        // ========================================================================
        /// <summary>
        /// Initializes a new instance of the ExtCursor class from the specified file.
        /// </summary>
        /// <param name="pFilename">The cursor file to load.</param>
        /// <param name="pDefaultSize">Whether the default system metric cursor size should be used.</param>
        /// <param name="pPreferredSize">The preferred size of the cursor to load.</param>
        public ExtCursor(string pFilename, bool pDefaultSize, Size pPreferredSize)
            : this(pFilename, pDefaultSize, pPreferredSize, true)
        {
        }

        // ========================================================================
        /// <summary>
        /// Initializes a new instance of the ExtCursor class from the specified file.
        /// </summary>
        /// <param name="pFilename">The cursor file to load.</param>
        /// <param name="pDefaultSize">Whether the default system metric cursor size should be used.</param>
        /// <param name="pPreferredSize">The preferred size of the cursor to load.</param>
        /// <param name="pShareable">Whether the cursor handle is to be shared.</param>
        public ExtCursor(string pFilename, bool pDefaultSize, Size pPreferredSize, bool pShareable)
        {
            _LoadFrom = _DesignLoadCursorFrom.File;
            bool Loaded = false;
            if (pShareable)
            {
                _CursorLocation = pFilename;
                _SharedHandle = true;
                _DefaultSize = pDefaultSize;
                _PreferredSize = new Size(pPreferredSize.Width, pPreferredSize.Height);
                Loaded = ExtCursorsSharedRepository.Instance.AssignSharedHandle(this, _DefaultSize, _PreferredSize, null);
            }
            if (!Loaded)
            {
                _SharedHandle = false;
                LoadFromFileEx(pFilename, pDefaultSize, pPreferredSize);
            }
        }

        // ========================================================================
        /// <summary>
        /// Initializes a new instance of the ExtCursor class from the supplied byte data.
        /// </summary>
        /// <param name="pCursorData">The byte data to load the cursor from.</param>
        /// <param name="pDefaultSize">Whether the default system metric cursor size should be used.</param>
        /// <param name="pPreferredSize">The preferred size of the cursor to load.</param>
        public ExtCursor(byte[] pCursorData, bool pDefaultSize, Size pPreferredSize)
        {
            _LoadFrom = _DesignLoadCursorFrom.RuntimeCreated;
            LoadFromStreamEx(pCursorData, pDefaultSize, pPreferredSize);
        }

        // ========================================================================
        /// <summary>
        /// Initializes a new instance of the ExtCursor class from the supplied stream.
        /// </summary>
        /// <param name="pCursorStream">The stream to load the cursor from.</param>
        /// <param name="pDefaultSize">Whether the default system metric cursor size should be used.</param>
        /// <param name="pPreferredSize">The preferred size of the cursor to load.</param>
        public ExtCursor(Stream pCursorStream, bool pDefaultSize, Size pPreferredSize)
        {
            _LoadFrom = _DesignLoadCursorFrom.RuntimeCreated;
            LoadFromStreamEx(pCursorStream, pDefaultSize, pPreferredSize);
        }

        // ========================================================================
        /// <summary>
        /// Initializes a new instance of the ExtCursor class from a resource.
        /// </summary>
        /// <param name="pResourceAssembly">The assembly containing the resource.</param>
        /// <param name="pResXName">The name of the compiled resource file, e.g. "MyApp.Properties.Resources".</param>
        /// <param name="pResourceName">The name of the resource.</param>
        /// <param name="pDefaultSize">Whether the default system metric cursor size should be used.</param>
        /// <param name="pPreferredSize">The preferred size of the cursor to load.</param>
        public ExtCursor(Assembly pResourceAssembly, string pResXName, string pResourceName, bool pDefaultSize, Size pPreferredSize)
            : this(pResourceAssembly, pResXName, pResourceName, pDefaultSize, pPreferredSize, true)
        {
        }

        // ========================================================================
        /// <summary>
        /// Initializes a new instance of the ExtCursor class from a resource.
        /// </summary>
        /// <param name="pResourceAssembly">The assembly containing the resource.</param>
        /// <param name="pResXName">The name of the compiled resource file, e.g. "MyApp.Properties.Resources".</param>
        /// <param name="pResourceName">The name of the resource.</param>
        /// <param name="pDefaultSize">Whether the default system metric cursor size should be used.</param>
        /// <param name="pPreferredSize">The preferred size of the cursor to load.</param>
        /// <param name="pShareable">Whether the cursor handle is to be shared.</param>
        public ExtCursor(Assembly pResourceAssembly, string pResXName, string pResourceName, bool pDefaultSize, Size pPreferredSize, bool pShareable)
        {
            _LoadFrom = _DesignLoadCursorFrom.RuntimeCreatedResxResource;
            bool Loaded = false;
            if (pShareable)
            {
                _SharedHandle = true;
                _ResXName = pResXName;
                _ResourceName = pResourceName;
                _DefaultSize = pDefaultSize;
                _PreferredSize = new Size(pPreferredSize.Width, pPreferredSize.Height);
                Loaded = ExtCursorsSharedRepository.Instance.AssignSharedHandle(this, _DefaultSize, _PreferredSize, pResourceAssembly);
            }
            if (!Loaded)
            {
                ResourceManager ResX = new ResourceManager(pResXName, pResourceAssembly);
                byte[] CursorData = (byte[])ResX.GetObject(pResourceName, null);
                LoadFromStreamEx(CursorData, pDefaultSize, pPreferredSize);
            }
        }
        #endregion //"Constructors"

        #region "Loading methods"
        // ========================================================================
        private void LoadFromStreamEx(byte[] pCursorData, bool pDefaultSize, Size pPreferredSize)
        {
            MemoryStream LoadStream = new MemoryStream(pCursorData);
            try
            {
                LoadFromStreamEx(LoadStream, pDefaultSize, pPreferredSize);
            }
            finally
            {
                LoadStream.Dispose();
            }
        }

        // ========================================================================
        private bool LoadFromStreamEx(Stream pCursorStream, bool pDefaultSize, Size pPreferredSize)
        {
            bool Result = false;
            FileStream TempStream = null;
            string TempFileName = "";
            try
            {
                TempFileName = System.IO.Path.GetTempFileName();
                TempStream = new FileStream(TempFileName, FileMode.Open, FileAccess.Write, FileShare.None);
                StreamTransfer(pCursorStream, TempStream);
                // belt'n'braces...
                TempStream.Flush();
                TempStream.Close();
                TempStream.Dispose();
                TempStream = null;
                // load from temp file...
                Result = LoadFromFileEx(TempFileName, pDefaultSize, pPreferredSize);
            }
            finally
            {
                if (TempStream != null)
                {
                    TempStream.Close();
                    TempStream.Dispose();
                    TempStream = null;
                }
                if ((TempFileName != "") && File.Exists(TempFileName))
                {
                    try
                    {
                        File.Delete(TempFileName);
                    }
                    catch
                    {
                        // swallow exceptions
                        // worst case - temp file may not have been deleted?
                    }
                }
            }
            return Result;
        }

        // ========================================================================
        private bool LoadFromFileEx(string pFilename, bool pDefaultSize, Size pPreferredSize)
        {
            _DefaultSize = pDefaultSize;
            _PreferredSize = new Size(pPreferredSize.Width, pPreferredSize.Height);
            int Flags = LR_DEFAULTCOLOR | LR_LOADFROMFILE;
            if (pDefaultSize)
            {
                Flags = Flags | LR_DEFAULTSIZE;
            }
            return AssignHCursor(LoadImage(IntPtr.Zero, pFilename, IMAGE_CURSOR, pPreferredSize.Width, pPreferredSize.Height, Flags), true);
        }

        // ========================================================================
        private void LoadFromStream(byte[] pCursorData)
        {
            MemoryStream LoadStream = new MemoryStream(pCursorData);
            try
            {
                LoadFromStream(LoadStream);
            }
            finally
            {
                LoadStream.Dispose();
            }
        }

        // ========================================================================
        private void LoadFromStream(Stream pCursorStream)
        {
/*
 * Used to try to load the cursor directly from the stream - but...
 * as we're most likely to be using ExtCursor to load a cursor that Cursor can't cope with,
 * there seems little point in waiting for Cursor to figure out it can't cope - then raise an
 * exception - then try to load the handle (which we could do from the start), there seems little or
 * no advantage.
            bool Loaded = false;
            try
            {
                pCursorStream.Seek(0, SeekOrigin.Begin);
                _Cursor = new Cursor(pCursorStream);
                _OwnedHandle = false;
                Loaded = true;
            }
            catch
            {
            }
 */
            //if (!Loaded)
            //{
                FileStream TempStream = null;
                string TempFileName = "";
                try
                {
                    TempFileName = System.IO.Path.GetTempFileName();
                    TempStream = new FileStream(TempFileName, FileMode.Open, FileAccess.Write, FileShare.None);
                    StreamTransfer(pCursorStream, TempStream);
                    // belt'n'braces...
                    TempStream.Flush();
                    TempStream.Close();
                    TempStream.Dispose();
                    TempStream = null;
                    if (!AssignHCursor(ExtCursor.LoadCursorFromFile(TempFileName), true))
                    {
                        throw new Exception("Unable to load cursor");
                    }
                }
                finally
                {
                    if (TempStream != null)
                    {
                        TempStream.Close();
                        TempStream.Dispose();
                        TempStream = null;
                    }
                    if ((TempFileName != "") && File.Exists(TempFileName))
                    {
                        try
                        {
                            File.Delete(TempFileName);
                        }
                        catch
                        {
                            // swallow exceptions
                            // worst case - temp file may not have been deleted?
                        }
                    }
                }
            //}
        }
        #endregion //"Loading methods"

        #region "Cursor drop and build"
        // ========================================================================
		private void DropCursor(bool disposing)
		{
			if (_Cursor != null)
			{
				_Cursor.Dispose();
				_Cursor = null;
			}
            if (_OwnedHandle && (_HCursor != IntPtr.Zero))
            {
                // we own the handle to the cursor - so it's our responsibility to destroy it...
                try
                {
                    if (DestroyCursor(_HCursor))
                    {
                        _HCursor = IntPtr.Zero;
                        _OwnedHandle = false;
                        if (!disposing)
                        {
                            // invoke HandleDestroyed events...
                            DoHandleDestroyed();
                        }
                    }
                }
                catch
#if DEBUG
                    (Exception ex)
#endif
                {
                    // swallow exceptions
                    // worst case - unknown scenario
#if DEBUG
                    throw ex;
#endif
                }
            }
		}

		// ========================================================================
		private void ReBuildCursor()
		{
            // drop the existing cursor (and handle, if neccessary)...
			DropCursor(false);
            if (!DesignMode && _SharedHandle && 
                ((_LoadFrom == _DesignLoadCursorFrom.File) || (_LoadFrom == _DesignLoadCursorFrom.ResxResource)))
            {
                // get the handle from shared handle repository...
                if (ExtCursorsSharedRepository.Instance.AssignSharedHandle(this, _DefaultSize, _PreferredSize, null))
                {
                    // shared handle used - no further action required...
                    return;
                }
            }
            if (!DesignMode)
            {
                // remove any previous handle sharing...
                ExtCursorsSharedRepository.Instance.RemovePreviousReferences(this, true);
            }
			bool Loaded = false;
            if (_CursorStream != null)
			{
                LoadFromStreamEx(_CursorStream, _DefaultSize, _PreferredSize);
			}
			else if (_LoadFrom == _DesignLoadCursorFrom.File)
			{
                LoadFromFileEx(LocalPath, _DefaultSize, _PreferredSize);
				if (DesignMode && (_Cursor != null))
				{
                    // get the stream so it matches the file specified...
                    LoadCursorStreamFromFile(_CursorLocation);
				}
			}
            else if (_LoadFrom == _DesignLoadCursorFrom.ResxResource)
            {
                if (DesignMode)
                {
                    // load it directly from the originating file in the resource...
                    Loaded = LoadFromFileEx(_Filename, _DefaultSize, _PreferredSize);
                    if (Loaded)
                    {
                        // get the stream so it matches the file specified...
                        LoadCursorStreamFromFile(_Filename);
                    }
                }
                else
                {
                    // load directly from resource at runtime...
                    try
                    {
                        Assembly ExecAssy = Assembly.GetEntryAssembly();
                        ResourceManager ResX = new ResourceManager(_ResXName, ExecAssy);
                        byte[] CurData;
                        object obj = ResX.GetObject(_ResourceName, null);
                        CurData = (byte[])obj;
                        _CursorStream = new MemoryStream(CurData);
                        // try to load it from stream...
                        LoadFromStreamEx(_CursorStream, _DefaultSize, _PreferredSize);
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show("Error loading cursor resource: " + _ResXName + "." + _ResourceName + "\r" + ex.Message, "ExtCursor Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
            }
		}

        // ========================================================================
        internal string LocalPath
        {
            get
            {
                if (!DesignMode && _CursorFileLocal)
                {
                    string CursorPath = _CursorLocation;
                    string LocalPath = Path.GetDirectoryName(Application.ExecutablePath);
                    string SubPath = Path.GetDirectoryName(_CursorLocation);
                    bool FileExists = false;
                    // the file maybe designated local - but the original path might have sub-directories...
                    do
                    {
                        CursorPath = LocalPath + @"\" + Path.GetFileName(CursorPath);
                        FileExists = File.Exists(CursorPath);
                        if (!FileExists)
                        {
                            if (SubPath == "")
                            {
                                break;
                            }
                            LocalPath = LocalPath + @"\" + Path.GetFileName(SubPath);
                            SubPath = Path.GetDirectoryName(SubPath);
                        }
                    } while (!FileExists);
                    return CursorPath;
                }
                else
                {
                    return _CursorLocation;
                }
            }
        }

        // ========================================================================
        internal bool AssignHCursor(IntPtr HCursor, bool Owned)
        {
            bool Result = false;
            if (HCursor != IntPtr.Zero)
            {
                Result = true;
                _OwnedHandle = Owned;
                _HCursor = HCursor;
                _Cursor = new Cursor(_HCursor);
                // do HandleCreated events...
                DoHandleCreated();
            }
            return Result;
        }

        // ========================================================================
        private static void StreamTransfer(Stream Source, Stream Destination)
        {
            int Len = System.Convert.ToInt32(Source.Length);
            Source.Seek(0, SeekOrigin.Begin);
            byte[] Buffer = new byte[Len];
            Source.Read(Buffer, 0, Len);
            Destination.Write(Buffer, 0, Len);
            Source.Seek(0, SeekOrigin.Begin);
        }

        // ========================================================================
        private void ClearAll()
        {
            ClearCursorStream();
            _LoadFrom = _DesignLoadCursorFrom.None;
            _Filename = "";
            _CursorLocation = "";
            _ResourceName = "";
            _ResXName = "";
        }

        // ========================================================================
        private void ClearCursorStream()
        {
            if (_CursorStream != null)
            {
                _CursorStream.Dispose();
                _CursorStream = null;
            }
        }

        // ========================================================================
        private bool LoadCursorStreamFromFile(string Filename)
        {
            bool Result = false;
            ClearCursorStream();
            FileStream Reader = null;
            try
            {
                Reader = new FileStream(Filename, FileMode.Open, FileAccess.Read);
                _CursorStream = new MemoryStream();
                StreamTransfer(Reader, _CursorStream);
                Result = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error loading cursor from filename: " + Filename + "\r" + ex.Message, "ExtCursor Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                ClearCursorStream();
            }
            finally
            {
                if (Reader != null)
                {
                    Reader.Close();
                    Reader.Dispose();
                }
            }
            return Result;
        }
        #endregion //"Cursor drop and build"

        #region "Do Events methods"
        //=====================================================================
        internal void DoHandleCreated()
        {
            if (!DesignMode && !_Initializing)
            {
                Delegate EventDelegate = Events[EventHandleCreated];
                if (EventDelegate != null)
                {
                    EventDelegate.DynamicInvoke(new object[] { new EventArgs() });
                }
            }
        }

        //=====================================================================
        private void DoHandleDestroyed()
        {
            if (!DesignMode && !_Initializing)
            {
                Delegate EventDelegate = Events[EventHandleDestroyed];
                if (EventDelegate != null)
                {
                    EventDelegate.DynamicInvoke(new object[] { new EventArgs() });
                }
            }
        }
        #endregion //"Do Events methods"

        #region "Public Properties"
        #region "Public Properties - Read-only, non-browsable"
        // ========================================================================
        /// <summary>
        /// Gets the System.Windows.Cursor contained in this ExtCursor.
        /// </summary>
        [Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [ReadOnly(true)]
		public Cursor Cursor
		{
			get
			{
				return _Cursor;
			}
		}

        // ========================================================================
        /// <summary>
        /// Gets handle of the cursor.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [ReadOnly(true)]
        public IntPtr Handle
        {
            get
            {
                return _HCursor;
            }
        }

        // ========================================================================
        /// <summary>
        /// Gets a value indicating whether the cursor handle has been created.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [ReadOnly(true)]
        public bool IsHandleCreated
        {
            get
            {
                return (_HCursor != IntPtr.Zero);
            }
        }
        #endregion //"Public Properties - Read-only, non-browsable"

        #region "Public Properties - non-browsable loading"
        // ========================================================================
        /// <summary>
        /// [Form Initialization only - DO NOT USE AT RUNTIME]
        /// </summary>
        [Browsable(false)]
        [DefaultValue(null)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [RefreshProperties(RefreshProperties.All)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public ExtCursorFileStreamer CursorStream
        {
            get
            {
                if ((_LoadFrom == _DesignLoadCursorFrom.LocalResource) && (_CursorStream != null))
                {
                    // from local resource and stream has been loaded...
                    return new ExtCursorFileStreamer(_CursorStream);
                }
                else
                {
                    // when the cursor is not loaded from local resource
                    // return null to prevent the cursor stream from being serialized into the forms resources...
                    return null;
                }
            }
            set
            {
                if (value.CursorData.Length == 0)
                {
                    _CursorStream = null;
                }
                else
                {
                    _CursorStream = new MemoryStream();
                    _CursorStream.Write(value.CursorData, 0, value.CursorData.Length);
                }
            }
        }

        // ========================================================================
        /// <summary>
        /// [Form Initialization only - DO NOT USE AT RUNTIME]
        /// </summary>
        [Browsable(false)]
        [DefaultValue("")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public string Resource
        {
            get
            {
                if (_LoadFrom == _DesignLoadCursorFrom.ResxResource)
                {
                    return _Filename + ";" + _ResXName + ";" + _ResourceName;
                }
                else
                {
                    return "";
                }
            }
            set
            {
                if (value != "")
                {
                    _CursorLocation = "";
                    _Filename = "";
                    _ResourceName = "";
                    _ResXName = "";
                    string[] ResBits = value.Split(new char[] { ';' });
                    if (ResBits.Length == 3)
                    {
                        _LoadFrom = _DesignLoadCursorFrom.ResxResource;
                        _Filename = ResBits[0];
                        _ResXName = ResBits[1];
                        _ResourceName = ResBits[2];
                        if (!_Initializing)
                        {
                            if (!LoadCursorStreamFromFile(_Filename))
                            {
                                ClearAll();
                            }
                            ReBuildCursor();
                        }
                    }
                }
            }
        }
        #endregion //"Public Properties - non-browsable loading"

        #region "Public Properties - Design browsable"
        // ========================================================================
        /// <summary>
        /// [Form Initialization only - DO NOT USE AT RUNTIME]
        /// </summary>
        [Category("Cursor")]
		[Description("The cursor stored in this component.")]
		[DefaultValue("")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[RefreshProperties(RefreshProperties.All)]
		[Bindable(BindableSupport.Default)]
		[Editor("ExtCursors.Design.ExtCursorEditor", typeof(UITypeEditor))]
		[TypeConverter("ExtCursors.Design.ExtCursorFileConverter")]
		[DisplayName("Cursor")]
        [DesignOnly(true)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public string CursorFile
		{
			get
			{
                if (_LoadFrom == _DesignLoadCursorFrom.ResxResource)
                {
                    return _Filename + ";" + _ResXName + ";" + _ResourceName;
                }
                else
                {
                    return _Filename;
                }
			}
			set
			{
                if (DesignMode)
                {
                    if (value != _Filename)
                    {
                        if (value == "")
                        {
                            // none...
                            ClearAll();
                            ReBuildCursor();
                        }
                        else if (value.IndexOf(';') > -1)
                        {
                            // from resource...
                            string[] ResBits = value.Split(new char[] { ';' });
                            if (ResBits.Length == 3)
                            {
                                _LoadFrom = _DesignLoadCursorFrom.ResxResource;
                                _CursorLocation = "";
                                _Filename = ResBits[0];
                                _ResXName = ResBits[1];
                                _ResourceName = ResBits[2];
                                if (!_Initializing)
                                {
                                    if (!LoadCursorStreamFromFile(_Filename))
                                    {
                                        ClearAll();
                                    }
                                }
                            }
                            else
                            {
                                ClearAll();
                            }
                            ReBuildCursor();
                        }
                        else
                        {
                            // from local (form) resource...
                            _LoadFrom = _DesignLoadCursorFrom.LocalResource;
                            _Filename = value;
                            _SharedHandle = false;
                            _CursorLocation = "";
                            _ResourceName = "";
                            _ResXName = "";
                            if (!_Initializing)
                            {
                                if (!LoadCursorStreamFromFile(_Filename))
                                {
                                    ClearAll();
                                }
                                ReBuildCursor();
                            }
                        }
                    }
                }
			}
		}

		// ========================================================================
        /// <summary>
        /// [Form Initialization only - DO NOT USE AT RUNTIME]
        /// </summary>
        [Category("Cursor")]
		[Description("Specifies the file location from which the cursor is loaded at runtime.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[RefreshProperties(RefreshProperties.All)]
		[DefaultValue("")]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public string CursorLocation
		{
			get
			{
				return _CursorLocation;
			}
			set
			{
				if (value != _CursorLocation)
				{
                    _LoadFrom = _DesignLoadCursorFrom.File;
					_CursorLocation = value;
                    _Filename = "";
                    _ResourceName = "";
                    _ResXName = "";
					if (!_Initializing)
					{
                        if (!LoadCursorStreamFromFile(_CursorLocation))
                        {
                            ClearAll();
                        }
                        ReBuildCursor();
                    }
				}
			}
		}

		// ========================================================================
        /// <summary>
        /// Gets or sets whether cursor file location is local to the application at runtime [DESIGN ONLY]
        /// </summary>
        [Category("Cursor")]
		[Description("Determines whether the cursor file, specified by CursorLocation, will be in the application directory at runtime.")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[RefreshProperties(RefreshProperties.All)]
		[DefaultValue(false)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool CursorLocationLocal
		{
			get
			{
				return _CursorFileLocal;
			}
			set
			{
				_CursorFileLocal = value;
			}
        }

        // ========================================================================
        /// <summary>
        /// Gets or sets whether cursor handle is to be shared.
        /// </summary>
        [Category("Cursor")]
        [Description("Determines whether the cursor handle is shared at runtime - the handle is re-used by all ExtCursor components using the same cursor source.\r\rShared handles can only be used where the cursor is loaded from a file or project resource - not when the cursor is loaded from a local (form) resource.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [RefreshProperties(RefreshProperties.All)]
        [DefaultValue(false)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool SharedHandle
        {
            get
            {
                return _SharedHandle;
            }
            set
            {
                if (_Initializing || (_LoadFrom == _DesignLoadCursorFrom.ResxResource) || (_LoadFrom == _DesignLoadCursorFrom.File))
                {
                    _SharedHandle = value;
                }
            }
        }

        // ========================================================================
        /// <summary>
        /// Gets or sets the preferred size of the cursor to be loaded.
        /// </summary>
        [Category("Cursor Sizing")]
        [Description("The preferred size of the cursor.\r\rIf this property is set to (0,0) and the SizeDefault property is set to 'true' then default system metric size for cursors is used.  If this property is set to (0,0) and the SizeDefault property is set to 'false' then the actual size of the cursor is used (if the cursor contains multiple sizes then the first is used).")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [RefreshProperties(RefreshProperties.All)]
        [DefaultValue(typeof(Size), "0,0")]
        public Size SizePreferred
        {
            get
            {
                return _PreferredSize;
            }
            set
            {
                if ((value.Width != _PreferredSize.Width) || (value.Height != _PreferredSize.Height))
                {
                    _PreferredSize = new Size(value.Width, value.Height);
                    if (!_Initializing)
                    {
                        ReBuildCursor();
                    }
                }
            }
        }

        // ========================================================================
        /// <summary>
        /// Gets or sets a value indicating whether the default system metric cursor size should be used when loading.
        /// </summary>
        [Category("Cursor Sizing")]
        [Description("Determines whether to use default system metric size for the cursor.\r\rIf this property is 'true' and the SizePreferred is (0,0) the default system metric size for cursors is used.  If this property is 'false' and the SizePreferred is (0,0) then the actual size of the cursor is used (if the cursor contains multiple sizes then the first is used).")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        [RefreshProperties(RefreshProperties.All)]
        [DefaultValue(true)]
        public bool SizeDefault
        {
            get
            {
                return _DefaultSize;
            }
            set
            {
                if (value != _DefaultSize)
                {
                    _DefaultSize = value;
                    if (!_Initializing)
                    {
                        ReBuildCursor();
                    }
                }
            }
        }
        #endregion //"Public Properties - Design browsable"

        #region "Public Properties - Design browsable (informational, read-only)"
        // ========================================================================
        /// <summary>
        /// Gets the cursor hotspot.
        /// </summary>
        [Category("Information")]
        [Description("The HotSpot point within the cursor.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [RefreshProperties(RefreshProperties.All)]
        [DefaultValue(typeof(Point), "0,0")]
        [ParenthesizePropertyName(true)]
        [ReadOnly(true)]
        public Point HotSpot
        {
            get
            {
                if (_Cursor != null)
                {
                    return _Cursor.HotSpot;
                }
                else
                {
                    return new Point(0, 0);
                }
            }
        }

        // ========================================================================
        /// <summary>
        /// Gets the cursor size.
        /// </summary>
        [Category("Information")]
        [Description("The size of the cursor.")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [RefreshProperties(RefreshProperties.All)]
        [DefaultValue(typeof(Size), "0,0")]
        [ParenthesizePropertyName(true)]
        [ReadOnly(true)]
        public Size Size
        {
            get
            {
                if (_Cursor != null)
                {
                    return _Cursor.Size;
                }
                else
                {
                    return new Size(0, 0);
                }
            }
        }
        #endregion //"Public Properties - Design browsable (informational, read-only)"
        #endregion //"Public Properties"

        #region "Events"
        // ========================================================================
        /// <summary>
        /// Occurs when a handle is created for the cursor.
        /// </summary>
        [Category("Events")]
        [Description("Occurs when the cursor handle is created.")]
        public event EventHandler HandleCreated
        {
            add
            {
                Events.AddHandler(EventHandleCreated, value);
            }
            remove
            {
                Events.RemoveHandler(EventHandleCreated, value);
            }
        }

        // ========================================================================
        /// <summary>
        /// Occurs when the cursor handle is destroyed.
        /// </summary>
        [Category("Events")]
        [Description("Occurs when the cursor handle is destroyed.")]
        public event EventHandler HandleDestroyed
        {
            add
            {
                Events.AddHandler(EventHandleCreated, value);
            }
            remove
            {
                Events.RemoveHandler(EventHandleCreated, value);
            }
        }
        #endregion //"Events"

        #region "Disposal methods"
        // ========================================================================
		protected override void Dispose(bool disposing)
		{
#if DEBUG
            System.Diagnostics.Debug.WriteLine("ExtCursor.Dispose(" + disposing.ToString() + ")  [_Disposed = " + _Disposed.ToString() + "]");
#endif
            if (!_Disposed)
            {
                if (_SharedHandle)
                {
                    if (ExtCursorsSharedRepository.Exists)
                    {
                        ExtCursorsSharedRepository.Instance.ExtCursor_Disposed(this);
                    }
                }
                DropCursor(true);
                if (_CursorStream != null)
                {
                    _CursorStream.Dispose();
                }
                _Disposed = true;
            }
            base.Dispose(disposing);
        }
        #endregion //"Disposal methods"

        #region "ISupportInitialize Members"
        // ========================================================================
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void BeginInit()
		{
			_Initializing = true;
		}

		// ========================================================================
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void EndInit()
		{
            if (_Initializing)
            {
                _Initializing = false;
                ReBuildCursor();
            }
        }
        #endregion //"ISupportInitialize Members"
    }
    #endregion //"Class : ExtCursor"

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

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