Introduction
If we have a Windows Forms application which has File -> Open functionality, adding a
few lines of code allows the user to open files dragged from the Windows Explorer. This
article describes how to add this feature to your application.
Adding Drag-and-Drop capability to an existing Windows Forms application
Suppose we have simple image viewer program (demo project DropFiles
).
When tje user selects File -> Open, it shows the 'Open File' dialog:
private void mnuFileOpen_Click(object sender, System.EventArgs e)
{
OpenFileDialog openDlg = new OpenFileDialog();
openDlg.Filter = "Jpeg files (*.jpg)|*.jpg|Bitmap files (*.bmp)|"
+ *.bmp|All Files (*.*)|*.*";
openDlg.FileName = "" ;
openDlg.CheckFileExists = true;
openDlg.CheckPathExists = true;
if ( openDlg.ShowDialog() != DialogResult.OK )
return;
OpenFile(openDlg.FileName);
}
The function
OpenFile
loads the image file to a class member
Bitmap
m_Bitmap
, and the
Paint
event handler shows this bitmap on the
screen:
private Bitmap m_Bitmap;
private void OpenFile(string sFile)
{
Bitmap bmp;
try
{
bmp = (Bitmap)Bitmap.FromFile(sFile, false);
if ( bmp != null )
{
m_Bitmap = (Bitmap) bmp.Clone();
this.AutoScroll = true;
this.AutoScrollMinSize = new Size (
m_Bitmap.Width,
m_Bitmap.Height);
Invalidate();
}
}
catch (Exception ex)
{
MessageBox.Show(
this,
ex.Message,
"Error loading from file");
}
}
private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
if ( m_Bitmap != null )
{
e.Graphics.DrawImage(
m_Bitmap,
new Rectangle(this.AutoScrollPosition.X,
this.AutoScrollPosition.Y,
m_Bitmap.Width,
m_Bitmap.Height));
}
}
To open files dropped from the Windows Explorer we need to set the form
AllowDrop
property to
true
. After this add the
DragEnter
and
DragDrop
event handlers.
DragEnter
handler is simple - program accepts only files:
private void Form1_DragEnter(object sender, System.Windows.Forms.DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
e.Effect = DragDropEffects.Copy;
else
e.Effect = DragDropEffects.None;
}
The DragEventArgs.GetData
function returns an IDataObject
interface that contains the data associated with this DragDrop
event. When a file or files are dropped from Explorer to the form, IDataObject
contains an array of file names. If one file is dropped, the array contains one
element. All the time the DragDrop
event handler is working, the Windows
Explorer instance is not responding. Therefore, we need to call the OpenFile
function asynchronously. This is done using the BeginInvoke
function and
a delegate pointing to OpenFile
. It is a good idea also to move the
form to the foreground after dropping file to it.
private delegate void DelegateOpenFile(String s); private DelegateOpenFile m_DelegateOpenFile;
private void Form1_Load(object sender, System.EventArgs e)
{
m_DelegateOpenFile = new DelegateOpenFile(this.OpenFile);
}
private void Form1_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
{
try
{
Array a = (Array)e.Data.GetData(DataFormats.FileDrop);
if ( a != null )
{
string s = a.GetValue(0).ToString();
this.BeginInvoke(m_DelegateOpenFile, new Object[] {s});
this.Activate(); }
}
catch (Exception ex)
{
Trace.WriteLine("Error in DragDrop function: " + ex.Message);
}
}
This is all we need to implement Drag-and-Drop capability in Windows Forms
application. However, good programming style requires writing the wrapper class
which encapsulates this feature.
Using the DragDropManager class
The second demo project
DropFiles1
opens files dropped from
Explorer using helper class
DragDropManager
. The project is very
simple - it allows you to open one or more files and shows the file names in listbox. It
has
mnuFileOpen_Click
function which shows Open File dialog and
calls
OpenFiles(Array a)
passing array of file names selected by
user.
Form that uses DragDropManager
should implement IDropFileTarget
interface defined in the same file as DragDropManager
:
public interface IDropFileTarget
{
void OpenFiles(System.Array a);
}
So, main form is derived from
IDropFileTarget
:
public class Form1 : System.Windows.Forms.Form, IDropFileTarget
and has OpenFiles function which opens files selected from File - Open dialog or
dropped from Windows Explorer:
public void OpenFiles(Array a)
{
string sError = "";
string sFile;
lstFiles.Items.Clear();
for ( int i = 0; i < a.Length; i++ )
{
sFile = a.GetValue(i).ToString();
FileInfo info = new FileInfo(sFile);
if ( ! info.Exists )
{
sError += "\nIncorrect file name: " + sFile;
}
else
{
lstFiles.Items.Add(sFile);
}
}
if ( sError.Length > 0 )
MessageBox.Show(this, sError, "Open File Error");
}
The Form has the member
private DragDropManager m_DragDropManager;
which is initialized in
Load
event handler:
private void Form1_Load(object sender, System.EventArgs e)
{
m_DragDropManager = new DragDropManager();
m_DragDropManager.Parent = this;
}
So, the steps that should be done for using
DragDropManager
class
in Windows Form are:
- Derive the form from
IDropFileTarget
interface;
- Implement
IDropFileTarget
interface adding function
OpenFiles(Array
a)
;
- Add member of
DragDropManager
type to the form;
- Initialize it setting
Parent
property to form reference.
Class DragDropManager
This class keeps reference to owner form in class member and has delegate used for
asynchronous call to the form:
private Form m_parent;
private delegate void DelegateOpenFiles(Array a); private DelegateOpenFiles m_DelegateOpenFiles;
Initialization is done when client sets
Parent
property:
public Form Parent
{
set
{
m_parent = value;
if ( ! ( m_parent is IDropFileTarget ) )
{
throw new Exception(
"DragDropManager: Parent form doesn't implement IDropFileTarget interface");
}
m_DelegateOpenFiles = new DelegateOpenFiles(((IDropFileTarget)m_parent).OpenFiles);
m_parent.AllowDrop = true;
m_parent.DragEnter += new System.Windows.Forms.DragEventHandler(this.OnDragEnter);
m_parent.DragDrop += new System.Windows.Forms.DragEventHandler(this.OnDragDrop);
}
}
OnDragEnter
and
OnDragDrop
are subscribed to owner
form
DragEnter
and
DragDrop
events:
private void OnDragEnter(object sender, System.Windows.Forms.DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
e.Effect = DragDropEffects.Copy;
else
e.Effect = DragDropEffects.None;
}
private void OnDragDrop(object sender, System.Windows.Forms.DragEventArgs e)
{
try
{
Array a = (Array)e.Data.GetData(DataFormats.FileDrop);
if ( a != null )
{
m_parent.BeginInvoke(m_DelegateOpenFiles, new Object[] {a});
m_parent.Activate(); }
}
catch (Exception ex)
{
Trace.WriteLine("Error in DragDropManager.OnDragDrop function: " + ex.Message);
}
}
Acknowledgements
- Using helper class with subscribing to owner form events. Joel Matthias.
http://www.codeproject.com/csharp/restoreformstate.asp
- Using form AutoScroll properties. Christian Graus.
http://www.codeproject.com/cs/media/csharpgraphicfilters11.asp