Skip to main content

C# WPF Snippet - Disabling Dragging from a TextBox [Beginner]


Reading the title, you are probably even wondering why this even deserves a tutorial. Disabling the ability to drag selected text out of a TextBox? That has got to just be a flag somewhere, right? That is what I thought, and it turns out I was quite wrong.

The TextBox in WPF has a lot of built in behavior, from responding to keyboard shortcuts for copy/paste to having its own context menu. It even has a built in spell checker and undo stack! One of these built in features is the ability to drag selected text into and out of a TextBox. Generally, this is probably a nice feature to have (although I don't ever use it) - but sometimes it can get in the way. For instance, if your application is paying close attention to mouse capture or focus (say you are inside a popup), an errant drag operation started from within your captured area doesn't generally do useful things.

Disabling dropping into a TextBox is pretty easy - all you have to do is set the AllowDrop property on the TextBox to false. Disabling dragging, on the other hand, is not so easy. None of the properties on TextBlock seemed to be relevant, and when I tried to Google for the answer, the best that I found was forumpost on MSDN. And guess what - the forum post said that what I was trying to do was impossible!

Well, that annoyed me to no end, so I broke out Reflector and started working my way backward through the framework code. I'll spare you the horrifying gritty details of my search - suffice it to say, I was successful in my quest! You can in fact disable dragging from a TextBox, and it is actually pretty simple to do. So simple, in fact, that I'm just going to drop all the code for it in one block:

Test App Screenshot
 
<Window x:Class="WpfTextBoxDragCopy.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="DataObject Event Test" Height="138" Width="300">
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <TextBlock Text="Regular TextBox: " Grid.Row="0" Grid.Column="0" />
    <TextBox x:Name="_TextBlock1" Grid.Row="0" Grid.Column="1" />

    <TextBlock Text="No Copy: " Grid.Row="1" Grid.Column="0" />
    <TextBox x:Name="_TextBlock2" Grid.Row="1" Grid.Column="1" />

    <TextBlock Text="No Drag Copy: " Grid.Row="2" Grid.Column="0" />
    <TextBox x:Name="_TextBlock3" Grid.Row="2" Grid.Column="1" />

    <TextBlock Text="No Paste: " Grid.Row="3" Grid.Column="0" />
    <TextBox x:Name="_TextBlock4" Grid.Row="3" Grid.Column="1" />
  </Grid>
</Window>

using System.Windows;

namespace WpfTextBoxDragCopy
{
  public partial class Window1 : Window
  {
    public Window1()
    {
      InitializeComponent();
      DataObject.AddCopyingHandler(_TextBlock2, NoCopy);
      DataObject.AddCopyingHandler(_TextBlock3, NoDragCopy);
      DataObject.AddPastingHandler(_TextBlock4, NoPaste);
    }

    private void NoCopy(object sender, DataObjectCopyingEventArgs e)
    {
      e.CancelCommand();
    }

    private void NoDragCopy(object sender, DataObjectCopyingEventArgs e)
    {
      if (e.IsDragDrop)
      { e.CancelCommand(); }
    }

    private void NoPaste(object sender, DataObjectPastingEventArgs e)
    {
      e.CancelCommand();
    }
  }
}
 
It all has to do with the events on DataObject. When a copy (or a paste for that matter) is initiated on a TextBox, deep inside the TextBox code the DataObject.Copying event is fired (or DataObject.Pasting if it was a paste). This is a routed event, which means it bubbles up from its source i.e., the TextBox. If anyone along the way catches the event and calls the CancelCommand method on the event args, the copy (or paste) gets canceled.

And even better, the event args have a property called IsDragDrop which (you might have guessed) is true if the copy/paste was triggered due to a drag/drop operation. So to disable dragging out of a TextBox, all you have to do is listen for the DataObject.Copying event, and if IsDragDrop is true, call CancelCommand.

For the example above, that particular sequence of events happens for _TextBox3. _TextBox2 shows disabling copying altogether, and _TextBox4 shows disabling pasting altogether.

As a random side note, if you use a PasswordBox (a special form of TextBox) copying anything out through any of the standard means (drag, keyboard shortcut, context menu) is completely disabled. In fact, while browsing the code in Reflector, I found that it is hard coded to be disabled (which makes sense - being able to copy a password out of a password box is kind of a security risk).

That is it for this short snippet - I hope it has saved you some time. I know that if I had found a post like this when I was looking, it would have saved an annoying hour in Reflector. As always, you can grab the source for the example below.


Source Files:

Comments

Popular posts from this blog

C# Snippet - Shuffling a Dictionary [Beginner]

Randomizing something can be a daunting task, especially with all the algorithms out there. However, sometimes you just need to shuffle things up, in a simple, yet effective manner. Today we are going to take a quick look at an easy and simple way to randomize a dictionary, which is most likely something that you may be using in a complex application. The tricky thing about ordering dictionaries is that...well they are not ordered to begin with. Typically they are a chaotic collection of key/value pairs. There is no first element or last element, just elements. This is why it is a little tricky to randomize them. Before we get started, we need to build a quick dictionary. For this tutorial, we will be doing an extremely simple string/int dictionary, but rest assured the steps we take can be used for any kind of dictionary you can come up with, no matter what object types you use. Dictionary < String , int > origin = new Dictionary < string , int >();

C# Snippet - The Many Uses Of The Using Keyword [Beginner]

What is the first thing that pops into your mind when you think of the using keyword for C#? Probably those lines that always appear at the top of C# code files - the lines that import types from other namespaces into your code. But while that is the most common use of the using keyword, it is not the only one. Today we are going to take a look at the different uses of the using keyword and what they are useful for. The Using Directive There are two main categories of use for the using keyword - as a "Using Directive" and as a "Using Statement". The lines at the top of a C# file are directives, but that is not the only place they can go. They can also go inside of a namespace block, but they have to be before any other elements declared in the namespace (i.e., you can't add a using statement after a class declaration). Namespace Importing This is by far the most common use of the keyword - it is rare that you see a C# file that does not h

C# WPF Printing Part 2 - Pagination [Intermediate]

About two weeks ago, we had a tutorial here at SOTC on the basics of printing in WPF . It covered the standard stuff, like popping the print dialog, and what you needed to do to print visuals (both created in XAML and on the fly). But really, that's barely scratching the surface - any decent printing system in pretty much any application needs to be able to do a lot more than that. So today, we are going to take one more baby step forward into the world of printing - we are going to take a look at pagination. The main class that we will need to do pagination is the DocumentPaginator . I mentioned this class very briefly in the previous tutorial, but only in the context of the printing methods on PrintDialog , PrintVisual (which we focused on last time) and PrintDocument (which we will be focusing on today). This PrintDocument function takes a DocumentPaginator to print - and this is why we need to create one. Unfortunately, making a DocumentPaginator is not as easy as