Custom cursor in WPF?
Like Peter mentioned, if you already have a .cur file, you can use it as an embedded resource by creating a dummy element in the resource section, and then referencing the dummy's cursor when you need it.
For example, say you wanted to display non-standard cursors depending on the selected tool.
Add to resources:
<Window.Resources>
<ResourceDictionary>
<TextBlock x:Key="CursorGrab" Cursor="Resources/Cursors/grab.cur"/>
<TextBlock x:Key="CursorMagnify" Cursor="Resources/Cursors/magnify.cur"/>
</ResourceDictionary>
</Window.Resources>
Example of embedded cursor referenced in code:
if (selectedTool == "Hand")
myCanvas.Cursor = ((TextBlock)this.Resources["CursorGrab"]).Cursor;
else if (selectedTool == "Magnify")
myCanvas.Cursor = ((TextBlock)this.Resources["CursorMagnify"]).Cursor;
else
myCanvas.Cursor = Cursor.Arrow;
There is an easier way than managing the cursor display yourself or using Visual Studio to construct lots of custom cursors.
If you have a FrameworkElement you can construct a Cursor from it using the following code:
public Cursor ConvertToCursor(FrameworkElement visual, Point hotSpot)
{
int width = (int)visual.Width;
int height = (int)visual.Height;
// Render to a bitmap
var bitmapSource = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
bitmapSource.Render(visual);
// Convert to System.Drawing.Bitmap
var pixels = new int[width*height];
bitmapSource.CopyPixels(pixels, width, 0);
var bitmap = 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++)
bitmap.SetPixel(x, y, Color.FromArgb(pixels[y*width+x]));
// Save to .ico format
var stream = new MemoryStream();
System.Drawing.Icon.FromHandle(resultBitmap.GetHicon()).Save(stream);
// Convert saved file into .cur format
stream.Seek(2, SeekOrigin.Begin);
stream.WriteByte(2);
stream.Seek(10, SeekOrigin.Begin);
stream.WriteByte((byte)(int)(hotSpot.X * width));
stream.WriteByte((byte)(int)(hotSpot.Y * height));
stream.Seek(0, SeekOrigin.Begin);
// Construct Cursor
return new Cursor(stream);
}
Note that your FrameworkElement 's size must be a standard cursor size (eg 16x16 or 32x32), for example:
<Grid x:Name="customCursor" Width="32" Height="32">
...
</Grid>
It would be used like this:
someControl.Cursor = ConvertToCursor(customCursor, new Point(0.5, 0.5));
Obviously your FrameworkElement could be an <Image>
control if you have an existing image, or you can draw anything you like using WPF's built-in drawing tools.
Note that details on the .cur file format can be found at ICO (file format).
You have two basic options:
- When the mouse cursor is over your control, hide the system cursor by setting
this.Cursor = Cursors.None;
and draw your own cursor using whatever technique you like. Then, update the position and appearance of your cursor by responding to mouse events. Here are two examples:
http://www.xamlog.com/2006/07/17/creating-a-custom-cursor/
http://www.hanselman.com/blog/DeveloperDesigner.aspx
Additional examples can be found here:WPF Tutorial - How To Use Custom Cursors
Setting the Cursor to Render Some Text While Dragging
Getting fancy and using the Visual we are dragging for feedback [instead of a cursor]
How can I drag and drop items between data bound ItemsControls?
- Create a new Cursor object by loading an image from a .cur or .ani file. You can create and edit these kinds of files in Visual Studio. There are also some free utilites floating around for dealing with them. Basically they're images (or animated images) which specify a "hot spot" indicating what point in the image the cursor is positioned at.
If you choose to load from a file, note that you need an absolute file-system path to use the Cursor(string fileName)
constructor. Lamely, a relative path or Pack URI will not work. If you need to load the cursor from a relative path or from a resource packed with your assembly, you will need to get a stream from the file and pass it in to the Cursor(Stream cursorStream)
constructor. Annoying but true.
On the other hand, specifying a cursor as a relative path when loading it using a XAML attribute does work, a fact you could use to get your cursor loaded onto a hidden control and then copy the reference to use on another control. I haven't tried it, but it should work.