Reputation: 652
The use case: users need the ability to drag & drop email items from Outlook onto a form in my WinForms (.Net 4) application. The application saves these items in .msg format and stores them in a predetermined location.
The problem: my code is not robust against drag-drop from other sources (e.g. dragging a jpeg from IE onto the form triggers the same event). This is because I cannot determine whether the dragged item is an Outlook object, or what source the dragged item(s) came from.
Is there a workaround such that I can only accept drag-drop items of a particular type? Here is my code in the DragDrop event handler:
Outlook.Application outlook = new Outlook.Application();
Outlook.Selection sel = outlook.ActiveExplorer().Selection;
try
{
foreach (object item in sel)
{
if (item is Outlook.MailItem)
{
var mail = item as Outlook.MailItem;
CopyMailItemToLocalTempDir(mail);
txtComment.Text = "Email from " + mail.SenderName + " Regarding " + mail.Subject;
}
}
}
finally
{
// This is hokey but it prevents Outlook from remembering previously selected items
// - refer http://stackoverflow.com/questions/14090420/interop-outlook-doesnt-clear-selected-mails-at-drag-and-drop
var folder = outlook.ActiveExplorer().CurrentFolder;
outlook.ActiveExplorer().CurrentFolder = outlook.GetNamespace("MAPI").GetDefaultFolder(Outlook.OlDefaultFolders.olFolderContacts);
Thread.Sleep(50);
outlook.ActiveExplorer().CurrentFolder = folder;
Marshal.ReleaseComObject(sel);
Marshal.ReleaseComObject(outlook);
sel = null;
outlook = null;
}
Some details on the DragEventArgs object (e) when dragging from Outlook:
e.Data.GetFormats() returns:
{string[15]}
[0]: "RenPrivateSourceFolder"
[1]: "RenPrivateLatestMessages"
[2]: "RenPrivateMessages"
[3]: "RenPrivateItem"
[4]: "FileGroupDescriptor"
[5]: "FileGroupDescriptorW"
[6]: "FileDrop"
[7]: "FileNameW"
[8]: "FileName"
[9]: "FileContents"
[10]: "Object Descriptor"
[11]: "System.String"
[12]: "UnicodeText"
[13]: "Text"
[14]: "CSV"
e.Data.GetData("Text") returns:
"From\tSubject\tReceived\tSize\tCategories\t\r\nJoe Sender\tThis is the email subject\t10:51 AM\t3 KB\t\t"
Upvotes: 11
Views: 4618
Reputation: 529
Is there a workaround such that I can only accept drag-drop items of a particular type?
(When working with single files):
if (e.Data.GetData(typeof(...)) is ...)
//process it
and
if (e.Data.GetDataPresent(typeof(...)))
//process it
Since you have not specified the type to be Outlook.MailItem
(or maybe Image
) but the source: I assume your program fails on other parts of the code (due to bad design). E.g. ActiveExplorer().Selection
in 2nd line or in the finally {}
part. This is easily manageable by prefiltering/try-catching in place (before involving outlook) or in the DragEnter
event (better when the type is strictly some type of outlook).
To master drag & drop stuff, check out this.
or for special types like outlook messages, you already have a good solution with plenty of explanations in the article you mentioned.
I also found an answer to your non-general (outlook only) use case (wrote in 2012) that exhibits OutlookDataObject class and probably based on the article you mentioned (wrote in 2008).
Upvotes: 1
Reputation: 11577
i have here the source code of solution that allow to drop only outlook items. here are the event handlers:
private void Form1_DragEnter(object sender, DragEventArgs e)
{
//display formats available
this.label1.Text = "Formats:\n";
foreach (string format in e.Data.GetFormats())
{
this.label1.Text += " " + format + "\n";
}
//ensure FileGroupDescriptor is present before allowing drop
if (e.Data.GetDataPresent("FileGroupDescriptor"))
{
e.Effect = DragDropEffects.All;
}
}
private void Form1_DragDrop(object sender, DragEventArgs e)
{
//wrap standard IDataObject in OutlookDataObject
OutlookDataObject dataObject = new OutlookDataObject(e.Data);
//get the names and data streams of the files dropped
string[] filenames = (string[])dataObject.GetData("FileGroupDescriptorW");
MemoryStream[] filestreams = (MemoryStream[])dataObject.GetData("FileContents");
this.label1.Text += "Files:\n";
for (int fileIndex = 0; fileIndex < filenames.Length; fileIndex++)
{
//use the fileindex to get the name and data stream
string filename = filenames[fileIndex];
MemoryStream filestream = filestreams[fileIndex];
this.label1.Text += " " + filename + "\n";
//save the file stream using its name to the application path
FileStream outputStream = File.Create(filename);
filestream.WriteTo(outputStream);
outputStream.Close();
}
}
Upvotes: 1