Enable file drop onto a form

Started by dhilipkumar, Mar 27, 2009, 03:42 PM

Previous topic - Next topic

dhilipkumar

Enable file drop onto a form


If you have an html page saved on your hard disk, open a browser window and drop the html file onto it. The browser will display the page.

Dropping files on a form can be a handy feature sometimes, so we'll se how to enable our form to handle it.
As always, let's see the whole concept with an example.
I'll build an application with a RichTextBox that will be enabled to open two types of file: rich text format (.rtf) and plain text (.txt).
After you will have dragged the file on the form you will also be able to save it in its format (.rtf or .txt).
Neither other file types nor multiple file drop will be allowed.
This because I wanted to keep the application as simple as possible.

Ok, let's start with some declarations. We have 4 interesting controls on our form: a RichTextBox (contentRtb), a save .rtf button (rtfBtn), a save .txt button (txtBtn) and a CheckBox (wrapChk). There is also a label but it is not important.

We will begin handling the form's load event, just to do some setup:
private void mainForm_Load(object sender, EventArgs e) {
    // set a databinding between the richtextbox and the checkbox
    this.contentRtb.DataBindings.Add(new Binding("WordWrap", wrapChk, "Checked"));
     
    contentRtb.AllowDrop = true;
    contentRtb.DragEnter+=new DragEventHandler(contentRtb_DragEnter);
    contentRtb.DragDrop+=new DragEventHandler(contentRtb_DragDrop);
}

The first line of code has the purpose to setup a data binding between the WordWrap property of the contentRtb and the Checked property of the wrapChk. This is quite handy because now, when we check/uncheck the checkbox the richtextbox will set its WordWrap property accordingly to the Checked property of the checkbox. If we didn't do this, we would have had to write a handler for the wrapChk CheckedChanged event and set the contentRtb WordWrap property manually. Doing it with a data binding is just one line of code, nice and easy. The last three lines of code enable the contentRtb to accept file drop, then setup a couple of event handlers for the DragEnter event and for the DragDrop event.

When you drag a file and enter some control, the DragEnter event is fired, and when you release the mouse button within that control (during the drag action of course) the DragDrop event is fired.
In order to enable file drop you have to handle both of them separately.

Ok, so what we have to do? First of all, handle the DragEnter event. This is the code:
void contentRtb_DragEnter(object sender, DragEventArgs e) {
   if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
        e.Effect = DragDropEffects.Copy;
    } else {
        e.Effect = DragDropEffects.None;
    }


OSIX

dhilipkumar

Enable file drop onto a form


This is all we have to do here. Basically, if the data we're carrying with our drag&drop action is some file that is to be dropped, the if clause will evaluate to true. In that case we want the content of our draggin action to be copied to the control, and prepared to be handled. Otherwise we just do nothing setting the e.Effect property to DragDropEffects.None.

After the mouse button release we have to handle the DragDrop event:

void contentRtb_DragDrop(object sender, DragEventArgs e) {
    // verify data is of correct type
    if (e.Data.GetDataPresent(DataFormats.FileDrop)) {

        // get the filenames and store them in a string[] array
        string[] files=(string[])e.Data.GetData(DataFormats.FileDrop);

        // if more than one file dropped we reject the drop and alert the user
        if (files.Length>1) {
            MessageBox.Show("Please drop one file at a time.", "Too many files", MessageBoxButtons.OK, MessageBoxIcon.Information);
        } else if (files.Length==1) {
            try {
                FileInfo info = new FileInfo(files[0]);
                if (info.Extension == ".txt") {
                    contentRtb.LoadFile(files[0], RichTextBoxStreamType.PlainText);
                    txtBtn.Enabled = true;
                    rtfBtn.Enabled = false;
                } else if (info.Extension == ".rtf") {
                    contentRtb.LoadFile(files[0], RichTextBoxStreamType.RichText);
                    rtfBtn.Enabled = true;
                    txtBtn.Enabled = false;
                } else {
                    MessageBox.Show("Please drop only .txt or .rtf files", "Wrong file format", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            } catch {
                MessageBox.Show("An error occurred while trying to open the file", "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

    }
}


First of all we make sure that data is still present and in the correct format, as we did for the DragEnter event.
After that, we get the file names (along with the complete file paths) and store them in a string[] array. We could drag multiple files on the contentRtb but we want to handle just one file at a time, so we check how many strings are there in the array. If more than one we alert the user to drop just a single file on the contentRtb.
If there is just one file name, then ok, we can proceed.
Next move is to set up a FileInfo object to achieve some information about the file very quickly. We need to perform some checking on the extension since we're going to allow just .rtf and .txt file formats. So, if info.Extension is ".txt" we have a text file and we can load it in the contentRtb calling its LoadFile method. We also have to pass RichTextBoxStreamType.PlainText because the contentRtb likes to know how it has to handle the file. If we get a .txt file we want to edit it and maybe save it as another .txt file, so we enable just the txtBtn.
The same exact thing happens if we drop a .rtf file. The only difference is that we pass RichTextBoxStreamType.RichText to the LoadFile method and enable the rtbBtn.
If the file format is neither .txt nor .rtf we just alert the user and exit.
Notice that I put the code in a try{} catch{} block. You never know what could happen when dealing with files, so some precaution is always appreciated.

Once the DragDrop event handler exits the contentRtb will display the file contents. Now you can play with Word Wrap to eventually see appear/disappear the scrollbars if they're needed.

Ok, finally we would like to save the file.
Since we have two buttons we don't want to write the save file code twice, so I just wrote a SaveFile method which takes a RichTextBoxStreamType as argument and sets the SaveFileDialog.Filter property accordingly.

Here there are the two event handlers for the button click events and the SaveFile method:
private void rtfBtn_Click(object sender, EventArgs e) {
    SaveFile(RichTextBoxStreamType.RichText);
}

private void txtBtn_Click(object sender, EventArgs e) {
    SaveFile(RichTextBoxStreamType.PlainText);
}

private void SaveFile(RichTextBoxStreamType type) {
    SaveFileDialog sfd = new SaveFileDialog();
    sfd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
    sfd.OverwritePrompt = true;
    sfd.RestoreDirectory = true;
    if (type == RichTextBoxStreamType.PlainText) {
        sfd.Filter = "Text file (.txt) | *.txt";
    } else {
        sfd.Filter = "Rich Text Format (.rft) | *.rtf";
    }
    if (sfd.ShowDialog() == DialogResult.OK) {
        try {
            contentRtb.SaveFile(sfd.FileName, type);
        } catch {
            MessageBox.Show("Impossible to save the file", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }
}

If you press the txtBtn, the SaveFile method will be passed RichTextBoxStreamType.PlainText and RichTextBoxStreamType.RichText otherwise.
Once you enter the method it just prepares a SaveFileDialog, setting its initial directory to the user's physical desktop directory. I also want to be asked if I am overwriting a file and I want the Filter property to be set correctly so I can save just .txt and .rtf files, which won't lead to any problem since they are the only types we're dealing with. When the user clicks the ok button we just call the contentRtb.SaveFile method, passing it the file name along with the complete file path and the type. Another try{} catch{} is placed here for the same reason of the other one. If everything goes fine you should see the file appear where you decided to save it.