TX Text Control .NET for Windows Forms

Drag and Drop

Topic

Drag and drop in a TextControl works in two directions. Inside the document, users move or copy selected text with the mouse. Between applications, the editor accepts text and images dragged from other programs and can be the source of text dragged elsewhere. Files dragged from File Explorer need a few lines of code, which decide whether a file is loaded and where it goes.

The examples extend the form of Create Your First Windows Forms Application, with the editor textControl1.

Move and Copy Text With the Mouse

Selected text can be dragged to another place in the document:

Mouse actionResult
Drag the selected text and drop itMoves the text to the drop position
Hold down Ctrl while dropping the selected textCopies the text to the drop position

Dragging inside the document is part of the built-in mouse interface; Selections and Caret Position lists the other mouse actions.

Allow Dragging and Dropping

Three properties control the exchange with other applications:

PropertyControls
AllowDragWhether the editor can be the source of a drag operation, for example to drag selected text into another application
AllowDropWhether the editor accepts data that is dragged onto it
DropFormatsWhich formats the editor accepts on its own: PlainText, RichTextFormat, HTMLFormat and Image

DropFormats is a combination of DropFormat values. To accept only text, with and without formatting, but no HTML or images:

textControl1.AllowDrop = true;
textControl1.DropFormats = DropFormat.PlainText | DropFormat.RichTextFormat;

Images in the sense of DropFormat.Image are device-dependent or device-independent bitmaps, TIFF images and Windows metafiles.

Accept Files From File Explorer

A file dragged from File Explorer arrives as a list of file paths, which the editor doesn't interpret on its own. Your code handles the DragEnter and DragDrop events that TextControl inherits from the Windows Forms Control class: DragEnter decides whether the file is accepted, and DragDrop loads it.

The following code in Form1.cs accepts one document or image file at a time. A document is inserted where it is dropped; the supported types are Word, RTF, HTML, XML, PDF, Excel, text and TX Text Control files. An image file becomes an inline image at the drop position:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using TXTextControl;

public partial class Form1 : Form
{
    // The document types this form accepts and their formats
    private static readonly Dictionary<string, StreamType> documentFormats =
        new Dictionary<string, StreamType>(StringComparer.OrdinalIgnoreCase)
        {
            [".docx"] = StreamType.WordprocessingML,
            [".doc"] = StreamType.MSWord,
            [".rtf"] = StreamType.RichTextFormat,
            [".htm"] = StreamType.HTMLFormat,
            [".html"] = StreamType.HTMLFormat,
            [".xml"] = StreamType.XMLFormat,
            [".pdf"] = StreamType.AdobePDF,
            [".xlsx"] = StreamType.SpreadsheetML,
            [".txt"] = StreamType.PlainText,
            [".tx"] = StreamType.InternalUnicodeFormat
        };

    // The image types this form accepts and their import filter numbers
    private static readonly Dictionary<string, int> imageFilters =
        new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
        {
            [".bmp"] = 1,
            [".tif"] = 2,
            [".tiff"] = 2,
            [".wmf"] = 3,
            [".png"] = 4,
            [".jpg"] = 5,
            [".jpeg"] = 5,
            [".gif"] = 6,
            [".emf"] = 7,
            [".svg"] = 8
        };

    public Form1()
    {
        InitializeComponent();

        textControl1.AllowDrag = true;
        textControl1.AllowDrop = true;
        textControl1.DragEnter += TextControl1_DragEnter;
        textControl1.DragDrop += TextControl1_DragDrop;
    }

    private static string GetDroppedFile(IDataObject data)
    {
        // A file dragged from File Explorer arrives as an array of paths
        string[] files = data.GetData(DataFormats.FileDrop) as string[];

        if (files == null || files.Length != 1)
        {
            return null;
        }

        string extension = Path.GetExtension(files[0]);

        return documentFormats.ContainsKey(extension) || imageFilters.ContainsKey(extension)
            ? files[0] : null;
    }

    private void TextControl1_DragEnter(object sender, DragEventArgs e)
    {
        if (e.Data.GetDataPresent(DataFormats.FileDrop))
        {
            e.Effect = GetDroppedFile(e.Data) != null ? DragDropEffects.Copy : DragDropEffects.None;
        }
    }

    private void TextControl1_DragDrop(object sender, DragEventArgs e)
    {
        string path = GetDroppedFile(e.Data);

        if (path == null)
        {
            return;
        }

        // Move the input position to the point where the file was dropped
        Point clientPoint = textControl1.PointToClient(new Point(e.X, e.Y));
        textControl1.InputPosition = new InputPosition(clientPoint);

        string extension = Path.GetExtension(path);

        if (imageFilters.TryGetValue(extension, out int filterIndex))
        {
            // Insert the image inline; -1 is the input position
            textControl1.Images.Add(new TXTextControl.Image(path, filterIndex), -1);
        }
        else
        {
            // Insert the contents of the document there
            textControl1.Selection.Load(path, documentFormats[extension]);
        }
    }
}

Keep the namespace that Visual Studio created around the class. Run the application, drag a DOCX file from File Explorer into the text and release the mouse button: the content of the file appears at that point. A PNG file dropped the same way appears as an image in the line. A file of another type shows the "not allowed" mouse pointer.

The code works as follows:

  • documentFormats maps file extensions to values of StreamType, the formats the editor loads. imageFilters maps image extensions to the import filter numbers that Images lists.
  • GetDroppedFile reads the paths with DataFormats.FileDrop and returns the file only if it is a single file with a known extension.
  • In DragEnter, e.Effect tells Windows whether to accept the file. Data other than files is left to the editor, which handles the formats of DropFormats itself.
  • e.X and e.Y are screen coordinates. PointToClient turns them into coordinates of the control, and the InputPosition constructor finds the text position at that point.
  • The Image constructor takes the file name and the filter number, so that no file dialog box opens. ImageCollection.Add with a text position of -1 inserts the image inline at the input position. The form writes TXTextControl.Image because System.Drawing has an Image class as well.
  • Selection.Load inserts a document at the input position and keeps the rest of the document.

To open a dropped document as a new document instead, replace the Selection.Load call with a call of Load, which replaces the whole content:

textControl1.Load(path, documentFormats[extension]);

The TX Words sample of the setup, in %USERPROFILE%\Documents\TX Text Control 34.0.NET for Windows Forms\CSharp\TX Words\Drag And Drop, handles the same two events and accepts the same file types: it opens dropped documents as new documents and inserts dropped images. Images describes the other ways to insert and place images.

Next Steps