views:

73

answers:

3

I'm having trouble with the concept of threads and how to use them.

I'm trying to code a fairly basic chat program (as part of a larger program) and it currently works like this:

The 'NetworkSession' class receives the input from the server on a separate thread in a loop. If it receives input that indicates it should open a new chat window it constructs a new WPF class (ChatWindow) and displays it.

Originally I got the error that "The calling thread must be STA, because many UI components require this.". So i set the thread to be STA but now of course the WPF form is unusable because its running on the same thread as the blocking loop.

So my question is how do I create a new instance of a WPF form from within another thread.

I've seen alot of discussion about this but it tends to deal with running a delegate from a form that has already been constructed.

Here is some code.

while (Connected) //this loop is running on its own thread
        {



            Resp = srReceiver.ReadLine();
            if (Resp.StartsWith("PING")) SendToServer("PONG");

            if (Resp.StartsWith("CHAT FROM"))
            {

                String[] split = Resp.Split(' ');
                Console.WriteLine("Incoming Chat from {0}", split[2]);
                bool found = false;
                if (Chats.Count != 0)
                {
                    foreach (ChatWindow cw in Chats)
                    {
                        if (cw.User == split[2])
                        {
                            found = true;
                            cw.AddLine(cw.User, split[3]); // a function that adds a line to the current chat
                        }

                    }
                }
                if (!found)
                {

                        ChatWindow temp = new ChatWindow(split[2], split[3]);
                        Chats.Add(temp); //this is a collection with T = ChatWindow
                        temp.Show();

                }

            }

        }
A: 

What you really need to do is construct the window/form on your main UI thread. You probably need to define a delegate that you can call from your network thread and that delegate should have a method attached that will call this.Dispatcher.BeginInvoke() -> inside which you will construct your window.

The this.Dispatcher.BeginInvoke() call is necessary to execute code on the UI thread, otherwise even with a delegate, code would be executed on the network thread.

Both the delegate and the method for creating a new chat window should probably be attached to the MainWindow...

Marko
+3  A: 

If you're constructing NetworkSession from your UI Thread, you can snag a reference to the current Dispatcher that can manipulate the UI later.

NetworkSession.cs

private Dispatcher _dispatcher;

public NetworkSession()
{
   _dispatcher = Dispatcher.CurrentDispatcher;
}

//any thread can call this method
public void DoStuff()
{
   Action action = () =>
   {
      ChatWindow temp = new ChatWindow(split[2], split[3]);
      Chats.Add(temp);
      temp.Show();
   };

   _dispatcher.BeginInvoke(action);
}
bufferz
A: 

The code below, which I took from here worked for me:

    public static void StartChatWindow()
    {
        Thread thread = new Thread(() =>
        {
            ChatWindow chatWindow = new ChatWindow();
            chatWindow.Chat(); // Do your stuff here, may pass some parameters

            chatWindow.Closed += (sender2, e2) =>
                // Close the message pump when the window closed
            chatWindow.Dispatcher.InvokeShutdown();

            // Run the message pump
            System.Windows.Threading.Dispatcher.Run();
        });

        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
    }
Amittai Shapira