#716 – Using a Border As a Visual Indication That a Control Can Be Dragged

You can use the Thumb control to allow dragging a control around on a parent Canvas.  To make it a little more obvious to the user that the control can be dragged, you can set up a Border around the control that only shows up when the user moves the mouse over the control.

    <Window.Resources>
        <Style x:Key="BorderVisibleOnMouse" TargetType="Border">
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="BorderBrush" Value="Transparent"/>
            <Style.Triggers>
                <Trigger Property="Border.IsMouseOver" Value="True">
                    <Setter Property="BorderBrush" Value="LightBlue"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>

    <Canvas>
        <Thumb Canvas.Left="100" Canvas.Top="60" DragDelta="Thumb_DragDelta">
            <Thumb.Template>
                <ControlTemplate>
                    <Border Style="{StaticResource ResourceKey=BorderVisibleOnMouse}">
                        <Label Content="Elvis Drafted!"/>
                    </Border>
                </ControlTemplate>
            </Thumb.Template>
        </Thumb>
        <Thumb Canvas.Left="30" Canvas.Top="180" DragDelta="Thumb_DragDelta">
            <Thumb.Template>
                <ControlTemplate>
                    <Border Style="{StaticResource ResourceKey=BorderVisibleOnMouse}">
                        <Label Content="Richard the Lion-Hearted Captured!"/>
                    </Border>
                </ControlTemplate>
            </Thumb.Template>
        </Thumb>
    </Canvas>

716-001
716-002
716-003

#715 – Using the Thumb Control to Drag Objects on a Canvas

You can use the Thumb control for simple dragging of objects on a Canvas.  You set the Template of the Thumb control to contain the actual element to be dragged and then handle the thumb’s DragDelta event.

Below is a Canvas containing three controls, each wrapped in a Thumb and therefore draggable.

    <Canvas>
        <Thumb Canvas.Left="10" Canvas.Top="20" Canvas.ZIndex="99"  DragDelta="Thumb_DragDelta">
            <Thumb.Template>
                <ControlTemplate>
                    <Image Width="60" Height="60" Source="Crown.jpg"/>
                </ControlTemplate>
            </Thumb.Template>
        </Thumb>
        <Thumb Canvas.Left="100" Canvas.Top="60" DragDelta="Thumb_DragDelta">
            <Thumb.Template>
                <ControlTemplate>
                    <Image Width="80" Height="100" Source="HenryII.jpg"/>
                </ControlTemplate>
            </Thumb.Template>
        </Thumb>
        <Thumb Canvas.Left="30" Canvas.Top="180" DragDelta="Thumb_DragDelta">
            <Thumb.Template>
                <ControlTemplate>
                    <Label Content="Westminster, 19-Dec-1154"/>
                </ControlTemplate>
            </Thumb.Template>
        </Thumb>
    </Canvas>

In the event handler for each of the Thumb controls, we simply adjust the thumb’s position on the Canvas.

        private void Thumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
        {
            UIElement thumb = e.Source as UIElement;

            Canvas.SetLeft(thumb, Canvas.GetLeft(thumb) + e.HorizontalChange);
            Canvas.SetTop(thumb, Canvas.GetTop(thumb) + e.VerticalChange);
        }

715-001
715-002

#714 – Setting the Cursor to Render Some Text While Dragging

You can use the GiveFeedback to change the cursor during a drag-and-drop operation.  You can set the cursor to be some text by rendering a visual element to a bitmap and then converting that bitmap to a cursor.

