I have a TextBox that I allow my users to rotate. But what I would LOVE for my users is to have their Cursor rotate to the same angle that the TextBox was rotated at. For example, if they rotated the TextBox to 28°, then when the Cursor enters that TextBox the Cursor should also rotate itself to 28°.
-
Maybe... Since the "Cursor" is itself an actual Control, it could be rotated the same way I am rotating my other Controls. Hmmm, I'll try this and repost.anon271334– anon2713342010-05-14 16:12:15 +00:00Commented May 14, 2010 at 16:12
-
When you say Cursor do you mean the Mouse Cursor ie. the I Beam or are you refering to the Caret which is displayed to indicate where the next character will be inserted/deleted etc. I only ask because I of ten hear the word cursor used to imply both. I assume you mean the mouse cursor because the blinking caret does rotate, but thought I would ask anyway.Chris Taylor– Chris Taylor2010-05-14 16:18:55 +00:00Commented May 14, 2010 at 16:18
-
I am referring to the | Beam, :)anon271334– anon2713342010-05-14 16:54:30 +00:00Commented May 14, 2010 at 16:54
3 Answers
You can rotate your cursor using the System.Drawing.Icon class from WinForms in combination with WPF's bitmap rotation ability.
The way to do this is to load the icon, convert it to a BitmapSource, use Image and RenderTargetBitmap to rotate it, convert it back to an Icon, save it, and finally update bytes 2, 10, and 11 that make it a .cur instead of a .ico.
Here's what the code looks like:
public Cursor GetRotatedCursor(byte[] curFileBytes, double rotationAngle)
{
// Load as Bitmap, convert to BitmapSource
var origStream = new MemoryStream(curFileBytes);
var origBitmap = new System.Drawing.Icon(origStream).ToBitmap();
var origSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(origBitmap.GetHBitmap());
// Construct rotated image
var image = new Image
{
BitmapSource = origSource,
RenderTransform = new RotateTransform(rotationAngle)
};
// Render rotated image to RenderTargetBitmap
var width = origBitmap.Width;
var height = origBitmap.Height;
var resultSource = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
resultSource.Render(image);
// Convert to System.Drawing.Bitmap
var pixels = new int[width*height];
resultSource.CopyPixels(pixels, width, 0);
var resultBitmap = new System.Drawing.Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppPargb);
for(int y=0; y<height; y++)
for(int x=0; x<width; x++)
resultBitmap.SetPixel(x, y, Color.FromArgb(pixels[y*width+x]));
// Save to .ico format
var resultStream = new MemoryStream();
new System.Drawing.Icon(resultBitmap.GetHIcon()).Save(resultStream);
// Convert saved file into .cur format
resultStream.Seek(2); resultStream.WriteByte(curFileBytes, 2, 1);
resultStream.Seek(10); resultStream.WriteByte(curFileBytes, 10, 2);
resultStream.Seek(0);
// Construct Cursor
return new Cursor(resultStream);
}
If you want to avoid the loop, you can replace it with a small bit of usafe code to call the System.Drawing.Bitmap constructor that takes initialization data:
fixed(int* bits = pixels)
{
resultBitmap = new System.Drawing.Bitmap(width, height, width, System.Drawing.Imaging.PixelFormat.Format32bppPargb, new IntPtr(bits));
}
You'll need to call this every time your TextBox rotation changes. This can be done either from the code that rotates your TextBox, or from a PropertyChangedCallback on a value that is bound to the TextBox's rotation.
1 Comment
mmm I'm not sure... but since the cursor is managed by Windows.. I guess you would need to hide the cursor when it enters the textbox and draw your own (which would be easy to rotate since you are rotating the other controls).
Heh, Googling for a way to do this, the first result was naturally from SO, you might wanna check the accepted answer (if you are using wpf):
@Ray Burns has provided the answer but, according to the comments, a couple of people are looking for a version that works in the latest versions of .Net. Here's the important code for .Net 9 (I'm presuming you can create a 32x32 32bpp PArgb pixel array of the image you want called pixels).
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows.Input;
namespace NobleTech.Products.PathEditor;
internal class CursorUtils
{
private const int cursorSize = 32;
public static Cursor CreateFromPixels(uint[] pixels)
{
// Convert to System.Drawing.Bitmap
Bitmap resultBitmap = new(cursorSize, cursorSize, PixelFormat.Format32bppPArgb);
for (int y = 0; y < cursorSize; y++)
{
for (int x = 0; x < cursorSize; x++)
resultBitmap.SetPixel(x, y, Color.FromArgb((int)pixels[y * cursorSize + x]));
}
// Save to .ico format
using MemoryStream resultStream = new();
Icon.FromHandle(resultBitmap.GetHicon()).Save(resultStream);
// Convert saved file into .cur format
resultStream.Seek(0, SeekOrigin.Begin);
resultStream.WriteByte(0);
resultStream.WriteByte(0);
resultStream.WriteByte(2);
resultStream.WriteByte(0);
// Set the hotspot
resultStream.Seek(10, SeekOrigin.Begin);
resultStream.WriteByte(cursorSize / 2);
resultStream.WriteByte(0);
resultStream.WriteByte(cursorSize / 2);
resultStream.WriteByte(0);
resultStream.Seek(0, SeekOrigin.Begin);
// Construct Cursor
return new Cursor(resultStream);
}
}
You'll need to reference the System.Drawing.Common package.