0

I' running a WPF application and want to do something like this:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        TextBox textBox = new TextBox();
        mainGrid.Children.Add(textBox);
        textBox.Text = "one";
        Thread.Sleep(1000);
        textBox.Text = "two";
        Thread.Sleep(1000);
        textBox.Text = "three";
        Thread.Sleep(1000);
        textBox.Text = "four";
    }
}

The display doesn't load until all of this processing is complete hence I have a blank, unresponsive application till 3 seconds and a textbox with the word four after running the above code.

I had a look at the BackgroundWorker class and tried this:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        // Set up background worker object & hook up handlers
        BackgroundWorker backgroundWorker;
        backgroundWorker = new BackgroundWorker();
        backgroundWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
        backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);

        backgroundWorker.RunWorkerAsync();
    }

    private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
        {
            TextBox textBox = new TextBox();
            mainGrid.Children.Add(textBox);
            textBox.Text = "one";
            Thread.Sleep(1000);
            textBox.Text = "two";
            Thread.Sleep(1000);
            textBox.Text = "three";
            Thread.Sleep(1000);
            textBox.Text = "four";
        }));
    }

    private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Debug.WriteLine("done");
    }

Still the same behaviour. I want to run some tasks in the background/ in different threads which will make changes to the UI elements along execution and I want these changes to be reflected in the display immediately.

Nitin Labhishetty
  • 1,290
  • 2
  • 21
  • 41

3 Answers3

4

Consider using async and await instead:

private async void Window_Loaded(object sender, RoutedEventArgs e)
{
    TextBox textBox = new TextBox();
    mainGrid.Children.Add(textBox);
    textBox.Text = "one";
    await Task.Delay(1000);
    textBox.Text = "two";
    await Task.Delay(1000);
    textBox.Text = "three";
    await Task.Delay(1000);
    textBox.Text = "four";
}

Here is a rather nice tutorial on the subject.

Mike Eason
  • 9,525
  • 2
  • 38
  • 63
  • I'm trying to understand how async and await work. I found this answer: http://stackoverflow.com/a/7615440/2136312 But didn't understand this, the program does wait for the asynchronous call to be completed. Then how is this different from regular waiting? why does adding async make the GUI responsive? – Nitin Labhishetty Jul 07 '15 at 09:58
  • In a nutshell, async/await programming is just an *easier* way of writing `Tasks`. It allows the UI to remain responsive by running tasks in the background. Once the task has completed, it will continue on with the code. See [here](https://msdn.microsoft.com/en-us/library/hh191443.aspx) for a more in depth explanation. – Mike Eason Jul 07 '15 at 10:05
1

I agree with the previous posters however if you'd like to use background worker consider taking a look at this tutorial: http://www.wpf-tutorial.com/misc/multi-threading-with-the-backgroundworker/

[...] is called DoWork and the general rule is that you can't touch anything in the UI from this event. Instead, you call the ReportProgress() method, which in turn raises the ProgressChanged event, from where you can update the UI. Once you're done, you assign a result to the worker and then the RunWorkerCompleted event is raised.

Basically the UI needs to run on one thread. Otherwise you lose the point.

Daniel Hesslow
  • 359
  • 2
  • 10
0

async await is likely the best answer, however a timer can be used in this situation as well.

    System.Timers.Timer t;

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        t = new System.Timers.Timer();
        t.Elapsed += t_Elapsed;
        t.Interval = 1000;
        t.Start();
    }

    int tickCount = 0;

    void t_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        tickCount++;
        switch (tickCount)
        {
            case 1: textBox.Text = "one"; break;
            case 2: textBox.Text = "two"; break;
            case 3: textBox.Text = "three"; break;
            case 4: textBox.Text = "four"; break;
            case 5: t.Stop(); break;
        }

    }
oppassum
  • 1,746
  • 13
  • 22