#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

Advertisements

#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

#706 – Dragging User Interface Elements Between Applications

You can drag user interface elements between WPF applications using drag-and-drop.  To do this, you read the XAML for the portion of the logical tree that you want to drag and specify XAML as your data format.

The example below shows how to drag a StackPanel and everything in it from one application to another.

On the drag side, we use a XamlWriter object to store all of the XAML into a string.

        private void StackPanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            string xaml = XamlWriter.Save(e.Source);
            DataObject data = new DataObject(DataFormats.Xaml, xaml);

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

On the drop side, we use a XamlReader to load the data back into the application.  In this example, we set the StackPanel as the main content for the Window that we’re dragging it to.

        private void Window_Drop(object sender, DragEventArgs e)
        {
            string xaml = (string)e.Data.GetData(DataFormats.Xaml);
            this.Content = XamlReader.Load(new XmlTextReader(new StringReader(xaml)));
        }

706-001
706-002
706-003

#705 – Dragging a Custom Object Using Serialization as Format

When you use drag-and-drop in WPF, you specify a data format for the data to be dragged.  You can “drag” any object that you like, as long as the object is serializable.  Here’s an example, where a Dog object is dragged between instances of a WPF application.

The GUI consists of several labels that show properties of a Dog object.  The Dog can be dragged from the StackPanel or dropped onto the StackPanel.

    <StackPanel Orientation="Vertical" HorizontalAlignment="Center"
                MouseLeftButtonDown="StackPanel_MouseLeftButtonDown"
                AllowDrop="True" Drop="StackPanel_Drop">
        <Label Content="{Binding TheDog.Name}" FontWeight="Bold"/>
        <Label Content="{Binding TheDog.Age}"/>
        <Label Content="{Binding TheDog.BarkSound}"/>
        <Button Content="Create Dog" Click="Button_Click"/>
    </StackPanel>

The code-behind creates a Dog object on the button click.  When dragging starts, the Dog is passed in to the DoDragDrop method.  In the Drop handler, the Dog data is retrieved via the IDataObject interface.

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
        }

        private Dog theDog;
        public Dog TheDog
        {
            get { return theDog; }
            set
            {
                if (value != theDog)
                {
                    theDog = value;
                    RaisePropertyChanged("TheDog");
                }
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            TheDog = new Dog("Kirby", 15, "Woof");
        }

        private void StackPanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            DataObject data = new DataObject(DataFormats.Serializable, theDog);

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

        private void StackPanel_Drop(object sender, DragEventArgs e)
        {
            TheDog = (Dog)e.Data.GetData(DataFormats.Serializable);
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string prop)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }
    }

This allows us to drag the Dog from one instance of the application to another. You could also drag objects between applications in the same way.

705-001

705-002

For completeness, here’s the code for the Dog class:

    [Serializable]
    public class Dog
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public string BarkSound { get; set; }

        public Dog(string name, int age, string barkSound)
        {
            Name = name;
            Age = age;
            BarkSound = barkSound;
        }
    }

#704 – Escape Key Cancels Drag-and-Drop Behavior

You can always use the Escape key to cancel a drag-and-drop operation that is in progress.  The cursor will return to the default value and the Drop event for the target control will not be fired, even if the mouse is currently over a control that is a drop target.

#703 – Indicating Formats that Are Allowed to be Dropped

A control becomes a drop target by setting its AllowDrop property to true and handling its Drop event, where you retrieve the dragged data.  You could also verify in the Drop event whether the data being dropped is a format that the control can handle.  But at that point, the data has already been dropped.

The preferred method for a control to indicate which types of data it can handle is to handle the DragEnter and DragOver events and set the DragEventArgs.Effects property to indicate that the operation is not allowed.  This will result in the mouse cursor indicating that the drop operation is not allowed.  The Drop event will also not fire.

        private void Image_DragEnter(object sender, DragEventArgs e)
        {

            if (e.Data.GetDataPresent(DataFormats.Bitmap))
                e.Effects = DragDropEffects.Copy;
            else
                e.Effects = DragDropEffects.None;

            e.Handled = true;
        }

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

            e.Handled = true;
        }