Question

I have a .cur file path ("%SystemRoot%\cursors\aero_arrow.cur") witch I want to display in an image control. So I need to convert Cursor to ImageSource. I tried both CursorConverter and ImageSourceConverter but had no luck. I also tried creating Graphics from the cursor and then converting it to Bitmap, But that didn't work either.

This thread says:

it is complicated to convert Cursor to Icon direcetly because Cursor doesn't expose the imagesource it use.

and

If you really want to bind an image to cursor, there is an approach you might want to try.

Since WindowForm is capable of drawing the cursor, we can use WindowForm to draw cursor on a bitmap. After that we could find a way to copy that bitmap to something WPF support.

Now the funny thing is that I can't create a new instance of System.Windows.Forms.Cursor with neither the file path nor the stream since It throws the following exception:

System.Runtime.InteropServices.COMException (0x800A01E1): 
Exception from HRESULT: 0x800A01E1 (CTL_E_INVALIDPICTURE)    
at System.Windows.Forms.UnsafeNativeMethods.IPersistStream.Load(IStream pstm)   
at System.Windows.Forms.Cursor.LoadPicture(IStream stream)

So can anybody tell me the best way to convert System.Windows.Input.Cursor to ImageSource?

And what about .ani cursors? If I remember correctly System.Windows.Input.Cursor does not support animated cursors, So how can I show them to the user? Converting them to gif then using the 3d party gif libraries?

Was it helpful?

Solution

I found the solution in this thread: How to Render a Transparent Cursor to Bitmap preserving alpha channel?

So here is the code:

[StructLayout(LayoutKind.Sequential)]    
private struct ICONINFO
{
    public bool fIcon;
    public int xHotspot;
    public int yHotspot;
    public IntPtr hbmMask;
    public IntPtr hbmColor;
}

[DllImport("user32")]
private static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO pIconInfo);

[DllImport("user32.dll")]
private static extern IntPtr LoadCursorFromFile(string lpFileName);

[DllImport("gdi32.dll", SetLastError = true)]
private static extern bool DeleteObject(IntPtr hObject);

private Bitmap BitmapFromCursor(Cursor cur)
{
    ICONINFO ii;
    GetIconInfo(cur.Handle, out ii);

    Bitmap bmp = Bitmap.FromHbitmap(ii.hbmColor);
    DeleteObject(ii.hbmColor);
    DeleteObject(ii.hbmMask);

    BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
    Bitmap dstBitmap = new Bitmap(bmData.Width, bmData.Height, bmData.Stride, PixelFormat.Format32bppArgb, bmData.Scan0);
    bmp.UnlockBits(bmData);

    return new Bitmap(dstBitmap);
}

private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
    //Using LoadCursorFromFile from user32.dll, get a handle to the icon
    IntPtr hCursor = LoadCursorFromFile("C:\\Windows\\Cursors\\Windows Aero\\aero_busy.ani");

    //Create a Cursor object from that handle
    Cursor cursor = new Cursor(hCursor);

    //Convert that cursor into a bitmap
    using (Bitmap cursorBitmap = BitmapFromCursor(cursor))
    {
        //Draw that cursor bitmap directly to the form canvas
        e.Graphics.DrawImage(cursorBitmap, 50, 50);
    }
}

It's written for Win Forms and draws an image. But can be used in wpf as well with referencing to System.Windows.Forms. and then you can convert that bitmap to bitmap source and show it in an image control...

The reason I'm using System.Windows.Forms.Cursor instead of System.Windows.Input.Cursor is that I can't get to create a new instance of cursor using the IntPtr handle...

Edit: The above method does NOT work with cursors having low color bits. An alternative is to use Icon.ExtractAssociatedIcon instead:

System.Drawing.Icon i = System.Drawing.Icon.ExtractAssociatedIcon(@"C:\Windows\Cursors\arrow_rl.cur");
System.Drawing.Bitmap b = i.ToBitmap();

Hope that helps someone ...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top