Textbox Drag/Drop in WPF

So last week, somebody posted a question on Code Project about why a Drag Drop into a TextBox in WPF doesn’t actually work. When you attempt to drag and drop an item into a TextBox, it refuses to cooperate and leaves the mouse cursor as the Drop denied cursor and you can’t drop into the field. (Incidentally, this behaviour also applies to RichTextBox and FlowDocument controls). The reason that you can’t drop into these fields, even if you set AllowDrop to true, is that these particular controls mark drag and drop events as handled, preventing you from handling them yourself.

Now this might seem like a big problem – it certainly makes it look like you can’t drag/drop into a textbox, and this would seem to be a huge oversight on Microsoft’s part. Fortunately, with a little bit of knowledge of how WPF handles commands, it’s actually fairly easy to come up with a workaround. Remember that I said that WPF marks these operations as handled? This is the key to being able to work around it – each particular event (such as a DragOver event), also has a corresponding Preview event which we can hook into to perform our processing. Before I show you the code though, the OP posted a follow up query:

“A specific question I have about your solution is that you get the standard mouse cursor with the plus sign inside a box when the drag operation enters the edit box. In my initial solution to the drop into a ListBox I got a mouse cursor with an empty box, not the box with the plus sign. By experimenting I determined that you achieve this with the code you have in the PreviewDrag events. What about that code gets you the cursor with the plus sign, avoiding that anemic cursor without the plus sign?”

The following filename drag/drop sample demonstrates how changing the DragDropEffects parameter changes the appearance of the drop cursor, in answer to the Original Posters followup question:

<Window x:Class="SampleDragDrop.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:sys="clr-namespace:System;assembly=mscorlib"
  Title="Window1" Height="274" Width="300">
  <Window.Resources>
    <ObjectDataProvider
        MethodName="GetValues"
        ObjectType="{x:Type sys:Enum}"
        x:Key="DragProvider">
      <ObjectDataProvider.MethodParameters>
        <x:Type TypeName="DragDropEffects" />
      </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
    <Style TargetType="{x:Type TextBlock}">
      <Setter Property="Margin" Value="3" />
      <Setter Property="VerticalAlignment" Value="Center" />
    </Style>
  </Window.Resources>
  <Grid>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="101*" />
      <ColumnDefinition Width="177*" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
      <RowDefinition Height="27*" />
      <RowDefinition Height="203.258*" />
      <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <TextBlock Text="Drag Drop Effects" Grid.Column="0" Grid.Row="0" />
    <TextBlock Text="Drop target" Grid.Column="0" Grid.Row="1" />
    <TextBlock Text="Handled" Grid.Column="0" Grid.Row="2" />
    <ComboBox
      x:Name="cboDropEffects"
      Grid.Row="0"
      Grid.Column="1"
      Margin="2"
      SelectedIndex="0"
      ItemsSource="{Binding Source={StaticResource DragProvider}}" />
    <TextBox
      Grid.Row="1"
      Grid.Column="1"
      Margin="2"
      PreviewDragEnter="TextBox_PreviewDragEnter"
      PreviewDragOver="TextBox_PreviewDragEnter"
      PreviewDrop="TextBox_PreviewDrop" />
    <CheckBox x:Name="chkHandled" IsChecked="True" Grid.Row="2" Grid.Column="1" Margin="2" />
  </Grid>
</Window>

If you look carefully at the code, you see that we bind PreviewDragEnter and the PreviewDragOver to the same event handler. The PreviewDrop event maps to a different event handler, where we actually perform the drop of the filename.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace SampleDragDrop
{
  /// <summary>
  /// Interaction logic for Window1.xaml
  /// </summary>
  public partial class Window1 : Window
  {
    public Window1()
    {
      InitializeComponent();
    }

    private void TextBox_PreviewDragEnter(object sender, DragEventArgs e)
    {
      e.Effects = (DragDropEffects)cboDropEffects.SelectedItem;
      if (chkHandled.IsChecked.HasValue)
      {
        e.Handled = chkHandled.IsChecked.Value;
      }
    }

    private void TextBox_PreviewDrop(object sender, DragEventArgs e)
    {
      object text = e.Data.GetData(DataFormats.FileDrop);
      TextBox tb = sender as TextBox;
      if (tb != null)
      {
        tb.Text = string.Format("{0}", ((string[])text)[0]);
      }
    }
  }
}

When you run the sample, play around with the Drag Drop Effects values, and setting/unsetting the Handled checkbox, to see what behaviour the textbox exhibits (and the answer to the OPs question, is that setting e.Effects to DragDropEffects.All sets the cursor to the relevant cursor).

This sample is available here: SampleDragDrop. Don’t forget to change the extension from .doc to .zip when you download it.

2 thoughts on “Textbox Drag/Drop in WPF

Leave a comment