This will convert any image stored in your project to a cursor using an attached property. The image must be compiled as a resource!
Example
<Button MyLibrary:FrameworkElementExtensions.Cursor=""{MyLibrary:Uri MyAssembly, MyImageFolder/MyImage.png}""/>
FrameworkElementExtensions
using System;
using System.Windows;
using System.Windows.Media;
public static class FrameworkElementExtensions
{
#region Cursor
public static readonly DependencyProperty CursorProperty = DependencyProperty.RegisterAttached("Cursor", typeof(Uri), typeof(FrameworkElementExtensions), new UIPropertyMetadata(default(Uri), OnCursorChanged));
public static Uri GetCursor(FrameworkElement i) => (Uri)i.GetValue(CursorProperty);
public static void SetCursor(FrameworkElement i, Uri input) => i.SetValue(CursorProperty, input);
static void OnCursorChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (sender is FrameworkElement frameworkElement)
{
if (GetCursor(frameworkElement) != null)
frameworkElement.Cursor = new ImageSourceConverter().ConvertFromString(((Uri)e.NewValue).OriginalString).As<ImageSource>().Bitmap().Cursor(0, 0).Convert();
}
}
#endregion
}
ImageSourceExtensions
using System.Drawing;
using System.Windows.Media;
using System.Windows.Media.Imaging;
public static class ImageSourceExtensions
{
public static Bitmap Bitmap(this ImageSource input) => input.As<BitmapSource>().Bitmap();
}
BitmapSourceExtensions
using System.IO;
using System.Windows.Media.Imaging;
public static class BitmapSourceExtensions
{
public static System.Drawing.Bitmap Bitmap(this BitmapSource input)
{
if (input == null)
return null;
System.Drawing.Bitmap result;
using (var outStream = new MemoryStream())
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(input));
encoder.Save(outStream);
result = new System.Drawing.Bitmap(outStream);
}
return result;
}
}
BitmapExtensions
using System;
using System.Drawing;
using System.Runtime.InteropServices;
public static class BitmapExtensions
{
[StructLayout(LayoutKind.Sequential)]
public struct ICONINFO
{
/// <summary>
/// Specifies whether this structure defines an icon or a cursor. A value of TRUE specifies an icon; FALSE specifies a cursor.
/// </summary>
public bool fIcon;
/// <summary>
/// Specifies the x-coordinate of a cursor's hot spot. If this structure defines an icon, the hot spot is always in the center of the icon, and this member is ignored.
/// </summary>
public Int32 xHotspot;
/// <summary>
/// Specifies the y-coordinate of the cursor's hot spot. If this structure defines an icon, the hot spot is always in the center of the icon, and this member is ignored.
/// </summary>
public Int32 yHotspot;
/// <summary>
/// (HBITMAP) Specifies the icon bitmask bitmap. If this structure defines a black and white icon, this bitmask is formatted so that the upper half is the icon AND bitmask and the lower half is the icon XOR bitmask. Under this condition, the height should be an even multiple of two. If this structure defines a color icon, this mask only defines the AND bitmask of the icon.
/// </summary>
public IntPtr hbmMask;
/// <summary>
/// (HBITMAP) Handle to the icon color bitmap. This member can be optional if this structure defines a black and white icon. The AND bitmask of hbmMask is applied with the SRCAND flag to the destination; subsequently, the color bitmap is applied (using XOR) to the destination by using the SRCINVERT flag.
/// </summary>
public IntPtr hbmColor;
}
[DllImport("user32.dll")]
static extern IntPtr CreateIconIndirect([In] ref ICONINFO piconinfo);
[DllImport("user32.dll")]
static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool DestroyIcon(IntPtr hIcon);
public static System.Windows.Forms.Cursor Cursor(this Bitmap input, int hotX, int hotY)
{
ICONINFO Info = new ICONINFO();
IntPtr Handle = input.GetHicon();
GetIconInfo(Handle, out Info);
Info.xHotspot = hotX;
Info.yHotspot = hotY;
Info.fIcon = false;
IntPtr h = CreateIconIndirect(ref Info);
return new System.Windows.Forms.Cursor(h);
}
}
CursorExtensions
using Microsoft.Win32.SafeHandles;
public static class CursorExtensions
{
public static System.Windows.Input.Cursor Convert(this System.Windows.Forms.Cursor Cursor)
{
SafeFileHandle h = new SafeFileHandle(Cursor.Handle, false);
return System.Windows.Interop.CursorInteropHelper.Create(h);
}
}
As
public static Type As<Type>(this object input) => input is Type ? (Type)input : default;
Uri
using System;
using System.Windows.Markup;
public class Uri : MarkupExtension
{
public string Assembly { get; set; } = null;
public string RelativePath { get; set; }
public Uri(string relativePath) : base()
{
RelativePath = relativePath;
}
public Uri(string assembly, string relativePath) : this(relativePath)
{
Assembly = assembly;
}
static Uri Get(string assemblyName, string relativePath) => new Uri($"pack://application:,,,/{assemblyName};component/{relativePath}", UriKind.Absolute);
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (Assembly == null)
return new System.Uri(RelativePath, UriKind.Relative);
return Get(Assembly, RelativePath);
}
}
Table of Contents
- Introduction
- Change cursor basic
- Class
- Usage
- Drag/Drop operation
- Full source for cursor operations
- Cursor class
- Window xaml
- Window code behind
- Full source for cursor operations
- Summary
- Source code
- See also
Introduction
There are times when it’s beneficial to change the mouse cursor to alert users of long operations or simply when the current selection of mouse cursors are insufficient.
Change cursor basic
The following class provides two methods, one will change the mouse cursor to wait cursor for an indicated amount of milliseconds, in this case the default of 5000 which is five seconds while the second method the cursor type is passed in.
Class
Imports System.Threading
Public Class CursorHelper
''' <summary>
''' Change mouse cursor to wait
for
five seconds.
''' </summary>
Public Shared Sub ChangeToWait(Optional mlliSeconds As Integer = 5000)
Task.Factory.StartNew(
Sub()
Windows.Application.Current.Dispatcher.Invoke(Sub() Mouse.OverrideCursor = Cursors.Wait)
Try
Thread.Sleep(mlliSeconds)
Finally
Windows.Application.Current.Dispatcher.Invoke(Sub() Mouse.OverrideCursor = Nothing)
End Try
End Sub)
End Sub
Public Shared Sub ChangeTo(cursor As Cursor, Optional mlliSeconds As Integer = 5000)
Task.Factory.StartNew(
Sub()
Windows.Application.Current.Dispatcher.Invoke(Sub() Mouse.OverrideCursor = cursor)
Try
Thread.Sleep(mlliSeconds)
Finally
Windows.Application.Current.Dispatcher.Invoke(Sub() Mouse.OverrideCursor = Nothing)
End Try
End Sub)
End Sub
End Class
Usage
Class MainWindow
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
CursorHelper.ChangeTo(Cursors.No)
End Sub
End Class
Drag/Drop operation
To handle changing the mouse cursor during drag-n-Drop operations, in this case between two labels or between a label and dropping files in from file explorer a cursor is created in
GiveFeedback event of the destination label.
To create a cursor in this case, add a suitable bitmap as a resource to a project, in the above screen shot a document image is used which can be accessed via MyResources.Dynamic where Dynamic is the resource name.
The following function takes the bitmap from resources and creates a cursor.
''' <summary>
''' Use bitmap image from project resources.
''' </summary>
''' <returns></returns>
Public Shared Function CreateDropLabelCursorFromImage() As Cursor
Dim iconInfo As New NativeMethods.IconInfo()
'
' Here we read an image from project resources.
'
NativeMethods.GetIconInfo(My.Resources.Dynamic.GetHicon(), iconInfo)
iconInfo.xHotspot = 0
iconInfo.yHotspot = 0
iconInfo.fIcon = False
Dim cursorHandle As SafeIconHandle = NativeMethods.CreateIconIndirect(iconInfo)
Return CursorInteropHelper.Create(cursorHandle)
End Function
In the window which will present the new cursor, a private variable of type cursor is used, if when GiveFeedback event is triggered on a copy operation and it’s not create it’s created and if created already simply used.
Full source for cursor operations
Add the following class to a project
- In the method CreateDropLabelCursorFromImage change My.Resources.Dynamic to a bitmap resource in the current project.
- Optionally pass the resource into CreateDropLabelCursorFromImage.
Cursor class
Option Infer On
Imports System.Drawing
Imports System.Runtime.InteropServices
Imports System.Security.Permissions
Imports System.Windows.Interop
Imports Microsoft.Win32.SafeHandles
Public Class CursorHelper
Private NotInheritable Class NativeMethods
Public Structure IconInfo
Public fIcon As Boolean
Public xHotspot As Integer
Public yHotspot As Integer
Public hbmMask As IntPtr
Public hbmColor As IntPtr
End Structure
Private Sub New()
End Sub
<DllImport("user32.dll")>
Public Shared Function CreateIconIndirect(ByRef icon As IconInfo) As SafeIconHandle
End Function
<DllImport("user32.dll")>
Public Shared Function DestroyIcon(hIcon As IntPtr) As Boolean
End Function
<DllImport("user32.dll")>
Public Shared Function GetIconInfo(hIcon As IntPtr, ByRef pIconInfo As IconInfo) _
As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
End Class
<SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode:=True)>
Private Class SafeIconHandle
Inherits SafeHandleZeroOrMinusOneIsInvalid
Public Sub New()
MyBase.New(True)
End Sub
Protected Overrides Function ReleaseHandle() As Boolean
Return NativeMethods.DestroyIcon(handle)
End Function
End Class
Private Shared Function InternalCreateCursor(bmp As Bitmap) As Cursor
Dim iconInfo = New NativeMethods.IconInfo()
NativeMethods.GetIconInfo(bmp.GetHicon(), iconInfo)
iconInfo.xHotspot = 0
iconInfo.yHotspot = 0
iconInfo.fIcon = False
Dim cursorHandle As SafeIconHandle = NativeMethods.CreateIconIndirect(iconInfo)
Return CursorInteropHelper.Create(cursorHandle)
End Function
''' <summary>
''' Use bitmap image from project resources.
''' </summary>
''' <returns></returns>
Public Shared Function CreateDropLabelCursorFromImage() As Cursor
Dim iconInfo As New NativeMethods.IconInfo()
'
' Here we read an image from project resources.
'
NativeMethods.GetIconInfo(My.Resources.Dynamic.GetHicon(), iconInfo)
iconInfo.xHotspot = 0
iconInfo.yHotspot = 0
iconInfo.fIcon = False
Dim cursorHandle As SafeIconHandle = NativeMethods.CreateIconIndirect(iconInfo)
Return CursorInteropHelper.Create(cursorHandle)
End Function
End Class
Window xaml
<Window
x:Class="MainWindow"
xmlns:local="clr-namespace:ChangeCursorDragDrop"
Title="Drop-Drop"
Width="332"
Height="244.66"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<Grid>
<StackPanel
Width="216"
Margin="55,45"
HorizontalAlignment="Center"
Orientation="Vertical">
<Label
Margin="10"
Padding="15,10"
HorizontalContentAlignment="Center"
Background="AliceBlue"
Content="Data to drag"
GiveFeedback="Label_GiveFeedback"
MouseLeftButtonDown="Label_MouseLeftButtonDown"
/>
<Label
Margin="10"
Padding="15,10"
HorizontalContentAlignment="Center"
AllowDrop="True"
Background="Khaki"
BorderThickness="5,10,5,10"
Content="Drag to here"
Drop="Label_Drop"
/>
</StackPanel>
</Grid>
</Window>
Window code behind
- Line 10, start drag operation where CType(e.Source,Label).Content gets the current text of the label the operation starts on
- Line 28 checks if the drop operation can as a file(s) drop from Windows Explorer while line 38 handles text which if the user started the operation on the top label.
- Lines 33 and 34 lead to methods to show which file(s) were dropped onto the label.
- Line 46 handles the cursor change.
01.Imports
System.Collections.Specialized
02.Imports
ChangeCursorDragDrop.Classes
03.
04.Class
MainWindow
05. ''' <summary>
06. ''' Start drag
operation
07. ''' </summary>
08. ''' <param name="sender"></param>
09. ''' <param name="e"></param>
10. Private
Sub
Label_MouseLeftButtonDown(sender As
Object, e
As
MouseButtonEventArgs)
11.
12. '
13. ' CType(e.Source,
Label).Content has the text for this label
14. '
15. Dim
data As
New
DataObject(DataFormats.Text, CType(e.Source, Label).Content)
16.
17. DragDrop.DoDragDrop(CType(e.Source,
DependencyObject), data, DragDropEffects.Copy)
18.
19. End
Sub
20. ''' <summary>
21. ''' End drag
operation, determine if the drop has files or text
22. ''' </summary>
23. ''' <param name="sender"></param>
24. ''' <param name="e"></param>
25. Private
Sub
Label_Drop(sender As
Object, e As
DragEventArgs)
26.
27.
28. If
e.Data.GetDataPresent(DataFormats.FileDrop) Then
29.
30. Dim
dataObject = CType(e.Data, DataObject)
31.
32. Dim
fileNames As
StringCollection = dataObject.GetFileDropList()
33. Operations.IterateFiles(fileNames)
34. 'Operations.Inspect(fileNames)
35.
36. CType(e.Source,
Label).Content = $"Count: {fileNames.Count}"
37.
38. ElseIf
e.Data.GetDataPresent(DataFormats.Text) Then
39. CType(e.Source,
Label).Content = CStr(e.Data.GetData(DataFormats.Text))
40. End
If
41.
42. End
Sub
43.
44. Private
_customCursor As
Cursor = Nothing
45.
46. Private
Sub
Label_GiveFeedback(sender As
Object, e
As
GiveFeedbackEventArgs)
47.
48. If
e.Effects = DragDropEffects.Copy Then
49.
50. '
51. ' Create
cursor if not created yet (first time)
52. '
53. If
_customCursor Is
Nothing
Then
54.
55. _customCursor
= CursorHelper.CreateDropLabelCursorFromImage()
56.
57. End
If
58.
59. '
60. ' Set
cursor
61. '
62. If
_customCursor IsNot Nothing
Then
63. e.UseDefaultCursors
= False
64. Mouse.SetCursor(_customCursor)
65. End
If
66.
67. Else
68. e.UseDefaultCursors
= True
69. End
If
70.
71. e.Handled
= True
72.
73. End
Sub
74.
75.End
Class
Summary
Steps and code has been provided to show the very basics to change a mouse cursor for a WPF window. There are many possibilities were this code can be applied but keep in mind changing the cursor should not be done for any other reason then a business requirement.
Source code
- Basic code sample
- Drag-n-Drop sample
See also
- WPF ListBox data templating/style
- WPF: Tips — Bind to Current item of Collection
- WPF Data, Item and Control Templates — Minimum Code, Maximum Awesomeness
- Different ways to dynamically select DataTemplate for WPF ListView
- Moving from WinForms to WPF
- WPF Control Templates
C# WPF Tutorial — How To Use Custom Cursors [Intermediate]
So a while back, I did a tutorial on how to do custom cursors inWinForms. In that
post I said that at some point in the future, I would write a post on
how to do custom cursors in WPF — and here we are! A lot of the code
used today is based off of the code from that previous tutorial, so if
you haven’t read it, I would go do so before you continue.
Sadly, creating and using a custom cursor for WPF is actually more
difficult than for WinForms. This is pretty much all due in the end to a
single problem — WPF does not use GDI, but the cursor still does. Since
the cursor is something at the Windows level (it needs to exist across
all application and work smoothly), it is still GDI, and has all the
benefits and flaws that come with that fact. So we end up needing to
cross that boundary every time you want to do something special with the
cursor in WPF.
But don’t worry! Fortunately, WPF wraps all the standard cursors (so you
can still easily change to, say, a wait cursor), but as soon as you want
to create a special image to use as your cursor, you are on your own.
Well, not really on your own — that is what this tutorial is here for.
And so, in we go. First, we are going to look at the rather small change
to the actual «cursor creation part of the code. If you remember, to
create a cursor for WinForms, you can just say something like this:
IntPtr curPtr;
/* CurPtr gets set to
a pointer to an icon */
Cursor myCur = new Cursor(curPtr);
Sadly, you can’t quite do that in WPF. This is because it is a different
Cursor object we are creating, and Microsoft did not give us a
constructor that takes an IntPtr. For WinForms, we used a
System.Windows.Forms.Cursor, but for WPF, we will be creating a
System.Windows.Input.Cursor. But while there is no constructor for it,
there is still a way to get a Cursor out of an IntPtr. It just happens
to be hidden elsewhere:
IntPtr curPtr;
/* CurPtr gets set to
a pointer to an icon */
SafeFileHandle handle = new SafeFileHandle(ptr, true);
Cursor myCur = System.Windows.Interop.CursorInteropHelper.Create(handle);
So first we have to convert the IntPtr into a SafeHandle
(SafeFileHandle extends SafeHandle — you can’t just create a
SafeHandle, it is an abstract class). Then we pass that handle into
the nice and easy to find (thats sarcasm in case you can’t tell)
Create method under System.Windows.Interop.CursorInteropHelper, and
we get a Cursor back that we can use with WPF.
So if we bring in the rest of the code from the previous tutorial, we
might get a class that looks like this:
using System;
using System.Windows.Interop;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.Windows.Input;
namespace WPFCursorTest
{
public class CursorHelper
{
private struct IconInfo
{
public bool fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}
[DllImport("user32.dll")]
private static extern IntPtr CreateIconIndirect(ref IconInfo icon);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
public static Cursor CreateCursor(System.Drawing.Bitmap bmp, int xHotSpot,
int yHotSpot)
{
IconInfo tmp = new IconInfo();
GetIconInfo(bmp.GetHicon(), ref tmp);
tmp.xHotspot = xHotSpot;
tmp.yHotspot = yHotSpot;
tmp.fIcon = false;
IntPtr ptr = CreateIconIndirect(ref tmp);
SafeFileHandle handle = new SafeFileHandle(ptr, true);
return CursorInteropHelper.Create(handle);
}
}
}
One thing you will need to remeber if you use this is that we are
dealing with a System.Drawing.Bitmap here. This is the old GDI type
bitmap object — a concept foreign to the new WPF classes. So you will
probably need to pull in a reference to System.Drawing in your
references section in your Visual Studio project, since WPF projects do
not have this reference by default.
I tried, quite hard, to figure out a way to create a cursor in WPF
without having to lean on System.Drawing.Bitmap. But in the end, I
need to get an Icon pointer out of the bitmap (thats the function
GetHicon), and the WPF bitmap objects refuse to ever give you a
pointer.
But wait, not all hope is lost! I may have to deal with a
System.Drawing.Bitmap, but that does not mean that everyone will need
to. It is possible to wrap this method in another method that can take
in a WPF construct, instead of a GDI one:
public static Cursor CreateCursor(UIElement element, int xHotSpot, int yHotSpot)
{
element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
element.Arrange(new Rect(0, 0, element.DesiredSize.Width,
element.DesiredSize.Height));
RenderTargetBitmap rtb = new RenderTargetBitmap((int)element.DesiredSize.Width,
(int)element.DesiredSize.Height, 96, 96, PixelFormats.Pbgra32);
rtb.Render(element);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
MemoryStream ms = new MemoryStream();
encoder.Save(ms);
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(ms);
ms.Close();
ms.Dispose();
Cursor cur = InternalCreateCursor(bmp, xHotSpot, yHotSpot);
bmp.Dispose();
return cur;
}
Here, I am taking in a UIElement — a core WPF construct. I measure and
arrage it (to make sure that it is internally rendered properly), and
then I «take a picture of it». I do this by creating a
RenderTargetBitmap. This is a class for WPF thats lets you take a
UIElement and draw it to a bitmap — but remember, this is a WPF bitmap
(a System.Windows.Media.Imaging.RenderTargetBitmap, to be precise). It
is not the same thing as a System.Drawing.Bitmap. So I create my
RenderTargetBitmap to be the correct size, and I give it a dpi of 96
(pretty standard). Then, I render the UIElement on the bitmap.
Ok, now I have a RenderTargetBitmap in my hands, and I want to get to
a System.Drawing.Bitmap. Microsoft could not have possibly made this
more difficult, and I’m really quite annoyed by it. There are a couple
methods that let you go from a System.Drawing.Bitmap to a WPF Bitmap
(they create a System.Windows.Interop.InteropBitmap), but there are no
methods that go in the other direction. Or at least none that I could
find — and I scoured the docs and read through Bitmap code in Reflector
for quite a while. If anyone knows of such a method (or a better way
than the way I am about to describe), please let me know.
I convert from a RenderTargetBitmap to a System.Drawing.Bitmap by
first creating an encoder, and encoding my bitmap as a PNG (you could
pick any encoder you like). I then create a MemoryStream and save my
encoded bitmap to the stream. And now, since the System.Drawing.Bitmap
has a constructor that can take a stream, I can create my new bitmap.
Once that is done, I clean up the memory stream, and proceed to create
the cursor. And, of course, once the cursor is created, I dispose the
System.Drawing.Bitmap that I had created.
So in the end, the code for the whole class looks like this:
using System;
using System.Windows.Interop;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.IO;
using System.Windows;
namespace WPFCursorTest
{
public class CursorHelper
{
private struct IconInfo
{
public bool fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}
[DllImport("user32.dll")]
private static extern IntPtr CreateIconIndirect(ref IconInfo icon);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
private static Cursor InternalCreateCursor(System.Drawing.Bitmap bmp,
int xHotSpot, int yHotSpot)
{
IconInfo tmp = new IconInfo();
GetIconInfo(bmp.GetHicon(), ref tmp);
tmp.xHotspot = xHotSpot;
tmp.yHotspot = yHotSpot;
tmp.fIcon = false;
IntPtr ptr = CreateIconIndirect(ref tmp);
SafeFileHandle handle = new SafeFileHandle(ptr, true);
return CursorInteropHelper.Create(handle);
}
public static Cursor CreateCursor(UIElement element, int xHotSpot, int yHotSpot)
{
element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
element.Arrange(new Rect(0, 0, element.DesiredSize.Width,
element.DesiredSize.Height));
RenderTargetBitmap rtb = new RenderTargetBitmap((int)element.DesiredSize.Width,
(int)element.DesiredSize.Height, 96, 96, PixelFormats.Pbgra32);
rtb.Render(element);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(rtb));
MemoryStream ms = new MemoryStream();
encoder.Save(ms);
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(ms);
ms.Close();
ms.Dispose();
Cursor cur = InternalCreateCursor(bmp, xHotSpot, yHotSpot);
bmp.Dispose();
return cur;
}
}
}
Kind of ugly, don’t you think? But at least it is encapsulated.
Now you probably wondering how to use this class. Well, it is really
easy:
public partial class CursorTest : Window
{
public CursorTest()
{
InitializeComponent();
TextBlock tb = new TextBlock();
tb.Text = "{ } Switch On The Code";
tb.FontSize = 10;
tb.Foreground = Brushes.Green;
this.Cursor = CursorHelper.CreateCursor(tb, 5, 5);
}
}
Here, I have a window, and I want my cursor to say «{ } Switch On The
Code». So in the constructor, I make a TextBlock with that text, and
just call CreateCursor. And all I need to do is set the Cursor
property of my window to the result. That code would make something that
looks like this:
I hope this code helps anyone who has been trying to create and use
custom cursors in WPF. If you would like, you can download a Visual
Studio solution
here, which
contains both the CursorHelper class and the sample test window shown
above. And if anyone comes up with a way to get rid of the need for
having to bring in System.Drawing, or figures out how to convert from
a WPF and GDI bitmap easily, let us know!
Source Files:
- WPFCursorTest.zip
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 16×16 or 32×32), 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.





