views:

52

answers:

2

I am trying to update a UI element from a different thread but I am clearly missing something with regards to the new WPF calls.

In 3.5 winforms I did something like the following when updating my UI via a different thread:

    public string SummaryTitle
  {
   get
   {
    if (IsHandleCreated && InvokeRequired)
    {
     IAsyncResult result = BeginInvoke(new Func<object, object[], object>(GetType().GetProperty("SummaryTitle").GetValue), this, null);
     return EndInvoke(result).ToString();
    }

    return myTextBox.Text.Trim();
   }
   set
   {
    if (IsHandleCreated && InvokeRequired)
    {
     BeginInvoke(new Action<object, object, object[]>(GetType().GetProperty("SummaryTitle").SetValue), this, value, null);
    }
    else
    {
     myTextBox.Text = value;
    }
   }
  }

Now with 4.0 WPF I am trying to simulate the above, but with the new Dispatcher (maybe there is a different/better way, besides dispatcher?). Here is the prototype code I am playing with now (I know there are nonthreading related issues still in it, please ignore that for now) and it is not working right:

     public string SomeText
  {
   get {
    if(!myTextBox.Dispatcher.CheckAccess())
    {
     object o = myTextBox.Dispatcher.Invoke(new Func<object, object[], object>(GetType().GetProperty("SomeText").GetValue), this, null);
     return o != null ? Convert.String(o) :  "";
    }

    return myTextBox.Text.Trim(); 
   }
   set
   {
    if (!myTextBox.Dispatcher.CheckAccess())
    {
     myTextBox.Dispatcher.Invoke(new Action<object, object, object[]>(GetType().GetProperty("SomeText").SetValue),this, value, null);
     return;
    } 

    myTextBox.Text = value;
   }
  }

Any help on how to make the WPF accessor function similar to the old 3.5 winforms method would be appreciated.


EDIT: I am looking more at a way to make an accessor on the UI for a screen element thread-safe than the thread caller or progressbar specifically. The accessor needs to be able to be executed from another assembly or class regardless of whether that calling class is using Thread or BackgroundWorker. Also, it needs to hold water for all UI elements, not just a progress bar. So that a textbox, etc. can also be make thread safe in this manner. I updated the examples to help better convey my troubles.

+1  A: 

I'm still fairly new with WPF, but whenever I use the dispatcher I specify what priority level to run the action on. This determines when the action actually gets run

myControl.Dispatcher.BeginInvoke(DispatcherPriority.Render,
    new Action(delegate()
    {
        // Code to execute goes here
    }
));

A list of the PriorityLevels can be found here

It looks like you're trying to update a progress bar... If your bar is bound to something (say a number that is between 0 and 100) you could add the dispatcher call to your long-running process and have it update the number. DataBinding should take care of the rest.

Rachel
A: 

I know that you are interested in getting Dispatcher to work properly, but for something like this, have you looked at using the BackgroundWorker? While a little limiting as far as priorities and such, it certainly simplifies running calculations on a background thread while updating the UI.

Here is a SO thread where I helped somebody use a BackgroundWorker.

Of course, if you are using some other threading model, this may not work for you - YMMV - but if you are simply interested in doing some work in the background while updating a ProgressBar, take a look.

Wonko the Sane
Thanks for the reply. I am currently using a backgroundworker, but is is in another class that is loosely coupled to the one above. The bg worker is the 'other thread' that is calling into the above accessor. If i call the above access from the bgworker without the invoke redirect, then it will throw your normal "can't access progress bar from another thread" type message. So, i am trying to reinvoke the same accessor, but from within the current executing thread, since it is possible that the currently executing thread is different from the GUI thread.
John
@John - instead of trying to update the progress bar from the DoWork event/method, set the WorkerReportsProgress property of the BGWorker to true, add an event handler for its ProgressChanged event, and call its ReportProgress method from within the DoWork event handler. Put the code for updating the ProgressBar in the ProgressChanged event handler. Otherwise, there is no real reason to use the BackgroundWorker over any other threading model, since you are handling the multithreading part yourself.
Wonko the Sane