How do I create a dynamic mouse cursor .NET without using interop?

强颜欢笑 提交于 2019-12-31 01:45:59

问题


I have an application which I'm interested in eventually porting to mono so I'm trying to avoid using p/invoke's to accomplish this task.

I would like to load a cursor dynamically, as in I have a Bitmap that is generated on the fly in the application. From what I can tell the safest way to do it without using p/invoke's is to create a .cur file which I can then load to a memory stream and use the Cursor(Stream) constructor. However I have no idea how to create a .cur file.

I found this article on the Microsoft Knowledge Base which sort of explains the format, but I'm not sure how it can be used without the interop calls. How To Create an Alpha Blended Cursor or Icon in Windows XP

Does anyone else have a managed solution I can use to accomplish this task?


回答1:


Here's an example in VB.NET of creating a bitmap on the fly and turning it into a cursor. Not sure who this will help 7+ years on, but here goes:

Private Function GetCircleCursor(iDiameter As Integer) As Cursor
Dim oBitmap As Bitmap = New Bitmap(Convert.ToInt32(iDiameter), Convert.ToInt32(iDiameter), System.Drawing.Imaging.PixelFormat.Format32bppArgb)

    Using g As System.Drawing.Graphics = Graphics.FromImage(oBitmap)
        g.Clear(Color.Transparent)

        g.DrawEllipse(New System.Drawing.Pen(Color.White, 3), New Rectangle(0, 0, iDiameter, iDiameter))
        g.DrawEllipse(New System.Drawing.Pen(Color.Black, 1), New Rectangle(0, 0, iDiameter, iDiameter))
    End Using

    Return New Cursor(oBitmap.GetHicon)
End Function



回答2:


I was reference the post: How can i replace cursor with bitmap in winform

You can create a Array of static cursor and use Timer to change it

to make dynamic mouse cursor effect!

Create static cursor from bitmap is so simply and without using interop:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        Icon icon = this.Icon;
        Bitmap bmp = icon.ToBitmap();
        Cursor cur = new Cursor(bmp.GetHicon());

        this.Cursor = cur;
    }
}



回答3:


The code below creates a stream filled with data in .cur format, which I succesfully tested in Ubuntu with Mono. However .NET without using Win32 API only supports black and white colors in custom cursor:

The Cursor class does not support animated cursors (.ani files) or cursors with colors other than black and white.

Therefore in Unix with Mono one is rewarded with a cursor which

  • displays black and white colors only
  • has no semi-transparent pixels, only fully opaque / fully transparent ones

¯\_(ツ)_/¯

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace StackoverflowExample
{
    public static class CursorHelper
    {
        public static Cursor CreateCursor(Bitmap bmp, Point hotSpot)
        {
            // https://en.wikipedia.org/wiki/ICO_(file_format)
            var bmpData = bmp.LockBits(new Rectangle(default, bmp.Size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

            try
            {
                int numBytes = bmpData.Stride * bmpData.Height;
                var bgraValues = new byte[numBytes];
                Marshal.Copy(bmpData.Scan0, bgraValues, 0, numBytes);

                int max = Math.Max(bmp.Width, bmp.Height);

                if (max > 256)
                    throw new NotSupportedException();

                byte iconSizeByte = _sizes.FirstOrDefault(s => s >= max); // 0 means 256
                int iconSizeI = iconSizeByte == 0 ? 256 : iconSizeByte;
                const int bytesPerPixel = 4;
                const int bytesPerPixelSource = 4;
                byte[] emptyPixel = new byte[bytesPerPixel];

                using (var stream = new MemoryStream())
                using (var writer = new BinaryWriter(stream))
                {
                    writer.Write((ushort)0); // idReserved
                    writer.Write((ushort)2); // idType, 1 = .ico 2 = .cur
                    writer.Write((ushort)1); // idCount

                    writer.Write(iconSizeByte);
                    writer.Write(iconSizeByte);
                    writer.Write((byte)0); // colorCount
                    writer.Write((byte)0); // reserved
                    writer.Write((ushort)hotSpot.X);
                    writer.Write((ushort)hotSpot.Y);

                    var pixelsCount = iconSizeI * iconSizeI;
                    var xorLength = pixelsCount * bytesPerPixel;
                    var andLength = pixelsCount / 8 * 2;

                    writer.Write((uint)(40 + xorLength + andLength)); // sizeInBytes
                    writer.Write((uint)stream.Position + sizeof(uint)); // fileOffset = 22 = 0x16

                    writer.Write(40u); // cursorInfoHeader.biSize
                    writer.Write((int)iconSizeI); // cursorInfoHeader.biWidth
                    writer.Write((int)iconSizeI * 2); // cursorInfoHeader.biHeight
                    writer.Write((ushort)1); // cursorInfoHeader.biPlanes
                    writer.Write((ushort)(8 * bytesPerPixel)); // cursorInfoHeader.biBitCount
                    writer.Write(0u); // cursorInfoHeader.biCompression
                    writer.Write(0u); // cursorInfoHeader.biSizeImage
                    writer.Write(0); // cursorInfoHeader.biXPelsPerMeter;
                    writer.Write(0); // cursorInfoHeader.biYPelsPerMeter;
                    writer.Write(0u); // cursorInfoHeader.biClrUsed = binaryReader2.ReadUInt32();
                    writer.Write(0u); // cursorInfoHeader.biClrImportant = binaryReader2.ReadUInt32();

                    using (var andMask = new MemoryStream(andLength))
                    {
                        byte def = 255;

                        for (int j = 0; j < iconSizeI; j++)
                        {
                            int y = iconSizeI - 1 - j;
                            byte curByte = def;

                            for (int i = 0; i < iconSizeI; i++)
                            {
                                var bitIndex = 7 - i % 8;

                                if (i < bmp.Width && y < bmp.Height)
                                {
                                    var p = y * bmpData.Stride + i * bytesPerPixelSource;
                                    stream.Write(bgraValues, p, bytesPerPixel);

                                    if (bgraValues[p + 3] > 0)
                                        curByte = (byte)(curByte & ~(1 << bitIndex));
                                }
                                else
                                    stream.Write(emptyPixel, 0, emptyPixel.Length);

                                if (bitIndex == 0)
                                {
                                    andMask.WriteByte(curByte);
                                    curByte = def;
                                }
                            }
                        }

                        for (int j = 0; j < iconSizeI; j++)
                            for (int b = 0; b < iconSizeI / 8; b++)
                                andMask.WriteByte(def);

                        andMask.Seek(0, SeekOrigin.Begin);
                        andMask.CopyTo(stream);
                    }

                    stream.Seek(0, SeekOrigin.Begin);

                    // debug
                    // File.WriteAllBytes("/home/kolia/Documents/stream", stream.ToArray());
                    // stream.Seek(0, SeekOrigin.Begin);

                    var cursor = new Cursor(stream);
                    return cursor;
                }
            }
            finally
            {
                bmp.UnlockBits(bmpData);
            }
        }

        private static readonly byte[] _sizes = { 16, 32, 64, 128 };
    }
}



回答4:


Simple: YOU CAN NOT - the functionaltiy you ask for is not part of the .NET framework, so you need to go native.

If you application needds porting to mono, isloate this code in one class so you can turn if off like with a compiler switch - not hard.



来源:https://stackoverflow.com/questions/7610889/how-do-i-create-a-dynamic-mouse-cursor-net-without-using-interop

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!