#713 – Setting the Cursor to an Image of an UIElement While Dragging
December 17, 2012 6 Comments
You can use the GiveFeedback to change the cursor during a drag-and-drop operation. You can go a bit further and set the cursor to an image that represents the user interface element that you are dragging by rendering the UIElement to a bitmap and then converting that bitmap to a cursor.
This example is based on code written by Brandon Cannaday, at http://www.switchonthecode.com/tutorials/wpf-tutorial-how-to-use-custom-cursors . To start with, here is Brandon’s code, as modified by reader “Swythan”:
public class CursorHelper { private static class NativeMethods { public struct IconInfo { public bool fIcon; public int xHotspot; public int yHotspot; public IntPtr hbmMask; public IntPtr hbmColor; } [DllImport("user32.dll")] public static extern SafeIconHandle CreateIconIndirect(ref IconInfo icon); [DllImport("user32.dll")] public static extern bool DestroyIcon(IntPtr hIcon); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo); } [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] private class SafeIconHandle : SafeHandleZeroOrMinusOneIsInvalid { public SafeIconHandle() : base(true) { } override protected bool ReleaseHandle() { return NativeMethods.DestroyIcon(handle); } } private static Cursor InternalCreateCursor(System.Drawing.Bitmap bmp) { var iconInfo = new NativeMethods.IconInfo(); NativeMethods.GetIconInfo(bmp.GetHicon(), ref iconInfo); iconInfo.xHotspot = 0; iconInfo.yHotspot = 0; iconInfo.fIcon = false; SafeIconHandle cursorHandle = NativeMethods.CreateIconIndirect(ref iconInfo); return CursorInteropHelper.Create(cursorHandle); } public static Cursor CreateCursor(UIElement element) { element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); element.Arrange(new Rect(new Point(), element.DesiredSize)); RenderTargetBitmap rtb = new RenderTargetBitmap( (int)element.DesiredSize.Width, (int)element.DesiredSize.Height, 96, 96, PixelFormats.Pbgra32); rtb.Render(element); var encoder = new PngBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(rtb)); using (var ms = new MemoryStream()) { encoder.Save(ms); using (var bmp = new System.Drawing.Bitmap(ms)) { return InternalCreateCursor(bmp); } } } }
Now that we have the helper class, we can use it to set the cursor to the image of a Label that we are dragging. Here’s the XAML defining a label to drag from and one to drag to.
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" Margin="45"> <Label Content="Data to drag" Background="AliceBlue" Padding="15,10" MouseLeftButtonDown="Label_MouseLeftButtonDown" GiveFeedback="Label_GiveFeedback"/> <Label Content="Drag to here" Background="MediumSpringGreen" Padding="15,10" Margin="20" AllowDrop="True" Drop="Label_Drop"/> </StackPanel>
Here’s the relevant drag-and-drop related code:
private void Label_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { DataObject data = new DataObject(DataFormats.Text, ((Label)e.Source).Content); DragDrop.DoDragDrop((DependencyObject)e.Source, data, DragDropEffects.Copy); } private void Label_Drop(object sender, DragEventArgs e) { ((Label)e.Source).Content = (string)e.Data.GetData(DataFormats.Text); } private Cursor customCursor = null; private void Label_GiveFeedback(object sender, GiveFeedbackEventArgs e) { if (e.Effects == DragDropEffects.Copy) { if (customCursor == null) customCursor = CursorHelper.CreateCursor(e.Source as UIElement); if (customCursor != null) { e.UseDefaultCursors = false; Mouse.SetCursor(customCursor); } } else e.UseDefaultCursors = true; e.Handled = true; }