Saturday, May 21, 2005

On Dragging and Dropping . . .

I like to play with lots of things at once - and one is a tool to easily manage my textures. Now the first thing I desired was drag and drop functionality. I found this to be a little tricky, I couldn't find too much documentation that helped but with a little experimenting here is what I got. This particular example is for loading PNG but could be trivially modified to support GIF, JPG and BMP - possible others too (whatever the windows Bitmap format supports)

The Plan

I want to drag images to my program and have them displayed. If it's anything other than .png nothing's displayed and the users cursor goes to the "no entry" sign.

To use drag and drop for a task such as this there are a few things that matter.

AllowDrop this should be set to true. I think it's present on any component so if you wish to add drag&drop to a button or some little icon or a picture box or . . . whatever - it's no problem! The important thing is you set it to true.

Next the Events - we only care about 2.

DragOver this is when you've dragged something and you're moving it over your control. Here you should report to the user - if it is something acceptable or not acceptable. (The cursor has the no-entry sign for NO! or it has a little box for YES!)

DragDrop we've let go in the control area so now do something magic with the thing we've dropped.

The Simplest Program Possible

Start a new windows project.

Now hop to the form designer.

Select a picture box and drag it on to the form. Go to the picture box properties section and under docking select Fill.

Okay a nice big grey box. Just what we've always wanted. We want to get some drag drop action going so let's set the AllowDrop bool. Now - I couldn't find this in the properties form-designer explorer! Maybe in later versions of it's there. So I added it manually into the pregenerated code.

private void InitializeComponent()

//A lot of windows generated code

// pictureBox1
this.pictureBox1.AllowDrop = true;
this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this.pictureBox1.Location = new System.Drawing.Point(3, 16);
this.pictureBox1.Name = "pictureBox1";


With that added we'll now potentially be able to accept stuff dragged over to our form (well picture box specifically but it does take all the form-real-estate!). Next to events. So for this let's hop back to the form designer.

Click on the little lightening arrow for events and double click the space next to DragOver. This will take us into the programming section.

Programming OnDragOver

private void [Your Project Name]_DragOver(object sender, System.Windows.Forms.DragEventArgs e)

In this function we check what's being dragged in and whether or not we like it. Then we tell the computer our general feelings. So we want to check several things.

  1. Is it a file from windows-explorer (not the webbrowser :P, the file manager)?

  2. Does it have a file path associated with it?

  3. Does it use the .png extension?

  4. Will windows allow us to copy it?

We find this all out using DragEventArgs e this contain lots of useful stuff. There's a GetFormats method that allows us to check if the data being dragged can be converted into what we want. This function is obviously poorly named - it should be IsFormat. Because that's what it's used for. IsFormat(bitmap) for instance. But for an unknown reason it's called GetFormats.

So with this we'll check two things. If it's coming from Windows - this is pretty easy and we use one of preset formats, the second if it can be converted to file name. This is a little harder, I only discovered this format name by trial and error - though I'm sure it's documented somewhere. Enough chat let's see the code.

//Are we dragging a file in
if( e.Data.GetDataPresent(DataFormats.FileDrop) &&

So we ask Is it from Windows? and CanWeGetAFileName from it. The W at the end of FileName might be for Windows I don't really know. You'd imagine the filename would just be a string but confusingly it's an array of strings! At position 0 though is the final name. I didn't check the other positions so who knows what mysteries they might hold.

The next thing to check are: Is it a PNG and Can we make a copy of it?. Not too hard!

if((e.AllowedEffect & DragDropEffects.Copy) != 0)
string[] filePathInfo = (string[]) e.Data.GetData("FileNameW", true);

if(filePathInfo.Length == 0)

e.Effect = DragDropEffects.Copy;

So the first IF statement is querying the dataobject and asking if it's okay to copy it. Does it contain the DragDropEffects.Copy flag? The we convert the data that's being dragged into an array of strings. We check the string array isn't empty and then take the first string of the array - which should be the file path. The final check is does it end with .png? - if it does, well then we're cooking.

Finally - the final line, where we're giving the go ahead for copying!

e.Effect = DragDropEffects.Copy;

We send back what we want to do with the thing be dragged. In our case we'll be copying it (rather than say moving it). Setting this changes the mouse cursor to something appropiate. That's our entire moving over function - cool heh!

Drag Drop

So the next and last event is when the user actually releases what's being dragged in the correct place. Then we want to load whatevers dragged up into the picture box. Pop back to the form designer, go to Events using the lightening bolt icon. Then scroll down to DragDrop. Double click the empty box next to it and wait to be transported to the code with the correct skeleton in place.

I'm going to throw down the code all at once. Because it's not complicated!

private void pictureBox1_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
string[] filePathInfo = (string[]) e.Data.GetData("FileNameW", true);
Bitmap bitmap =
new Bitmap(filePathInfo[0]);
this.pictureBox1.Image = bitmap;

And that's all there is to it. Drag and drop to your hearts content.

Post Scriptum

I know why it's an array of strings now! Well actually this is a guess but I assume it's if you grab a load of images than drag and drop them. This is very worth supporting too.

No comments: