Xeidos
Xeidos

Reputation: 352

How to Drag&Drop Outlook-Mail into richTextBox

I got a problem with my WinForms-Application; I want to Drag&Drop a Outlook Mail into a RichTextBox. I found many articles about the Drag&Drop function but they all insert the Mailtext into the rTB (see: Link).Actually I can insert document like .txt, .jpg, Outlook-mails from desktop... to my program. My richTextBox automatic generate an image for the file and insert this image on a position. Like:

rTB.

After the user Drag and Drop the file a image will be created on the Drop position and if the user double click the image the file will be opened.

PROBLEM:

The program work fine, but if I try to drag a mail out of Outlook, the program insert the mailbody to the richTextBox and no as an image.

I have saved one Mail on the desktop and try to insert this mail to my program. The following output is given in my richTextBox (would be perfect):

Mailicon from desktop per Drag&Drop:

MailIconfromdesk

Otherwise I tried to Drag&Drop a mai from Outlook to my program and the following output is given (Just look at the text and not the images:

Mail from Outlook per Drag&Drop (THE PROBLEM!!!): InsertFromOutlook

The Programm insert the cc/mailadress and Mailbody to the rTB.

Here is the code behind: (My richTextBox is a own created richTextBox called "MyRichTextBox" Download the project under: link_RICHTEXTBOX. )

CODE

    private void Form1DragDrop(object sender, DragEventArgs e)
            {
                Startup();
               //Microsoft.Office.Interop.Outlook.ApplicationClass oApp =
               //      new Microsoft.Office.Interop.Outlook.ApplicationClass();
               Microsoft.Office.Interop.Outlook.Explorer oExplorer = _Outlook.ActiveExplorer();
               Microsoft.Office.Interop.Outlook.Selection oSelection = oExplorer.Selection;

               foreach (object item in oSelection)
               {
                   Microsoft.Office.Interop.Outlook.MailItem mi = (Microsoft.Office.Interop.Outlook.MailItem)item;

                   rTB_test.Text = mi.Body.ToString();

                    string mailName = "Mail\n" + (mailList.Count + 1);
                    // load an image with enough room at the bottom to add some text:
                    Image img = Image.FromFile(Imagepath);
                    // now we add the text:
                    int width = img.Width;
                    using (Graphics G = Graphics.FromImage(img))
                    using (Font font = new Font("Arial", 7f))
                    {
                        SizeF s = G.MeasureString(mailName, font, width);
                        G.DrawString(mailName, font, Brushes.Black, 
                            (width - s.Width) / 2, img.Height - s.Height - 1);

                    }
                    // adding the image is easy only if we use the clipboard..
                    Clipboard.SetImage(img);
                    // now insert image
                    rTB_test.Paste();
                    // now we can get a hashcode as a unique key..
                    // ..we select the image we have just inserted:
                    rTB_test.SelectionStart = rTB_test.TextLength - 1;
                    rTB_test.SelectionLength = 1;
                    // finally we need to store the mail itself with its key:
                    mailList.Add(rTB_test.SelectedRtf.GetHashCode(), mi);   
                    // cleanup: unselect and set cursor to the end:
                    rTB_test.SelectionStart = rTB_test.TextLength;
                    rTB_test.SelectionLength = 0;
            }
        Microsoft.Office.Interop.Outlook.Application _Outlook = null;

        Dictionary<int, Microsoft.Office.Interop.Outlook.MailItem> mailList =
  new Dictionary<int, Microsoft.Office.Interop.Outlook.MailItem>();

        private void rTB_test_DoubleClick(object sender, EventArgs e)
        {
            var ss = rTB_test.SelectionStart;
            var sl = rTB_test.SelectionLength;
            int hash = rTB_test.SelectedRtf.GetHashCode();
            // a few checks:
            if (sl == 1 && mailList.Keys.Contains(hash))
            {
                Microsoft.Office.Interop.Outlook.MailItem mi = mailList[hash];
                // do stuff with the msgItem..
                // ..
            }
        }

        void lbl_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            Microsoft.Office.Interop.Outlook.MailItem mi =
              (Microsoft.Office.Interop.Outlook.MailItem)((Label)sender).Tag;
            // code to process the doubleclicked mail item..
        }

        void Startup()
        {
            _Outlook = new Microsoft.Office.Interop.Outlook.Application();
        }

        private void Form1_DragEnter(object sender, DragEventArgs e)
        {
            e.Effect = DragDropEffects.Copy;
        }

After the user double click on the picture the mail should be opened in Outlookexplorer.

UPDATE

If I use the code from TaW´s answer the following output is given:

DragDropfromOutlook

After I double click the icon the mail won´t be open... So the code from the answer is just a "iconcreation". Thank you in advanced!

Upvotes: 4

Views: 673

Answers (1)

TaW
TaW

Reputation: 54453

Here is what I meant in my comments:

private void Form1DragDrop(object sender, DragEventArgs e)
{
   Startup();
   Microsoft.Office.Interop.Outlook.ApplicationClass oApp = 
         new Microsoft.Office.Interop.Outlook.ApplicationClass();
   Microsoft.Office.Interop.Outlook.Explorer oExplorer = _Outlook.ActiveExplorer();
   Microsoft.Office.Interop.Outlook.Selection oSelection = Explorer.Selection;

   foreach (object item in oSelection)          
   {
       Microsoft.Office.Interop.Outlook.MailItem mi = 
        (Microsoft.Office.Interop.Outlook.MailItem)item;
      //    rTB_test.Text = mi.Body.ToString();
      Label lbl = new Label();
      lbl.AutoSize = false;
      lbl.Size = new Size( 80, 50);         // <-- your choice!
      lbl.Text = someText;                 // <-- your choice!
      lbl.TextAlign = ContentAlignment.BottomCenter;
      lbl.Image = someImage;             // <-- your choice!
      lbl.ImageAlign = ContentAlignment.TopCenter;
      int count = rTB_test.Controls.Count;
      int itemsPerRow = rTB_test.Width / 80;
      lbl.Location = new Point( (count % itemsPerRow) * 80, 
                                 count / itemsPerRow * 50); 
      lbl.Tag = mi;               // store the data object
      lbl.MouseDoubleClick += lbl_MouseDoubleClick;
      lbl.Parent = rTB_test;     // add to the RTF's Controls
   }
}

void lbl_MouseDoubleClick(object sender, MouseEventArgs e)
{
   Microsoft.Office.Interop.Outlook.MailItem mi = 
     (Microsoft.Office.Interop.Outlook.MailItem) ( (Label)sender).Tag;
   // code to process the doubleclicked mail item..
}

These Labels will sit on top of any Text in the RTB without interfering with it. If you want to, you can modify the code for the Location to move them out of the way or style the Labels in many other ways..

Update

After the last remarks the problem get a lot clearer: other parts of the application are already adding mail icons to the RTB and we shall add more 'of the same'..

Adding an Image is best done via the clipboard; here is code that will do that:

// create some test text, maybe extract it from the mailheader?..
string mailName = "Mail\n" + (mailList.Count + 1);
// load an image with enough room at the bottom to add some text:
Image img = Image.FromFile(yourMailImageFile);
// make the images unique by embedding a counter in a bright pixel:
img = (Image)fingerPrintID((Bitmap)img, 250 - mailList.Count);      //*1*
// now we add the text:
int width = img.Width;
using (Graphics G = Graphics.FromImage(img))
using (Font font = new Font("Arial", 7f))
{
    SizeF s = G.MeasureString(mailName, font, width);
    G.DrawString(mailName, font, Brushes.Black, 
        (width - s.Width) / 2, img.Height - s.Height - 1);

}
// adding the image is easy only if we use the clipboard..
Clipboard.SetImage(img);
// insert only at the end!        
rTB_test.SelectionStart = rTB_test.TextLength;
rTB_test.SelectionLength = 0;
// now insert image
rTB_test.Paste();
// now we can get a hashcode as a unique key..
// ..we select the image we have just inserted:
rTB_test.SelectionStart = rTB_test.TextLength - 1;
rTB_test.SelectionLength = 1;
// retrieve the counter id:
string id = GetID(rTB_test.SelectedRtf);    //*2*
// finally we need to store the mail itself with its key:
mailList.Add(id, mi);   
// cleanup: unselect and set cursor to the end:
rTB_test.SelectionStart = rTB_test.TextLength;
rTB_test.SelectionLength = 0

We need to create a Dictionary to store our mails:

Dictionary<string, Microsoft.Office.Interop.Outlook.MailItem> mailList = 
  new Dictionary<string, Microsoft.Office.Interop.Outlook.MailItem>();  // *3*

Here is how we can access the mails in the DoubleClick event:

private void rTB_test_DoubleClick(object sender, EventArgs e)
{
    var ss = rTB_test.SelectionStart;
    var sl = rTB_test.SelectionLength;
    string id = GetID(sr);  //*4*
    // a few checks:
    if (sl == 1 &&  mailList.Keys.Contains(id) && sr.Contains(@"{\pict\") )
    {
       Microsoft.Office.Interop.Outlook.MailItem mi = mailList[id]; 
       // do stuff with the msgItem, e.g..
       mi.Display();

    }
}

Here is the result along with the image I use:

enter image description here mail

Note that in addition to adding the image we also store the both the mail data and the position of the image in the RTB in a Dictionary.

Update 2: I have replaced the position of the image as key with a HashCode of its RtfText; this is robust to any changes in the rest of the RTF's content. However, it relies on the images being unique, so adding an index to their text as in the code is recommended. (Or setting a few random pixels, maybe based on a GUID..)

Update 3 & 4: (*1* - *6*)

We found that we need to make the key resilient to several changes, like fonts surrounding the icon, which will influence the Rtf code, or the user enlarging the image.

Here is a FingerPrint function, which will make the images we add in unobtrusivly unique by setting a few pixels at the top of the image. Three to set a marker and one to set an ID:

Bitmap fingerPrintID(Bitmap bmp, int key)  //*5*
{
    for (int i = 0; i < 3; i++)
    {
        bmp.SetPixel(i, 0, Color.FromArgb(255, 238,238,238)); // EE EE EE
    }
    bmp.SetPixel(3, 0, Color.FromArgb(255, key, key, key));
    return bmp;
}

And to retrieve this function will pull the 3 hex digits as string from the RTF code:

string GetID(string Rtf)   //*6*
{
    int x = Rtf.IndexOf("eeeeeeeeeeeeeeeeee");  // 238 238 238
    if (x < 0) return "";
    string hex = Rtf.Substring(x +18, 6);
    return hex;
}

I chose the pixels to be rather bright; if you know which image you use, you can optimze the color choice even more.. With the new string id, we don't need the GetHashcode calls..

Upvotes: 1

Related Questions