0

I'm currently working on a WPF project that is trying to continuously update a listbox based on a network streamed source.

It is my understanding that a initializeComponent() method will only actually display the WPF window once the MainWindow() method has terminated. However I am trying to have a while(true) loop inside it in order to continuously listen for update signals from a server and update the listbox with appropriate values.

Each individual method is working fine, it's just that it doesn't open the WPF window in its current form due to the while loop. I am trying to avoid using a background update thread in order to update the list box because I am aware that I would have to implement functionality in order to pass the thread "ownership" of the listbox to the thread and I'm not 100% sure of how to do this.

Is there a workaround, or better yet, is there something obvious that I'm missing to achieve my required functionality?

The code is as follows:

public MainWindow()
{
    TcpClient client = new TcpClient();
    client.Connect(serverAddress, port);
    NetworkStream stream = client.GetStream();

    numberOfPumps = 0; //initialize as 0 on startup.

    handshake(stream);            

    InitializeComponent();

    updatePumpList(stream);
    updateListBox();

    while(true)
    {

        updatePumpList(stream);
        updateListBox();
    }           
}

The updateListBox() method is simply adding items to the listbox from a dictionary.

private void updateListBox()
{
    foreach(KeyValuePair<string, PumpItem> kvp in pumpDict)
    {
        pumpListBox.Items.Add(kvp.Key + ": " + kvp.Value.state);
    }
}
Noam M
  • 3,156
  • 5
  • 26
  • 41
James
  • 356
  • 2
  • 13
  • Running your application loop inside the constructor is generally not a good idea. At this point your controls aren't fully setup and if anything throws an exception you're probably boned. Updating a control in response to a background process isn't that difficult. Look up the `Dispatcher.Invoke` method for UI controls. – Chris Ryding Apr 17 '16 at 23:29
  • @ChrisRyding It's only just now that I realise that of course MainWindow() is the constructor of the MainWindow class instance. Obviously treating the constructor like a Main method is a horrible idea as the class will never...well... construct (in this case with an infinite while) Appreciated in helping me come to this realisation. – James Apr 17 '16 at 23:59

2 Answers2

1

Unfortunately, you just can't do it the way you want to. In a Windows application, you must let the main (UI) thread run. Any looping you do on that thread will hang the whole application until it's done. If the window's up, that looks like the window is frozen (because it is). If it's not up yet, it looks like a wait cursor, forever. No way around that. The thread has to be left alone to process input, update the window, etc. Even manually pumping the message loop (anybody remember MFC?) is a poor expedient. Windows applications work best if you leave the main thread to do its thing as the designers intended.

We do a lot of stuff on the main thread of course, but it's quick stuff that hands control back before the user notices any latency. Synchronous Internet access is never quick enough, and a polling loop that lasts for the lifetime of your process is out of the question.

You've got two options here, both of them pretty anodyne in practice.

You could use a DispatchTimer, with asynchronous internet access.

The other is the worker thread you're trying to avoid. They're not that bad. Just keep a reference to the Thread object around to abort on program shutdown, and the thread has to "invoke into" the UI thread when it does anything that'll touch any UI (including setting any property that will raise a PropertyChanged event).

It's not a big deal at all:

Action act = () => Status = newStatus;
App.Current.Dispatcher.Invoke(act);
Community
  • 1
  • 1
  • In the way I'm approaching this it seems that you are correct and I have to bite the bullet and use a worker thread. Working on it now – James Apr 18 '16 at 00:01
  • 1
    I've looked into this and can confirm it looks to be the best way. In it's current iteration, I'm using a background worker which as far as I can work out is a lightweight background thread and seems to be functioning as required. In terms of this answer, it's right on the money in that it's probably best to work with threads to update the UI using Invoke. – James Apr 18 '16 at 01:17
0

Have you considered setting up an onLoad event handler for your WPF window which will trigger once the WPF window is displayed? The event handler could then run your while loop accordingly.

Alternatively, you could have a timer primed to fire an event a few moments after the end of the constructor, allowing the window to display and then the while loop to begin.

Fraser G
  • 113
  • 1
  • 6