#730 – Use QueryContinueDrag Event to Know When Mouse Button State Changes

The QueryContinueDrag event lets you know when a mouse button changes during a drag-drop operation.  It will also indicate whether the state of the Shift, Ctrl, or Alt keys change while dragging.  You wire up an event handler to the control that the drag-drop operation originates from.

In the example below, the source control waits for the left mouse button to be released and then cleans out its content.

        <Label Content="Drag from here" Background="LavenderBlush"
               HorizontalAlignment="Center" Margin="10" Padding="10"
               MouseDown="Label1_MouseDown"
               QueryContinueDrag="Label1_QueryContinueDrag"/>
        <Label Content="To here" Background="SandyBrown" AllowDrop="True"
               HorizontalAlignment="Center" Margin="10" Padding="10"
               Drop="Label2_Drop"/>
        private void Label1_MouseDown(object sender, MouseButtonEventArgs e)
        {
            Label lblFrom = e.Source as Label;

            if (e.LeftButton == MouseButtonState.Pressed)
                DragDrop.DoDragDrop(lblFrom, lblFrom.Content, DragDropEffects.Copy);
        }

        private void Label1_QueryContinueDrag(object sender, QueryContinueDragEventArgs e)
        {
            Label lblFrom = e.Source as Label;

            if (!e.KeyStates.HasFlag(DragDropKeyStates.LeftMouseButton))
                lblFrom.Content = "...";
        }

        private void Label2_Drop(object sender, DragEventArgs e)
        {
            string draggedText = (string)e.Data.GetData(DataFormats.StringFormat);

            Label toLabel = e.Source as Label;
            toLabel.Content = draggedText;
        }

730-001
730-002

Advertisement

#729 – Mouse.GetPosition Doesn’t Work While Dragging

If you are handling the DragOver event during a drag-and-drop operation and you want to find the current mouse position, you need to use DragEventArgs.GetPosition, rather than the static Mouse.GetPosition method.

In the example below, we initiate a drag-and-drop operation in a window and then try reporting the mouse’s position in the window’s DragOver handler.  We try using both methods to get the mouse position, but only the DragEventArgs.GetPosition method works.

<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Application 2" Height="350" Width="325"
        MouseDown="Window_MouseDown"
        AllowDrop="True" DragOver="Window_DragOver">
    <StackPanel>
        <Label Name="lblInfo1" Content="Info 1"/>
        <Label Name="lblInfo2" Content="Info 2"/>
    </StackPanel>
</Window>

 

        private void Window_MouseDown(object sender, MouseButtonEventArgs e)
        {
            DragDrop.DoDragDrop((DependencyObject)e.Source, "Sample", DragDropEffects.Copy);
        }

        private void Window_DragOver(object sender, DragEventArgs e)
        {
            System.Windows.Point p1 = Mouse.GetPosition(this);
            lblInfo1.Content = string.Format("Mouse.GetPosition: {0}, {1}", p1.X, p1.Y);

            System.Windows.Point p2 = e.GetPosition(this);
            lblInfo2.Content = string.Format("DragEventArgs.GetPosition: {0}, {1}", p2.X, p2.Y);
        }

729-001

#717 – Drag-and-Drop with Touch on Windows 7

You can implement drag-and-drop on a touch-based system in a similar way to how you implement drag-and-drop using the mouse.

You start by defining a handler for the TouchDown event of the control that serves as a drag source.  You then define a handler for the Drop event of the control that is the drop target.

    <StackPanel>
        <Label Content="Benjamin Disraeli"
               Background="AliceBlue" Margin="15" Padding="30,20" HorizontalAlignment="Center"
               TouchDown="Label_TouchDown"/>
        <Label Content="Drag to here"
               Background="Bisque" Margin="15" Padding="30,20" HorizontalAlignment="Center"
               AllowDrop="True" Drop="Label_Drop"/>
    </StackPanel>

In the code-behind, you call the DoDragDrop method to initiate drag-and-drop.

        // Drag source
        private void Label_TouchDown(object sender, TouchEventArgs e)
        {
            Label l = e.Source as Label;
            DragDrop.DoDragDrop(l, l.Content + " was Dragged!", DragDropEffects.Copy);
        }

        // Drag target
        private void Label_Drop(object sender, DragEventArgs e)
        {
            string draggedText = (string)e.Data.GetData(DataFormats.StringFormat);
            Label l = e.Source as Label;
            l.Content = draggedText;
        }

You can now touch and hold your finger down to drag.

717-001

717-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

#708 – Dragging a File Into a WPF Application

You can use drag-and-drop in WPF to allow a file to be dragged into your application, using the DataFormats.FileDrop format.

You first specify that you only support the FileDrop format.

        private void Window_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
                e.Effects = DragDropEffects.Copy;
            else
                e.Effects = DragDropEffects.None;

            e.Handled = true;
        }

        private void Window_DragOver(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
                e.Effects = DragDropEffects.Copy;
            else
                e.Effects = DragDropEffects.None;

            e.Handled = true;
        }

When you call GetData in your Drop event handler, you get back a list of filenames being dropped.  Below is an example where we set a Label to show the filename and then set the contents of a TextBlock to show the text from the file.

        private void Window_Drop(object sender, DragEventArgs e)
        {
            string[] filenames = (string[])e.Data.GetData(DataFormats.FileDrop);

            lblFilename.Content = filenames[0];

            txtContent.Text = File.ReadAllText(filenames[0]);
        }

708-001

 

708-002

#707 – Dragging a List of Items from a WPF Application into Excel

You can use drag-and-drop in WPF to drag a comma-separated list of values into Excel.  When you drop a CSV list into Excel, it will automatically put each item into a different cell.

In the example below, we have a list of items in a ListBox.

707-001

We can drag the list of items by handling the MouseLeftButtonDown event for the blue label.  We create a string containing a comma-separated list of the items.  We then create a DataObject with a format of CommaSeparatedValue.

        private void Label_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            List<string> stringItems = new List<string>();

            foreach (ListBoxItem lbi in lbEncyclopedias.Items)
                stringItems.Add((string)lbi.Content);

            string someValues = string.Join(",", stringItems);

            DataObject data = new DataObject(DataFormats.CommaSeparatedValue, someValues);

            DragDrop.DoDragDrop((DependencyObject)e.Source, data, DragDropEffects.Copy);
        }

When we drag this data to Excel, it shows the destination range during the drag.

707-002

When we release the mouse button, the items from the list are dropped into cells in this range.

707-003