So last week, somebody posted a question on CodeProject 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
{
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 at SampleDragDrop. Don't forget to change the extension from .doc to .zip when you download it.