Here’s a helper class containing a method that converts some text to a cursor.

    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(string cursorText)
        {
            // Text to render
            FormattedText fmtText = new FormattedText(cursorText,
                    new CultureInfo("en-us"),
                    FlowDirection.LeftToRight,
                    new Typeface(new FontFamily("Arial"), FontStyles.Normal, FontWeights.Normal, new FontStretch()),
                    12.0,  // FontSize
                    Brushes.Black);

            // The Visual to use as the source of the RenderTargetBitmap.
            DrawingVisual drawingVisual = new DrawingVisual();
            DrawingContext drawingContext = drawingVisual.RenderOpen();
            drawingContext.DrawText(fmtText, new Point());
            drawingContext.Close();

            // The BitmapSource that is rendered with a Visual.
            RenderTargetBitmap rtb = new RenderTargetBitmap(
                (int)drawingVisual.ContentBounds.Width,
                (int)drawingVisual.ContentBounds.Height,
                96,   // dpiX
                96,   // dpiY
                PixelFormats.Pbgra32);
            rtb.Render(drawingVisual);

            // Encoding the RenderBitmapTarget into a bitmap (as PNG)
            PngBitmapEncoder 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);
                }
            }
        }
    }

We can test this code by setting the cursor to some text when we drag a Label.

    <StackPanel>
        <Label Content="Mayflower" Background="AliceBlue" 
               HorizontalAlignment="Center" Margin="15" Padding="20,5"
               MouseLeftButtonDown="Label_MouseLeftButtonDown"
               GiveFeedback="Label_GiveFeedback"/>
        <Label Content="Drag to here" Background="SpringGreen" 
               HorizontalAlignment="Center" Margin="15"
               Drop="Label_Drop"/>
    </StackPanel>

Here’s the code for the drag-and-drop operation.

        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("Mayflower - 18 Dec 1620");

                if (customCursor != null)
                {
                    e.UseDefaultCursors = false;
                    Mouse.SetCursor(customCursor);
                }
            }
            else
                e.UseDefaultCursors = true;

            e.Handled = true;
        }

714-001

#713 – Setting the Cursor to an Image of an UIElement While Dragging

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;
        }

#712 – Showing a Custom Mouse Cursor While Dragging

You can use a non-default cursor while a drag-and-drop operation is in process by handling the GiveFeedback event.

In the example below, we load a custom cursor from a .cur file located in the same directory as the executing application.

        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 = new Cursor(new FileStream("Earth.cur", FileMode.Open));

                e.UseDefaultCursors = false;
                Mouse.SetCursor(customCursor);
            }
            else
                e.UseDefaultCursors = true;

            e.Handled = true;
        }

712-001

#711 – Changing the Mouse Cursor While Dragging

You can change the mouse cursor during a drag-and-drop operation by handling the GiveFeedback event for the drag source.  The GiveFeedbackEventArgs.Effects property will indicate the current effect, depending on the mouse position.  You can set the mouse cursor based on the effect.

In the example below, we change the mouse to a “hand” whenever the effect is Copy, indicating that we’re allowed to drop the data.

    <StackPanel Orientation="Vertical" HorizontalAlignment="Center" Margin="45">
        <Label Content="Data to drag" Background="AliceBlue" Padding="15,10" Margin="10"
               MouseLeftButtonDown="Label_MouseLeftButtonDown"
               GiveFeedback="Label_GiveFeedback"/>
        <Label Content="Drag to here" Background="MediumSpringGreen" Padding="15,10" Margin="10"
               AllowDrop="True" Drop="Label_Drop"/>
    </StackPanel>
        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 void Label_GiveFeedback(object sender, GiveFeedbackEventArgs e)
        {
            if (e.Effects == DragDropEffects.Copy)
            {
                e.UseDefaultCursors = false;
                Mouse.SetCursor(Cursors.Hand);
            }
            else
                e.UseDefaultCursors = true;

            e.Handled = true;
        }

711-001
711-002

#710 – DoDragDrop Is a Blocking Call

When you initiate a drag-and-drop operation by calling DragDrop.DoDragDrop, control will not return from the DoDragDrop method until the drag-and-drop operation has completed.

        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);

            // Not called until drag-and-drop is done
            ((Label)e.Source).Content = "DragDrop done";
        }

        private void Label_Drop(object sender, DragEventArgs e)
        {
            ((Label)e.Source).Content = (string)e.Data.GetData(DataFormats.Text);
        }

710-001
710-002