views:

308

answers:

3

Hello,

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;
 }
+3  A: 

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.

MasterMax1313
Alright, like in normal C#. I thought it would have been something special with WPF:P
Daok
And... the background working is not in the Toolbox hummm
Daok
just create an instance of the class directly, instead of the toolbox
MasterMax1313
+3  A: 

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.

Ed Swangren
It doesn't work the same way, I am readying and you cannot use this.BeginInvoke... your answer is not complete sorry cannot accept.
Daok
Well, the name is different, but the concept is the same. I said I did not know WPF. It's ok that you didn't accept, the accepted answer is far more complete.
Ed Swangren
+6  A: 

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;
}));
Robert Macnee
This is more what I have read yet on other website. Thx! Do you have a snippet to READ data... usually with 2.0 I do not need to do something but I have a cross thread exception while reading the textbox value :S
Daok
Yes, getting or setting dependency properties must be done on the UI thread, so it has to be within a Dispatcher.Invoke call as above.
Robert Macnee
Thank you, learn a lot from you today!
Daok
+1 for the example
Jacob Adams