views:

53

answers:

3
using System;
using System.Windows.Forms;
using agsXMPP;
using System.Text;
namespace iTalk2
{
    public partial class Main : Form
    {
        agsXMPP.XmppClientConnection objXmpp;

        public Main()
        {
            InitializeComponent();
        }


        private void Form1_Load(object sender, EventArgs e)
        {

            Console.WriteLine("Logging in. Please wait...");
            Console.ReadLine();
            objXmpp = new agsXMPP.XmppClientConnection();
            agsXMPP.Jid jid = null;
            jid = new agsXMPP.Jid("username" + "@gmail.com");
            objXmpp.Password = "password";
            objXmpp.Username = jid.User;
            objXmpp.Server = jid.Server;
            objXmpp.AutoResolveConnectServer = true;

            try
            {
                objXmpp.OnMessage += messageReceived;
                objXmpp.OnAuthError += loginFailed;
                objXmpp.OnLogin += loggedIn;
                objXmpp.Open();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.ReadLine();
            }
        }

        private void messageReceived(object sender, agsXMPP.protocol.client.Message msg)
        {
            string[] chatMessage = null;
            chatMessage = msg.From.ToString().Split('/');
            agsXMPP.Jid jid = null;
            jid = new agsXMPP.Jid(chatMessage[0]);
            agsXMPP.protocol.client.Message autoReply = null;
            autoReply = new agsXMPP.protocol.client.Message(jid, agsXMPP.protocol.client.MessageType.chat, "This is a test");
            objXmpp.Send(autoReply);
        }

        private void loginFailed(object o, agsXMPP.Xml.Dom.Element el)
        {
            Console.WriteLine("Login failed. Please check your details.");
        }

        private void loggedIn(object o)
        {
            Console.WriteLine("Logged in and Active.");
            lblStatus.Text = "Online";
        }

        private void txtUsername_TextChanged(object sender, EventArgs e)
        {

        }

        private void label1_Click(object sender, EventArgs e)
        {

        }

        private void label2_Click(object sender, EventArgs e)
        {

        }

        private void txtPassword_TextChanged(object sender, EventArgs e)
        {

        }

        private void btnlogin_Click(object sender, EventArgs e)
        {

        }

    }

}

This code is not working. the function 'loggedIn(object o)' is not working. it says the lblStatus (which is a label) is on another thread. the error window says "Cross-thread operation not valid: Control 'lblStatus' accessed from a thread other than the thread it was created on." thanks in advance.

+1  A: 

WinForms is designed such that controls must only be manipulated on the UI-thread, the thread that runs the message-loop that manages the control.

Try this instead:

 private void loggedIn(object o)
 {
     Console.WriteLine("Logged in and Active.");
     Action act = () => lblStatus.Text = "Online";
     Invoke(act);   
 }

If your application is such that this method can be called on the UI thread or a separate worker thread, you'd be better off testing forInvokeRequired(simply: am I on the control's UI thread?) and dealing with the result appropriately. For example,

 private void loggedIn(object o)
 {
     if(InvokeRequired)
         Invoke(new Action<object>(loggedIn), o);         
     else
     {
        Console.WriteLine("Logged in and Active.");
        lblStatus.Text = "Online";          
     }
 }

Note that Invokewill block until the UI-update is completed. If you want something more fire-and-forget, use BeginInvokeinstead.

Ani
pls explain these two linesAction act = () => lblStatus.Text = "Online"; Invoke(act);
vishnu
That creates a delegate of type `Action` through a lambda-expression, and then Invokes it on the UI thread.
Ani
+1  A: 

You need to invoke a call on the UI thread. If you add code as follows at the top of the loggedIn method it should work:-

if(InvokeRequired)
{
    Invoke(new Action<object>(loggedIn), o);
    return;
}
kronoz
it worked. can you explain what is happening.
vishnu
Sure. In WinForm applications there is one thread that specifically deals with all the UI. All operations occur in that thread unless you fire off a separate thread to do something else. loggedIn is being called back on a different thread, so it has to get the UI thread to run it in order to update the label. InvokeRequired is a property of Forms that indicates whether you're on the UI thread or not (true if not), if so you have to Invoke the call on the original thread. As @Ani points out this blocks (i.e. doesn't finish until done), you *could* use BeginInvoke to do this in the background.
kronoz
(continuing from above...) but in this case, just updating a label, there seems no need, unless this other thread needs to do other work. Phew!
kronoz
A: 

When you start an application it is running from a single thread. This is the main thread, sometimes called the UI thread (since the UI will usually be rendered at startup and as a consequence it will be on that main thread.

Now, when you listen to events, your methods/delegates will get called from new threads. This is a consequence of the event based design. Normally this is not a problem unless you are trying to share data between two threads. This is exactly what happens with your UI elements. In this case your UI elements were created by your first thread but other threads are trying to update its value.

Given your design, you should check for IsInvokeRequired on the control and if so, use Invoke to set the new value. This will marshal your call from the new thread into the main thread that your UI is running on and will allow you to safely change the control.

Erik Noren