tags:

views:

60

answers:

2

I am creating an email software which send email to some accounts. I want to append text every time new email is sent or failed. But textbox shows me report after sending all emails. If the toList is very large like 30+ emails the app screen goes white and after sending all emails GUI comes back with the updated OutPutTextBox. Here is the code inside the SendButton_Click method

foreach (String to in toList)
            {
                bool hasSent = SendMail(from, "password", to, SubjectTextBox.Text, BodyTextBox.Text);

                if (hasSent)
                {
                    OutPutTextBox.appendText("Sent to: " + to);
                }
                else
                {
                    OutPutTextBox.appendText("Failed to: " + to);
                }
            }
+3  A: 

What you actually want to do is invoke SendMail asynchronously. There are several ways to do this in .NET 4.0. I recommend starting a Task object in your foreach loop, and scheduling a task continuation for each one to the UI thread:

string subject = SubjectTextBox.Text;
string body = BodyTextBox.Text;
var ui = TaskScheduler.FromCurrentSynchronizationContext();
List<Task> mails = new List<Task>();
foreach (string to in toList)
{
  string target = to;
  var t = Task.Factory.StartNew(() => SendMail(from, "password", target, subject, body))
      .ContinueWith(task =>
      {
        if (task.Result)
        {
          OutPutTextBox.appendText("Sent to: " + to); 
        }
        else
        {
          OutPutTextBox.appendText("Failed to: " + to); 
        }
      }, ui);
  mails.Add(t);
}

Task.ContinueWhenAll(mails.ToArray(), _ => { /* do something */ });

(syntax might be slightly off; I didn't compile it).

Stephen Cleary
any code snippet?
coure06
See updated answer.
Stephen Cleary
couple of issue with this solution. First its sending all emails to same address. Second how to know all Tasks are finished?
coure06
(1) - my bad; see updated answer. (2) - use `Task.ContinueWhenAll`; see updated answer.
Stephen Cleary
A: 

This seems to work (assumes a WinForm application):

namespace WindowsFormsApplication5
{
    using System;
    using System.Threading;
    using System.Windows.Forms;

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        TextBox OutPutTextBox = new TextBox();

        /// <summary>
        /// Represents Send Mail information.
        /// </summary>
        class MailInfo
        {
            public string from;
            public string password;
            public string to;
            public string subject;
            public string body;
        }

        void ProcessToDoList( string[] toList )
        {
            foreach ( var to in toList )
            {
                MailInfo info = new MailInfo();
                info.from = "xx"; //TODO.
                info.password = "yy"; //TODO.
                info.to = "zz"; //TODO.
                info.subject = "aa"; //TODO.
                info.body = "bb"; //TODO.
                ThreadPool.QueueUserWorkItem( this.SendMail, info );
            }
        }

        /// <summary>
        /// Send mail.
        /// NOTE: this occurs on a separate, non-UI thread.
        /// </summary>
        /// <param name="o">Send mail information.</param>
        void SendMail( object o )
        {
            MailInfo info = ( MailInfo )o;
            bool hasSent = false;

            //TODO: put your SendMail implementation here. Set hasSent field.
            //

            // Now test result and append text.
            //

            if ( hasSent )
            {
                this.AppendText( "Sent to: " + info.to );
            }
            else
            {
                this.AppendText( "Failed to: " + info.to );
            }
        }

        /// <summary>
        /// Appends the specified text to the TextBox control.
        /// NOTE: this can be called from any thread.
        /// </summary>
        /// <param name="text">The text to append.</param>
        void AppendText( string text )
        {
            if ( this.InvokeRequired )
            {
                this.BeginInvoke( ( Action )delegate { this.AppendText( text ); } ); 
                return;
            }

            OutPutTextBox.AppendText( text );
        }
    }
}
Adel Hazzah