|
|||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionBelieve it or not, I've never written an application that's needed to use drag & drop. I'm working on something now where I'd like to use that feature, and so I went searching for a simple example of how d&d works. Amazingly, I couldn't find one--all the examples I found seemed to deal with tree controls or didn't deal specifically with how to use d&d. While simple, there are some things that I discovered when writing a little demonstration program, so I decided to write a beginner's tutorial on d&d. Creating A Simple Image ViewerI've decided to illustrate d&d with a simple, single image viewer capable of viewing JPG, BMP, and PNG files. The STAThread AttributeThe very first step is to ensure that your [STAThread]
static void Main()
{
Application.Run(new Form1());
}
The AllowDrop PropertyThe first step is to identify the control for which you want to enable d&d and set the
But what if you want to enable d&d for a particular control, say a
Nor, if you have: PictureBox pb=new PictureBox();
and you start typing: pb.Allow
will Intellisense auto-complete. Yet, this doesn't mean that you can't manually assign this property! It seems like the In any case, since the demonstration application doesn't do anything else besides display an image, it makes sense in this case to enable d&d for the form rather than just the The Drag And Drop EventsThe next step is to wire-up the d&d events (note that the Since we're adding d&d capability to the form, we can use the designer to add our methods for the four events we're almost always interested in:
The DragEnter EventThis is simple enough--this event fires when the mouse enters a control in which d&d is enabled. It gives you the opportunity to:
DragEventArgsThe
In my example, I'm interested in two things:
and in particular, the application accepts images dragged from Explorer, so I'm interested in the filename. As a result, the private void OnDragEnter(object sender, System.Windows.Forms.DragEventArgs e)
{
Debug.WriteLine("OnDragEnter");
string filename;
validData=GetFilename(out filename, e);
where all the real work is done in the protected bool GetFilename(out string filename, DragEventArgs e)
{
bool ret=false;
filename=String.Empty;
if ( (e.AllowedEffect & DragDropEffects.Copy) == DragDropEffects.Copy)
{
Array data=((IDataObject)e.Data).GetData("FileName") as Array;
if (data != null)
{
if ( (data.Length ==1) && (data.GetValue(0) is String) )
{
filename=((string[])data)[0];
string ext=Path.GetExtension(filename).ToLower();
if ( (ext==".jpg") || (ext==".png") || (ext==".bmp") )
{
ret=true;
}
}
}
}
return ret;
}
First, I test whether the source allows copying of the data. Second, I get the data based on the filename. When dropping an image, there are several formats that the data supports, which can be inspected in the debugger:
We see that "FileName" is one of the acceptable formats, so I'm going to request that data in the FileName format. However, we're not done yet. The object returned is actually an
So, what I want to do is: verify that there is only one datum being dropped (rather than a collection of filenames) and, while probably overkill, I want to verify that the data type of this one and only one entry is a Managing The DataNow that we have the filename, what do we do with it? In my application, I'd like to display a small image to give the user some feedback, showing the picture about to be dropped onto the application. This is a good example that illustrates that sometimes you'll have to manage the data intelligently. I wanted to deal with two problems:
The rest of the private void OnDragEnter(object sender, System.Windows.Forms.DragEventArgs e)
{
Debug.WriteLine("OnDragEnter");
string filename;
validData=GetFilename(out filename, e);
if (validData)
{
if (lastFilename != filename)
{
thumbnail.Image=null;
thumbnail.Visible=false;
lastFilename=filename;
getImageThread=new Thread(new ThreadStart(LoadImage));
getImageThread.Start();
}
else
{
thumbnail.Visible=true;
}
e.Effect=DragDropEffects.Copy;
}
else
{
e.Effect=DragDropEffects.None;
}
}
Notice that I first check whether image being displayed is the one again being dragged onto the application. If so, we have the image and we're all set to display the thumbnail (there's a potentially small bug in this code--can you figure it out?) If it's a new image, I'm going to start up a thread that actually loads the image and puts it into the thumbnail when it's ready. Let's look at that thread and related functions: public delegate void AssignImageDlgt();
protected void LoadImage()
{
nextImage=new Bitmap(lastFilename);
this.Invoke(new AssignImageDlgt(AssignImage));
}
protected void AssignImage()
{
thumbnail.Width=100;
// 100 iWidth
// ---- = ------
// tHeight iHeight
thumbnail.Height=nextImage.Height * 100 / nextImage.Width;
SetThumbnailLocation(this.PointToClient(new Point(lastX, lastY)));
thumbnail.Image=nextImage;
}
The image is loaded into The DragOver EventSomething I didn't realize about the private void OnDragOver(object sender, System.Windows.Forms.DragEventArgs e)
{
Debug.WriteLine("OnDragOver");
if (validData)
{
if ( (e.X != lastX) || (e.Y != lastY) )
{
SetThumbnailLocation(this.PointToClient(new Point(e.X, e.Y)));
}
}
}
Again, we're calling a separate method to actually set the thumbnail position: protected void SetThumbnailLocation(Point p)
{
if (thumbnail.Image==null)
{
thumbnail.Visible=false;
}
else
{
p.X-=thumbnail.Width/2;
p.Y-=thumbnail.Height/2;
thumbnail.Location=p;
thumbnail.Visible=true;
}
}
This method determines the thumbnail position and its visibility, based on whether the The DragLeave EventWhen the mouse leaves our application, we want to hide any visual cues that are currently being displayed: private void OnDragLeave(object sender, System.EventArgs e)
{
Debug.WriteLine("OnDragLeave");
thumbnail.Visible=false;
}
The DragDrop EventIn this event handler, we want to verify that the data being dropped is valid. This is easy, using the private void OnDragDrop(object sender, System.Windows.Forms.DragEventArgs e)
{
Debug.WriteLine("OnDragDrop");
if (validData)
{
while (getImageThread.IsAlive)
{
Application.DoEvents();
Thread.Sleep(0);
}
thumbnail.Visible=false;
image=nextImage;
AdjustView();
if ( (pb.Image != null) && (pb.Image != nextImage) )
{
pb.Image.Dispose();
}
pb.Image=image;
}
}
After we are ensured that the worker thread has terminated, I'm adjusting the application's ConclusionWhile drag and drop is itself simple, some planning has to go into:
These are things that I think are useful to bring to a beginner's attention. It's not so simple after all!
|
||||||||||||||||||||||||||||||