Patrick Desjardins
Patrick Desjardins

Reputation: 140823

WPF application in a loop, how to not have the whole application freeze?

I am having fun with WPF and got a problem. I have googled and found this website that has the same problem of me but without any working solution.

The problem is that I have a button that do some processing of data (around 30 sec). I want to have the button to disable and to have log writing in a text box... the problem is that it doesn't disable and it doesn't wrote any thing on the textbox until the processing is completely done.

Any idea?

 private void button1_Click(object sender, RoutedEventArgs e)
 {
        this.button1.IsEnabled = false;
        //Long stuff here
         txtLog.AppendText(Environment.NewLine + "Blabla");
        //End long stuff here
        this.button1.IsEnabled = true;
 }

Upvotes: 2

Views: 2175

Answers (3)

Robert Macnee
Robert Macnee

Reputation: 11820

As others have said, use the BackgroundWorker or some other method of doing work asychronously.

You can declare it under your Window, initialize it somewhere like the Loaded event, and use it in the Click event. Here's your method, modified to use BackgroundWorker, assuming you've declared it under the Window as _bw:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    _bw = new BackgroundWorker();
    _bw.DoWork += new DoWorkEventHandler((o, args) =>
    {
        //Long stuff here       
        this.Dispatcher.Invoke((Action)(() => txtLog.AppendText(Environment.NewLine + "Blabla")));
    });

    _bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler((o, args) =>
    {
        //End long stuff here
        this.Dispatcher.Invoke((Action)(() => this.button1.IsEnabled = true));
    });
}

private void button1_Click(object sender, RoutedEventArgs e)
{
    this.button1.IsEnabled = false;

    _bw.RunWorkerAsync();
}

Note that anything that modifies your UI from another thread must be done within a Dispatcher.Invoke or Dispatcher.BeginInvoke call, WPF does not allow you to get or set DependencyProperty values from any thread but the one where the object was created (more about this here).

If you wanted to read from txtLog instead of modifying it, the code would be the same:

//Long stuff here       
this.Dispatcher.Invoke((Action)(() => 
{
    string myLogText = txtLog.Text;
    myLogText = myLogText + Environment.NewLine + "Blabla";
    txtLog.Text = myLogText;
}));

Upvotes: 8

CodeMonkey1313
CodeMonkey1313

Reputation: 16011

do it async. create a backgroundworker process to handle the data and the application will continue to respond. MSDN Resources on the Class. Since WPF is using C# (or VB.net) you can still use the same types of threading objects. I've used the background worker successfully in a WPF app myself.

Upvotes: 3

Ed Swangren
Ed Swangren

Reputation: 124652

That operation is being performed on the UI thread. This means that it will block the Windows message pump from processing until it has completed. no pump = no UI updates. You should launch the job on another thread. I don't know WPF, but in C# I would use either the Thread or BackgroundWorker classes.

Upvotes: 3

Related Questions