How could I load files to a form by drag-and-drop?
Which event will appear?
Which component should I use?
And how to determine name of file and others properties after drag-and-dropping it to a form?
Thank you!
Code
private void panel1_DragEnter(object sender, DragEventsArgs e){
if (e.Data.GetDataPresent(DataFormats.Text)){
e.Effect = DragDropEffects.Move;
MessageBox.Show(e.Data.GetData(DataFormats.Text).toString());
}
if (e.Data.GetDataPresent(DataFormats.FileDrop)){
}
}
ok, this works.
How about files? How can I get filename and extension?
and this is only a dragEnter
action.
asked Dec 18, 2011 at 9:51
gaussblurincgaussblurinc
3,6229 gold badges34 silver badges64 bronze badges
2
This code will loop through and print the full names (including extensions) of all the files dragged into your window:
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
foreach (string filePath in files)
{
Console.WriteLine(filePath);
}
}
Cody Gray♦
236k50 gold badges486 silver badges567 bronze badges
answered Dec 18, 2011 at 9:55
Sarwar ErfanSarwar Erfan
18k5 gold badges45 silver badges57 bronze badges
Check the below link for more info
Drag and Drop Files into winforms
private void Form2_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
string[] filePaths = (string[])(e.Data.GetData(DataFormats.FileDrop));
foreach (string fileLoc in filePaths)
// Code to read the contents of the text file
if (File.Exists(fileLoc))
using (TextReader tr = new StreamReader(fileLoc))
{
MessageBox.Show(tr.ReadToEnd());
}
}
}
René Vogt
42.6k14 gold badges78 silver badges98 bronze badges
answered Dec 18, 2011 at 10:05
1
You need work with 2 below Events, of course while your Control/Form AllowDrop property’s is true.
private void Home_DragOver(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
e.Effect = DragDropEffects.Link;
else
e.Effect = DragDropEffects.None;
}
private void Home_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
//YourOpenMethod(files);
}
}
Enjoy…
answered Dec 13, 2019 at 13:27
MiMFaMiMFa
8449 silver badges13 bronze badges
1
Вступление
Доброго времени суток, уважаемые хабрчане. Данный пост не будет особо отличать оригинальностью, уникальностью или гениальностью. Я бы назвал его руководство по пользованию. Думаю многие бывалые программисты сделают множество замечаний, за что я в целом буду только благодарен, тем более если это будет конструктивная критика. Но, в целом, мой пост ориентирован больше на начинающих программистов, так как бывалые скорее всего сталкивались с такой задачей и уже решили её сами.
Итак, о чем же будет сегодняшний наш разговор. Как вы поняли из названия, я расскажу как реализовать Drag and Drop на языке C# в Visual Studio. Думаю многие начинающие программисты сталкивались с такой проблемой, когда существует несколько списков и вы хотите перетащить элементы из одного в другой, но придумать как это сделать или найти понятный мануал не могли. И ведь так хочется, чтоб в вашем приложении было все красиво и современно, а приходилось обходится простым выбором элемента и переносом его в другой по нажатию на кнопку. Надеюсь сегодня этим простым руководством я смогу всё-таки помочь парочек юных бойцов(я и сам такой, как мне кажется) и развеять любые проблемы связанные с реализацией данного функционала.
Хочу заметить, что я не буду подробно рассматривать как найти или добавить тот или другой элемент на форму, не буду также подробно описывать свойства объектов.
С чего все начиналось
Вкратце расскажу с чего все начиналось и что мы с вами должны будем сделать(ТЗ). Началось всё с того, что моей задачей стала реализация автомата с напитками, причём полная реализация, с учетом всех нюансов, таких как замена напитков, их цен, пополнение кассы автомата без использование кода и многое другое, что могло быть опущено при других обстоятельствах. Но я подумал, если делать, то максимально реалистичнои решил добавить drag and drop денег из кошелька в слот для денег с чем пришлось изрядно помучатся, но что придало моей программе изюминку.
Техническое задание
У нас имеется два объекта: ListView, который представляет наш кошелёк и ListBox, в котором при перетаскивании будет появляться название купюры либо монеты. Необходимо создать программу, которая позволит перетаскивать элементы из объекта ListView в ListBox без дополнительных кнопок.
Ну, что ж, вызов принят. Приступим.
Шаг 1 Создание рабочей области
Создаем новый проект Windows Form Applicarion и добавляем на форму следующие элементы:
- ListView. Наш кошелёк откуда мы будем перетаскивать денежку. Напомню, что все элементы будут представлены в виде изображений монет и купюр.
- ListBox. Список, куда мы будем всё это перетаскивать, в котором будут отображаться названия номиналов.
- ImageList. Именно отсюда мы получим наши изображения денег.
- Label. Вспомогательный элемент, который будет показывать в какую позицию будет добавлено название в ListBox.
Шаг 2 Подготовка элементов
Добавляем в ImageList картинки отсюда. Называем их так, чтоб вам потом было понятно, где какая картинка. Позже поймете для чего. В свойствах ListView есть поле View, я выбрал режим просмотра Large icon, но вы можете выбрать Small icon. В зависимости от выбора в поле LargeImageList или SmallImageList выбираете имя вашего объекта ImageList. Но это еще не всё. Теперь открываем свойство Items для всё того же ListView и добавляем новый элемент. Для него в поле значение ImageIndex изображение, а в поле Tag прописываем текст, согласно номиналу на изображении. И так добавляем все необходимые нам элементы.
Теперь все картинки отображаются в ListView и имеют «название»(Tag). Настраиваем по размерам все объекты и балуемся настройками внешнего вида на свой вкус. Советую каждый элемент называть так, чтоб было понятно для чего он.
Вот что получилось у меня:
Шаг 3 Код
Пришло время самой важной и самой сложной части. В целом, скопировать и вставить будет не сложно, но важно понять код! Выжпрограммисты;)
Добавляем события для объектов.
Для ListView:
- MouseDown
- MouseUp
- MouseMove
Для ListBox:
- DragOver
- DragDrop
- DragEnter
- DragLeave
Теперь всё готов для написания кода. Включаем внимательность, отключаем мобильные телефоны и другие отвлекающие факторы(кота тоже можно отключить).
В первую очередь добавим в начало класса переменные, которые нам пригодятся в дальнейшем. Описывать их не буду, так как их предназначение вы поймете в коде.
private int indexOfItemUnderMouseToDrag;
private int indexOfItemUnderMouseToDrop;
private Rectangle dragBoxFromMouseDown;
private Point screenOffset;
ListView MouseDown
Это событие будем происходить в тот момент, когда вы нажмете на левую кнопку мыши в объекте ListView.
private void ListDragSource_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
// Получаем индекс выбранного элемента
indexOfItemUnderMouseToDrag = ListDragSource.Items.IndexOf( ListDragSource.GetItemAt(e.X, e.Y) );
if (indexOfItemUnderMouseToDrag != ListBox.NoMatches)
{
// DragSize показывает на сколько можно сместить мышку, чтоб произошло событие
Size dragSize = SystemInformation.DragSize;
// Создаем прямоугольник в центре которого расположен курсор
dragBoxFromMouseDown = new Rectangle(new Point(e.X - (dragSize.Width /2),
e.Y - (dragSize.Height /2)), dragSize);
}
else
// Сбрасываем наш прямоугольник если мышка не на каком-либо элементе в ListView.
dragBoxFromMouseDown = Rectangle.Empty;
}
ListView MouseUp
Отпустив кнопку мышь мы автоматически «бросаем» объект.
private void ListDragSource_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) {
// Сбросить прямоугольник если кнопка отпущена
dragBoxFromMouseDown = Rectangle.Empty;
}
ListView MouseMove
Двигая мышку мы вызываем это событие, причём работать оно начинает только если мы вышли за пределы нашего «кошелька».
Хочу обратить ваше внимание на строку:
ListDragSource.Items.RemoveAt(indexOfItemUnderMouseToDrag);
Ее можно так же запрограммировать не только на удаление, но и на уменьшение количества таких элементов в списке, если их несколько. В данном случае всё зависит от цели и вашей фантазии. Выбор действия(перемещение или копирование) описан в части DragOver.
private void ListDragSource_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
{
// Если курсор вышел за пределы ListView - начинаем перетаскивание
if (dragBoxFromMouseDown != Rectangle.Empty &&
!dragBoxFromMouseDown.Contains(e.X, e.Y))
{
// screenOffset служить для определения границ экрана
screenOffset = SystemInformation.WorkingArea.Location;
DragDropEffects dropEffect = ListDragSource.DoDragDrop(ListDragSource.Items[indexOfItemUnderMouseToDrag],
DragDropEffects.All);
// Если было выбрано "перемещение" удалить элемент из ListView
if (dropEffect == DragDropEffects.Move)
{
//Также можете ничего не удалять или уменьшать количество(изменять какие-то параметры)
ListDragSource.Items.RemoveAt(indexOfItemUnderMouseToDrag);
// Если в списке есть элементы вставляем новый элемент над выбранным
if (indexOfItemUnderMouseToDrag > 0)
ListDragSource.Items[indexOfItemUnderMouseToDrag - 1].Selected = true;
else if (ListDragSource.Items.Count > 0)
// Вставляем на место первого элемента
ListDragSource.Items[0].Selected = true;
}
}
}
}
ListBox DragOver
В зависимости от задачи нам могут понадобится разные действия с нашими элементами из списка. Перемещение, копирование — всё это может пригодится. В коде ниже реализовано следующее:
- По умолчанию все элементы будут перемещаться
- При зажатом Ctrl все элементы будут копироваться
private void ListDragTarget_DragOver(object sender, System.Windows.Forms.DragEventArgs e)
{
if ((e.KeyState & 8) == 8 &&
(e.AllowedEffect & DragDropEffects.Copy) == DragDropEffects.Copy)
{
// Ctrl для копирования
e.Effect = DragDropEffects.Copy;
}
else if ((e.AllowedEffect & DragDropEffects.Move) == DragDropEffects.Move)
{
// По умолчанию перемещение
e.Effect = DragDropEffects.Move;
}
else
e.Effect = DragDropEffects.None;
// Можно добавить другие кнопки или действия при перетаскивании
// Получаем индекс над которым расположен курсор
// Так как позиция мышки вычисляется по в координатах всего экрана конвертируем
// координаты относительно рабочего окна
indexOfItemUnderMouseToDrop =
ListDragTarget.IndexFromPoint(ListDragTarget.PointToClient(new Point(e.X, e.Y)));
// Изменение текста нашего вспомогательного Label
if (indexOfItemUnderMouseToDrop != ListBox.NoMatches)
DropLbl.Text = "Вставить над элементом #" + (indexOfItemUnderMouseToDrop + 1);
else
DropLbl.Text = "Вставить в конец";
}
ListBox DragDrop
Добавление перетаскиваемого объекта в новый список. Здесь самое важное это аргумент sender, который является нашим перетаскиваемым элементом. И здесь снова включается ваша фантазия. В данном случае, я просто получаю значение Tag из полученного объекта(помните мы добавляли в поле Tag названия номиналов?). Точно таким же образом вы можете реализовать всё, что пожелает ваша душа.
private void ListDragTarget_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
{
// Проверим, что перетаскиваемый не пустой
Object item = (object)e.Data.GetData(typeof(System.Windows.Forms.ListViewItem));
// Выбираем действием в зависимости от действия(перемещения или копирования)
if (e.Effect == DragDropEffects.Copy ||
e.Effect == DragDropEffects.Move)
{
// Вставка элемента в новый список
if (indexOfItemUnderMouseToDrop != ListBox.NoMatches)
ListDragTarget.Items.Insert(indexOfItemUnderMouseToDrop, ((ListViewItem)item).Tag.ToString());
else
ListDragTarget.Items.Add(((ListViewItem)item).Tag.ToString());
}
// Так как ничего не перетаскивается, устанавливаем значение "None"
DropLbl.Text = "None";
}
ListBox DragEnter,DragLeave
Наконец, самое последнее, это «сброс» нашего Label. Так как если этого не сделать, то он продолжить нам показывать в какое место списка будет добавлен файл, даже если файл уже добавлен.
private void ListDragTarget_DragEnter(object sender, System.Windows.Forms.DragEventArgs e) {
// Устанавливаем значение "None"
DropLbl.Text = "None";
}
private void ListDragTarget_DragLeave(object sender, System.EventArgs e) {
// Устанавливаем значение "None", когда курсор вышел за пределы объекта, в который перетаскиваем элемент
DropLbl.Text = "None";
}
Заключение
Остается только запустить приложение и удостовериться, что всё работает(не забудьте протестировать программу с зажатым Ctrl). Теперь, сделав всё написанное выше, вы сможете адаптировать этот код под свои цели. Добавить счетчики для подсчета объектов, перетаскивать объекты не только из списков в списки, но и многое другое теперь можно реализовать в кратчайшие сроки.
Задание на дом
1. Добавьте учет количества монет в ListView(с помощью счетчика или любым другим удобным способом). Реализуйте не удаление монеты из ListView, а уменьшение их количества.
2. Добавьте любой новый объект(какой именно вы должны определить сами) и перетащите в него изображение из ListView. Каждое новое перетаскивание должно удалять предыдущее изображение.
Примечание: Можно добавить сразу ListView и добавлять в него фото в виде новых элементов. Не забудьте учесть, что если монетка(купюра) были добавлены ранее, то необходимо не добавлять новый элемент, а увеличить количество старых элементов.
На этом я заканчиваю свое руководство, надеюсь, что оно кому-то будет полезным. Спасибо за внимание!
{lang: ‘ru’}
.NET Framework позволяет легко обнаруживать перетаскиваемые объекты в/из приложения Windows Forms. Для этого можно использовать одно или несколько из доступных событий drag and drop. В обработчиках этих событий можно проверить, является ли объект файлом.
Для включения отслеживания событий drag and drops, нужно установить свойство AllowDrop в true и использовать одно или несколько следующих событий:
-
DragEnter: Возникает при завершении операции перетаскивания
-
DragOver: Происходит, когда элемент перетаскивается с помощью мыши в клиентскую область этого элемента.
-
DragDrop: Возникает, когда объект перетаскивается за пределы элемента управления.
-
DragLeave: Возникает, когда объект перетаскивается на элемент управления.
-
GiveFeedback: Происходит, когда элемент перетаскивается с помощью мыши. Система запрашивает у элемента управления обеспечения обратной совместимости с этим эффектом.
-
QueryContinueDrag: Происходит при перетаскивании элемента. Система опрашивает, можно ли продолжать операцию перетаскивания мышью.
Чтобы обработать перетаскивание одного или нескольких файлов внутрь элемента управления, необходимо обработать два события: DragEnter и DragDrop. В DragEnter мы проверяем, что перетаскиваемый элемент имеет тип DataFormats.FileDrop. Если это так, операция перетаскивания правда операция перетаскивания разрешается. Событие DragDrop получает список файлов для помещения в элемент с помощью метода GetData и добавляет их в массив строк. Каждый элемент массива будет содержать полный путь к каждому файлу, помещенному в элемент управления.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
private void filesListBox_DragEnter(object sender, DragEventArgs e) { if (e.Data.GetDataPresent(DataFormats.FileDrop, false) == true) { e.Effect = DragDropEffects.All; } } private void filesListBox_DragDrop(object sender, DragEventArgs e) { string[] files = (string[])e.Data.GetData(DataFormats.FileDrop); foreach (string file in files) { filesListBox.Items.Add(file); } } |
Статья написана по мотивам оригинальной англоязычной версии. Исходный код с работающим примером так же доступен по ссылке из неё.
Полезная статья? Их будет больше, если вы поддержите меня!
Table of Contents
- Introduction
- The Process
- Step 1: Enabling AllowDrop property of the DataGridView
- Step 2: Setting up the DragEnter event for some action
- Step 3: Finally make the DragDrop action happen here
- Caution: Hot Stuff Here
- See Also
Introduction
If you want to drag and drop files on a DataGridView in your WinForm (Windows Form) application and want the DataGridView to process and populate that data on it then you have come across the right article. Follow along and within minutes you will have the
right idea of how to achieve that.
The Process
Step 1: Enabling AllowDrop property of the DataGridView
Go to the properties tab of your DataGridView ad enable the AllowDrop to True instead of False, as follows:
Step 2: Setting up the DragEnter event for some action
Here you check whether the data format of the file(s), which is being dragged and dropped, is in the correct format. And if the format is acceptable then you modify the drop effect on the DataGridView accordingly. In order to achieve this you need to write
the following code:
private
void
dataGridView_DragEnter(object
sender, DragEventArgs e)
{
// Check if the Data format of the file(s) can be accepted
// (we only accept file drops from Windows Explorer, etc.)
if
(e.Data.GetDataPresent(DataFormats.FileDrop))
{
// modify the drag drop effects to Move
e.Effect = DragDropEffects.Move;
}
else
{
// no need for any drag drop effect
e.Effect = DragDropEffects.None;
}
}
Step 3: Finally make the DragDrop action happen here
In the DragDrop event of the DataGridView, you write your desired action of what to do when the data format of the file(s) being dropped is acceptable. Use the following code for this step:
private
void
dataGridView_DragDrop(object
sender, DragEventArgs e)
{
// still check if the associated data from the file(s) can be used for this purpose
if
(e.Data.GetDataPresent(DataFormats.FileDrop))
{
// Fetch the file(s) names with full path here to be processed
string
[] fileList = (
string
[])e.Data.GetData(DataFormats.FileDrop);
// Your desired code goes here to process the file(s) being dropped
}
}
And TADA! You have a working Drag and Drop feature on your DataGridView in your WinForm Application.
Caution: Hot Stuff Here
Wasted 5 mins figuring out that without enabling AllowDrop property of the DataGridView this whole feature wouldn’t work. So before you write the codes for DragEnter and DragDrop events do make sure that AllowDrop is enabled, or else you will also find yourself
wasting few minutes just to figure out this one. Happy coding!
See Also
Control.DragDrop Event: https://msdn.microsoft.com/en-us/library/system.windows.forms.control.dragdrop(v=vs.110).aspx
Control.DragEnter Event: https://msdn.microsoft.com/en-us/library/system.windows.forms.control.dragenter(v=vs.110).aspx
DataFormats.FileDrop Field: https://msdn.microsoft.com/en-us/library/system.windows.forms.dataformats.filedrop(v=vs.110).aspx
DragEventArgs Class: https://msdn.microsoft.com/en-us/library/system.windows.forms.drageventargs(v=vs.110).aspx
If you want to capture your users’ imaginations, enable drag-and-drop.
Drag-and-drop doesn’t fulfill requirements, but it contributes to making your application appear more professional and easy to use. In Windows Forms applications, drag-and-drop operations consist mostly of handling a series of events. By accomplishing a few mandatory steps and working with the information available in the event arguments, you can easily facilitate dragging and dropping files, text, and any other sort of serializable data objects. This article demonstrates how to import files from the Windows shell and how to enhance some UI controls to make them accept input via drag-and-drop. Notable examples are the TextBox and the PictureBox controls.
Drag-and-drop is one of those programming features rarely listed in the user requirements document because it is perceived as secondary and minor. Sure, drag-and-drop doesn’t make your application run faster, nor does it add anything to the core business of the application. Drag-and-drop is not the solution to any core business issues and doesn’t add any new functionality to the application. Yet drag-and-drop is a feature that, if missing, users promptly miss and are quick to add to the wish list for the next upgrade.
The first step to enable drag-and-drop in a form or control is setting the AllowDrop property to True.
As a matter of fact, drag-and-drop greatly simplifies how people use virtually any application. It makes transferring data from one control to the next, and from and to other applications and to the Windows shell itself, into a straightforward activity. Drag-and-drop has existed for a long time in Windows and had its first fully message-based SDK available with Windows for Workgroups in an earlier software era.
The advent of OLE made the drag-and-drop transition from messages to functions and generalized the format of the data being moved. Implementing drag-and-drop, though, remained a sort of nightmare for most programmers. Things were much easier for Visual Basic developers and you’ll still find that model at the root of the .NET Framework support for drag-and-drop.
Enabling Drag-and-Drop in Forms and Controls
All .NET components allowed in a Windows form have a Boolean property named AllowDrop, which is set to False by default. Turning that value to true is the first and key step to enabling drag-and-drop in a form or a control.
Some drag-and-drop functionality is built into the Control class, which implements very basic functionality required by classes that display information to the user. The Control class handles user input through the keyboard and mouse. Drag-and-drop results from a combined use of the mouse and keyboard. It is worth noting that in the .NET Framework, the Form class?the base class for all Windows form windows?inherits the Control class. This means that the set of drag-and-drop features is common to all forms and controls.
Accomplishing drag-and-drop operations is done handling a series of events and setting a few properties, most notably the aforementioned AllowDrop property. Table 1 lists the key members involved in all drag-and-drop operations.
The AllowDrop property is useful if you want the form or control to accept data from external applications or controls. A form or control that only exports data through drag-and-drop doesn’t need to set the value to True.
Let’s review the sequence of events and related steps that occur when data is being dragged from, or dropped onto, a form or control.
To make the form or control the target of a drag-and-drop operation, you start by setting AllowDrop to True. Next, handle the DragDrop event, indicating that the operation is complete. The event arguments include the data being moved and its format. In the handler of this event, the target of the drop decides what to do and how to refresh its own user interface in light of the drop.
During the operation, a few other events occur. They are: DragEnter when the mouse enters the control’s bounds; DragOver while the mouse is moved within the control’s bounds; and DragLeave when the mouse leaves the control. These events are necessary in a limited number of circumstances, like when you’re refreshing the UI to reflect the exact, and continuously changing, position of the mouse.
DoDragDrop is the method that governs the whole drag operation. The method fires events to the target and the source as needed.
To start a drag-and-drop operation, begin writing a handler for the control’s MouseDown event. The event occurs whenever the (left) mouse button is pressed within the control’s bounds. Pressing the mouse button is the first low-level event in any drag-and-drop operation.
The mouse down alone is not a reliable indicator that a drag-and-drop operation has started. When the mouse down event is fired, first store the coordinates at which the mouse button was pressed. More exactly, create a rectangle of a fixed and system-defined size centered around the point of click. Next, when the MouseMove event arrives, verify that the current coordinates of the mouse point are outside that rectangle. If so, the user is moving the mouse enough to denote a real drag-and-drop action. The beginning of the drag-and-drop operation is marked with a call to DoDragDrop. You call this method from within the MouseMove event handler.
Next, retrieve the data to drag and pack it in a serializable object. The DoDragDrop method takes two arguments: the actual data and the allowed effects. You pass actual data through an Object variable, using any valid .NET class, including custom classes.
The parameter indicating the allowed effects of the operation is any combination of the values defined in the DragDropEffects enum type. Note that if you’re dragging across the boundaries of the application (or between objects in the same application but managed by code in different AppDomains), your data object must fulfill an additional requirement: the data should be serializable or, at least, implement the IDataObject interface. Note that base classes like String, MetaFile, and Bitmap are considered serializable even if they don’t explicitly implement the ISerializable interface.
GiveFeedback is used to provide users with some feedback about the possible outcome of the ongoing operation, be it a copy, move, or link of the dragged data. Any call to GiveFeedback occurs within the initial call to the DoDragDrop method that any source control does to start a drag-and-drop operation. DoDragDrop determines the control under the current cursor location and raises the event if the control is a valid drop target.
If there is a change in the keyboard state (e.g., the CTRL key is pressed), the QueryContinueDrag event is also raised to ask the user whether to continue or conclude the operation. The control’s code determines what to do by setting a value in the event argument’s data. If the operation must continue, the DragOver event is raised followed by the GiveFeedback event. The DragOver and GiveFeedback events arrive in a pair so that the user is given the most up-to-date feedback on the mouse’s location.
Table 2 summarizes the steps to making a control or a form function as the source and the target of a drag-and-drop operation.
Those are the basics of drag-and-drop operations; now let’s review the code needed to support a few common situations.
Getting Files from the Windows Shell
When you select one or more files in any Explorer window, the names of the files are packed in an array of strings and passed along during the drag-and-drop operation. Suppose you have an MDI application and set the AllowDrop property of your main window to true. What happens when you drop one or more files selected from the shell onto that window? The drag-and-drop infrastructure fires the DragDrop event to the object that is the target of the drop: your form.
Implementing drag-and-drop from within a TextBox control is more complicated than with other controls. This is mostly due to the fact that the TextBox control handles the MouseDown event on its own.
More precisely, you need to handle a couple of events: DragOver and DragDrop. The former is useful to check the type of data being dragged and determines whether that data is of any interest to you. The latter finalizes the operation accepting the file names as effective input of the application. Listing 1 shows a possible implementation. Both events store arguments using an instance of the DragEventArgs class. The class counts the properties listed in Table 3.
Of particular interest is the property Data. It is a member of type IDataObject, an interface that gathers the methods to get and set data to drag and drop. The interface lists four methods: GetData, GetDataPresent, GetFormats, and SetData.
Any data coming to you through a drag-and-drop operation is normally available in multiple formats. The GetFormats method returns an array of strings with the names of the supported formats. You can check them all using the following code:
For Each fmt As String In e.Data.GetFormats()
' Output format name and type
Debug.WriteLine(fmt + " (" + _
e.Data.GetData(fmt).ToString() + ")")
Next
The .NET Framework groups the most commonly used data formats in the DataFormats enum type. The GetData method extracts and returns the data of the specified format and GetDataPresent indicates whether data is available in a given format. Finally, you use SetData to add data in a given format to the container being moved across controls.
When the Windows shell initiates the drag-and-drop operation, data is moved in six different formats: an array of shell IDs, the DragContext structure, the InShellDragLoop structure, the FileName, the FileNameW, and the FileDrop. The first three types are memory streams; the latter three are arrays of strings. The first three types are supported for backward compatibility toward Win16 and Win32 applications. FileName, FileNameW, and FileDrop are formats specifically designed for .NET applications. Of these, only FileDrop is significant in the vast majority of situations.
It is interesting to illustrate the difference between FileDrop and the other two formats. First and foremost, only the FileDrop format guarantees that, in case of multiple selections, all the file names are reported. FileName and FileNameW return a one-element array, the currently focused shell item. In addition, FileName returns the MS-DOS name of the file in the old 8+3 format; FileNameW returns the long file name. For example, you can get the focused item using the following code:
Dim s() As String
s = CType(e.Data.GetData("FileNameW"), String())
Dim fileName As String = s(0)
Filling a TextBox
More often than not, drag-and-drop involves moving or copying text from various types of text boxes hosted by different applications, like the Office suite of applications. The TextBox component doesn’t natively support drag-and-drop, but can be extended in order to allow drops and to drag text out. Let’s see how to tweak a .NET Framework TextBox control so that it can import the text dragged from external applications.
It’s a three-step task: set AllowDrop to True on the TextBox; write a DragOver handler to control the drag-over process; and write a DragDrop handler to finalize the whole operation (see Listing 2).
In the DragOver event, first, check the type of data being dragged. If the drag-and-drop operation is not carrying plain text, the control refuses any drop. Otherwise, it determines the effect of the operation by looking at the state of the keyboard. The default effect, if allowed by the source, is Copy, meaning that the data is copied to the control and not removed at the source. If the CTRL key is held down, and the Move effect is allowed, the effect is set to Move.
If you observe how drag-and-drop works in Microsoft Word, you’ll notice that a little caret is displayed close to the mouse pointer to denote the insertion point (see Figure 1).
To obtain a similar effect on a plain TextBox control, you can physically move the caret to the character closest to the mouse pointer. The .NET Framework provides no built-in class or method to read caret information like the current row and column position. You also need to resort to a trick to set the caret position.
TextBox1.SelectionStart = charIndex
TextBox1.SelectionLength = 0
Set the SelectionStart property to an integer value that represents the 0-based index of the character in the control’s buffer. At the same time, set the length of the selection to 0. As a result, the caret is moved close to the specified character. How can you determine the index of the character at a certain mouse position? Luckily enough, the Win32 Edit window?the Win32 counterpart of the TextBox control?supports the EM_CHARFROMPOS message. This message takes in the coordinates of a point within the TextBox’s client area and returns the 0-based index of the nearest character. To send this message to the TextBox, you need to interop with the underlying Win32 infrastructure. The code in Listing 3 shows the interop .NET class that calls into a Win32 DLL.
The .NET Framework provides full support for calls directed at the Win32 SDK. No matter how optimized the interop layer is, minimizing the number of calls and the quantity of data being marshaled back and forth is an elementary rule of good programming. For this reason, I’ve written a small Win32 C++ DLL that exposes just one function: GetCaretInfo. The function takes the handle of the TextBox and returns a structure named CARETINFO (see Listing 3).
Internally, the function retrieves the screen coordinates of the point underneath the mouse and translates it into client coordinates in which the origin corresponds to the top-left corner of the TextBox. The coordinates are passed as an argument to the EM_CHARFROMPOS message and the index of the nearest character is returned. This information is packed into the CARETINFO structure and returned. Figure 2 shows how the caret follows the mouse pointer during a drag-and-drop operation.
Figure 2 shows data being dropped from Microsoft Word. As the yellow ListBox demonstrates, the same text is available in various formats, including rich text format and HTML. Which format you choose is completely application-specific. For example, if you’re dropping onto a RichTextBox control, select the RichTextFormat option.
Filling a PictureBox from a File
In the previous example, I only considered one possible output: plain text. But you can design controls that accept a variety of data types. For example, the TextBox can be further extended to accept a drop from the shell and display the contents of the file. I’ll demonstrate this feature for a PictureBox control.
If you try to set the AllowDrop property on the PictureBox control at design-time, you’re probably going to have a hard time. For some reason, the property is not listed in the Property Grid, although it’s perfectly supported. You must set it using code.
PictureBox1.AllowDrop = True
The DragOver event handler ensures that the data comes from the shell and contains just one file name.
If Not e.Data.GetDataPresent("FileNameW") Then
e.Effect = DragDropEffects.None
Return
End If
e.Effect = = DragDropEffects.Copy
The DragDrop event handler retrieves the file name and attempts to extract the image in it. Note that at this point, no check has been made on the type of file. It can certainly be a JPEG as well as a TXT or a DOC file. You can filter the file type in the DragOver event or accept any file and try to render its contents as an image.
If the file doesn’t contain an image, a default image is displayed, as in the leftmost image of Figure 3.
Note that if the image you assign to the PictureBox’s Image property is not a valid object (that is, the source file is not a graphic file), an exception is thrown. Furthermore, it is important that you explicitly open and close the source file. To minimize coding, you could resort to the following.
PictureBox1.Image = bmp.FromFile(fileName)
The functional effect is the same, but a significant drawback is just around the corner. The image file results lock (don’t respond to changes) until you terminate the application. The sample image, shown when the dropped file is not a valid file, is generated using a bunch of GDI+ code.
Public Function DummyImage() As Image
Dim bmp As New Bitmap(200, 100)
Dim g As Graphics = _ Graphics.FromImage(bmp)
g.DrawString("No Valid Drop.", _
New Font("Impact", 20), _
Brushes.DarkRed, 10, 10)
g.Dispose()
Return bmp
End Function
Dragging Text Out of a TextBox
It’s pretty common that a drag operation starts when a MouseDown event is fired. Note that this is only the most intuitive scenario, certainly not a rule. Any event could be used to initiate a drag-and-drop procedure. Also note that certain controls have custom drag-specific events, as is the case with the ListView and TreeView controls. These controls fire the ItemDrag event to notify you that a drag operation is in course.
If you want to extend an existing control with drag-and-drop functionality, the first step is identifying the start event. For dragging text out of a TextBox, choose the MouseDown event. Setting up a drag operation is, in theory, pretty straightforward. You write a handler for the start event, place a call to the DoDragDrop method that all Control and Form objects expose, and finalize the operation when the method returns and you know the outcome of the operation (copy, move, or whatever). Does this sound like fun? Well, it’s not for all controls. Try writing a MouseDown event handler for a TextBox and you’ll run into trouble right away.
The TextBox control, in fact, does a lot of work in the MouseDown window event. To implement drag capabilities, you need to override the default behavior regarding text selection. To accomplish this, you can’t just write handlers for known events; you really need to derive a new class and override the WndProc method. Overriding WndProc gives you enough power to control the message flow that makes each Windows Forms control reside and interact within the core Windows UI infrastructure. To write a successful WndProc override, you need to know a lot about Win32 SDK programming and messages.
Let’s define a new TextBox class.
Namespace Wintellect
Public Class TextBox
Inherits System.Windows.Forms.TextBox
End Class
End Namespace
You also need to import a few constants. I don’t know why Microsoft didn’t create an enum type with all the Win32 SDK messages exposed as mnemonic constants. At any rate, here are the definitions you need for the messages of interest. I’ve extracted them out of the winuser.h header file that you find in the Visual Studio 98 installation, for example.
Private Const WM_LBUTTONDOWN As Integer = &H201
Private Const WM_LBUTTONUP As Integer = &H202
Private Const WM_SETCURSOR As Integer = &H20
The WndProc method is defined as follows:
Overrides Sub WndProc(ByRef m As Message)
Select Case m.Msg
Case WM_LBUTTONDOWN
MyMouseDown(m)
Case WM_SETCURSOR
MySetCursor(m)
Case Else
MyBase.WndProc(m)
End Select
End Sub
You overrule the behavior of the TextBox in case a MouseDown message is received or a SetCursor. WM_LBUTTONDOWN arrives when the left mouse button is depressed, but before the mouse is moved or the button is released. WM_SETCURSOR is sent whenever the control needs to render the cursor. (The role of this message will be clear in a moment.) In all other cases, the control behaves as usual and the base WndProc method is invoked.
So what’s going on and what are the goals of this code? The idea is to enable the following scenario: the user selects some text and moves the mouse over the selected text. When this happens, the mouse pointer changes to an arrow. (It is an I-beam by default.) To implement this feature effectively, you need to handle the low-level WM_SETCURSOR message. Incidentally, this is also the behavior of Word when you drag-and-drop text within a document window. The code below illustrates the SetCursor handler.
Private Sub MySetCursor(ByVal m As Message)
If IsOverSelectedText(m.HWnd) Then
Cursor.Current = Cursors.Arrow
Else
Cursor.Current = Cursors.IBeam
End If
End Sub
The Message class represents the information associated with each Windows message. The HWnd property is the handle of the underlying window. In this case, it corresponds to the Handle property of the TextBox. IsOverSelectedText is a helper function that retrieves the current position of the mouse and compares it to the selected text. IsOverSelectedText exploits the IsOnSelectedText member of the CARETINFO structure, as illustrated in Listing 3.
Function IsOverSelectedText(ByVal hWnd As IntPtr)_
As Boolean
Dim ci As NativeMethods.CARETINFO
ci = NativeMethods.GetCaretInfo(hWnd)
Return ci.IsOnSelectedText
End Function
In Listing 4, you can see the source code of the WM_LBUTTONDOWN message handler. When the mouse button is depressed, notice whether the mouse is over the selected text. If not, do as usual and yield to the base WndProc method. Otherwise, you can detect whether or not a drag operation has been started. How do you do that? A drag operation is defined as any movement of the mouse from the click point that exceeds a system measure. The .NET Framework uses the SystemInformation.DragSize constant to indicate this value. Although undocumented in the .NET Framework, there’s a better way to detect a drag operation called a Win32 API function.
The function is named DragDetect and lives in the user32 DLL. The DragDetect function takes the handle of the window within which the drag takes place and the current mouse position. It returns a Boolean value. To simplify things and minimize data marshaling, I’ve defined a Win32 wrapper for this function: IsDragging. Here’s the C++ source code:
BOOL APIENTRY IsDragging(HWND hwndTextBox) {
// Get the current mouse position
POINT pt;
GetCursorPos(&pt);
return DragDetect(hwndTextBox, pt);
}
At this point, you’re pretty much done. What remains to do is just calling DoDragDrop and packing the information for the target. You pass the text currently selected in the TextBox.
' Capture the outcome of the drag operation
dropEffect = DoDragDrop(SelectedText, _
DragDropEffects.Copy)
DoDragDrop is the method that governs the whole drag operation. The method fires the proper events to the target and to the source as needed. Pass in the data (plain text, in this case) and the allowed effects (only copy, in this case). When the method returns, the operation has completed and you must update the source accordingly. You need to do nothing special if the drag ended in a copy. If it ends in a move, cut the selected text from the source TextBox.
To top off the code, consider that when you drop onto another control, you should make sure that the original selection is restored on the source. At the same time, this should be avoided if the drag-and-drop takes place within the context of the same control. Figure 4 shows a sample application with two TextBox controls that support drag-and-drop.
Dragging Between PictureBox Controls
Implementing drag-and-drop from within a TextBox control is more complicated than with other controls. This is because the TextBox control handles the MouseDown event on its own; subsequently, custom code must interact with that implementation. Supporting a drag operation from a PictureBox control is significantly easier.
Earlier in this article, I discussed how to drop a file name onto a PictureBox control and have it display the contained image, if any. Can you drop an in-memory bitmap too? And can you drag-and-drop images between PictureBox controls? Sure! Let’s see how.
Adding drop support to a PictureBox control for bitmap objects is as easy as checking an extra condition in the DragOver handler.
If e.Data.GetDataPresent("FileNameW") Or _
e.Data.GetDataPresent(DataFormats.Bitmap) Then
e.Effect = DragDropEffects.Copy
Return
End If
The dragged data object that contains a bitmap becomes an acceptable drop for a PictureBox control. When the drop occurs, you examine the data being carried and decide how to proceed.
If e.Data.GetDataPresent("FileNameW") Then
LoadPictureFromFile(e)
Return
End If
If e.Data.GetDataPresent(DataFormats.Bitmap) Then
LoadPictureFromBitmap(e)
Return
End If
Dragging a picture from a PictureBox control is easy too: you can blissfully choose the MouseDown event to start the operation. In this case, there are no drawbacks or caveats because the PictureBox control has no need to process the message and there’s no risk that you will break existing behaviors.
Sub PictBox_MouseDown(ByVal sender As Object, _ ByVal e As MouseEventArgs) _
Handles MyBase.MouseDown
Dim obj As New DataObject
obj.SetData(DataFormats.Bitmap, Me.Image)
obj.SetData(DataFormats.Metafile, Me.Image)
Dim eff As DragDropEffects
eff = DoDragDrop(obj, DragDropEffects.Copy)
End Sub
To pack data, you can use an instance of the DataObject class. This class provides a default (but recommended) implementation of the IDataObject interface. To add data, you call SetData one or more times, each time indicating a value and a data format. The same information can be stored in various formats to reach a wide range of clients. In the example above, the image is saved as a bitmap and metafile. This simple thing makes it possible for you to drag from the TextBox to a PowerPoint slide. Go ahead and believe!
Conclusion
Drag-and-drop is not reputed to be a feature that makes your application richer from a functional point of view. Perhaps this is a debatable point. What drag-and-drop does is make applications easier and more intuitive to use. In this article, I’ve discussed the basics of drag-and-drop in the .NET Framework, and focused on the members, the classes, and the model that makes it work. Next, I’ve provided a few practical examples including how to enhance TextBox and PictureBox controls to accept drops and drag their data out.
Although drag-and-drop can be easily implemented within the context of an application, my advice is to insulate the code in the body of individual controls by building a custom library of components that know how to accept this intuitive and immediate form of input.
- Remove From My Forums
-
Question
-
Hi All, Can anyone please point me in the right direction first of all if it is possible yet in creating Drag and Drop controls within my own applications i.e basically within my Client app…having my own toolbox of controls which i have created using custom controls and then be able to drag and drop them at runtime ?
if so does any one have pointers to tutorial materials or books on how i can go about doing this, i do have some idea on how it would work but quite limited, as i am researching this for now. thanks
Answers
-
Hi, ShadowOfTheBeast,
I think what you want is to drag and drop controls between forms,
I wrote a small sample to show that, and you can do more with this sample.
Here are two forms, the ToolBox with a PictureBox, and MainWindow
ToolBox Form
Code Snippet
public Form5()
{
InitializeComponent();
this.AllowDrop = true;
this.Text = «ToolBox»;
}
private void Form5_Load(object sender, EventArgs e)
{
Form6 form = new Form6();
form.Show();
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && e.Clicks == 1)
{
DoDragDrop(pictureBox1, DragDropEffects.Copy);
}
}
private void Form5_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
private void Form5_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetData(typeof(PictureBox)) is PictureBox)
{
PictureBox temp = (PictureBox)e.Data.GetData(typeof(PictureBox));
temp.Location = new Point(e.X — this.Location.X-temp.Width/2, e.Y — this.Location.Y-temp.Height/2);
this.Controls.Add(temp);
}
}
MainWindow Form
Code Snippet
public Form6()
{
InitializeComponent();
this.AllowDrop = true;
this.Text = «MainWindow»;
}
private void Form6_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
private void Form6_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetData(typeof(PictureBox)) is PictureBox)
{
PictureBox temp = (PictureBox)e.Data.GetData(typeof(PictureBox));
temp.Location = new Point(e.X — this.Location.X — temp.Width / 2, e.Y — this.Location.Y — temp.Height / 2);
this.Controls.Add(temp);
}
}
Hopes this helps,
Regards