问题
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