How to Update UI from Another Thread Running in Another Class

How to update UI from another thread running in another class

First you need to use Dispatcher.Invoke to change the UI from another thread and to do that from another class, you can use events.

Then you can register to that event(s) in the main class and Dispatch the changes to the UI and in the calculation class you throw the event when you want to notify the UI:

class MainWindow : Window
{
private void startCalc()
{
//your code
CalcClass calc = new CalcClass();
calc.ProgressUpdate += (s, e) => {
Dispatcher.Invoke((Action)delegate() { /* update UI */ });
};
Thread calcthread = new Thread(new ParameterizedThreadStart(calc.testMethod));
calcthread.Start(input);
}
}

class CalcClass
{
public event EventHandler ProgressUpdate;

public void testMethod(object input)
{
//part 1
if(ProgressUpdate != null)
ProgressUpdate(this, new YourEventArgs(status));
//part 2
}
}

UPDATE:

As it seems this is still an often visited question and answer I want to update this answer with how I would do it now (with .NET 4.5) - this is a little longer as I will show some different possibilities:

class MainWindow : Window
{
Task calcTask = null;

void buttonStartCalc_Clicked(object sender, EventArgs e) { StartCalc(); } // #1
async void buttonDoCalc_Clicked(object sender, EventArgs e) // #2
{
await CalcAsync(); // #2
}

void StartCalc()
{
var calc = PrepareCalc();
calcTask = Task.Run(() => calc.TestMethod(input)); // #3
}
Task CalcAsync()
{
var calc = PrepareCalc();
return Task.Run(() => calc.TestMethod(input)); // #4
}
CalcClass PrepareCalc()
{
//your code
var calc = new CalcClass();
calc.ProgressUpdate += (s, e) => Dispatcher.Invoke((Action)delegate()
{
// update UI
});
return calc;
}
}

class CalcClass
{
public event EventHandler<EventArgs<YourStatus>> ProgressUpdate; // #5

public TestMethod(InputValues input)
{
//part 1
ProgressUpdate.Raise(this, status); // #6 - status is of type YourStatus
//part 2
}
}

static class EventExtensions
{
public static void Raise<T>(this EventHandler<EventArgs<T>> theEvent,
object sender, T args)
{
if (theEvent != null)
theEvent(sender, new EventArgs<T>(args));
}
}

@1) How to start the "synchronous" calculations and run them in the background

@2) How to start it "asynchronous" and "await it": Here the calculation is executed and completed before the method returns, but because of the async/await the UI is not blocked (BTW: such event handlers are the only valid usages of async void as the event handler must return void - use async Task in all other cases)

@3) Instead of a new Thread we now use a Task. To later be able to check its (successfull) completion we save it in the global calcTask member. In the background this also starts a new thread and runs the action there, but it is much easier to handle and has some other benefits.

@4) Here we also start the action, but this time we return the task, so the "async event handler" can "await it". We could also create async Task CalcAsync() and then await Task.Run(() => calc.TestMethod(input)).ConfigureAwait(false); (FYI: the ConfigureAwait(false) is to avoid deadlocks, you should read up on this if you use async/await as it would be to much to explain here) which would result in the same workflow, but as the Task.Run is the only "awaitable operation" and is the last one we can simply return the task and save one context switch, which saves some execution time.

@5) Here I now use a "strongly typed generic event" so we can pass and receive our "status object" easily

@6) Here I use the extension defined below, which (aside from ease of use) solve the possible race condition in the old example. There it could have happened that the event got null after the if-check, but before the call if the event handler was removed in another thread at just that moment. This can't happen here, as the extensions gets a "copy" of the event delegate and in the same situation the handler is still registered inside the Raise method.

C# WPF - How to simply update UI from another class/thread

To do this, I'd recommend declaring a static variable that holds a UI control of your likings, in this case, a Button. Also add using System.Windows.Controls; at the beginning. So your code should look like this:

using System.Threading;
using System.Windows;
using System.Windows.Controls;

namespace WPF_Test
{
public partial class MainWindow : Window
{
public static Button xamlStaticButton;

public MainWindow()
{
InitializeComponent();
xamlStaticButton = xaml_button;
xamlStaticButton.Content = "Text changed on start";
}

private void xaml_button_Click(object sender, RoutedEventArgs e)
{
Threading.t1.Start();
UIControl.ChangeButtonName("Updated from another CLASS.");
}
}
}

So pretty much what I did was to make a placeholder for the button, then assigning it on start.

class UIControl : MainWindow
{
public static void ChangeButtonName(string text)
{
App.Current.Dispatcher.Invoke(delegate {
xamlStaticButton.Content = text;
});
}
}

Now I inherited the MainWindow class to the UIControl class for the sake of convenience. Also to make this work with multithreading, I added the App.Current.Dispatcher.Invoke(delegate { /*your UI code you want to execute*/});. This will make sure your UI will update even if you are on another thread.

android: update UI from another thread in another class

You can post a runnable which does the UI operation to main thread as follows,

public class Utils {

public static void runOnUiThread(Runnable runnable){
final Handler UIHandler = new Handler(Looper.getMainLooper());
UIHandler .post(runnable);
}
}

Utils.runOnUiThread(new Runnable() {
@Override
public void run() {
// UI updation related code.
}
});

Update Android UI from a thread in another class


What I'm trying to do is update the UI from a thread which has been
created in another class. I've seen all of the suggestions, such as
async, handlers, runnable, etc... but I've having real trouble
implementing them in separate classes.

Generally for your goal i recommend to you use:

  • AsyncTask
  • IntentService with ResultReceiver

I don't think that its too tricky. Absolutely not. If you have it as separated class(es) and not as inner class(es) in some Activity class so i recommend to use constructor where you will pass context, widgets, generally whatever you want and then in correct methods(which allows UI update) update your UI.

I'm doing it because i like when i have clean classes(so UI class have only UI implementations and logic is positioned separately).

Example:

public class TaskExample extends AsyncTask<Void, Integer, Void> {

private Context c;
private Button b;

public TaskExample(Context c, Button b) {
this.c = c;
this.b = b;
}

protected Void doInBackground(Void... params) {
// some work
if (isSomethingConnected) {
publishProgress(Constants.IS_CONNECTED);
}
return null;
}

public void onProgressUpdate(Integer... params) {
switch (params[0]) {
case Constants.IS_CONNECTED:
b.setText("Connected");
break;
case Constants.ANOTHER_CONSTANT:
// another work
break;
}
}
}

Usage:

public class Main extends Activity implements View.OnClickListener {

private Button b;

public void onCreate(Bundle b) {
super.onCreate(b);
// initialise widgets and set listeners to appropriate widgets
}

public void onClick(View v) {
switch(v.getId()) {
case R.id.connectBtn:
startWorker();
break;
}
}

private void startWorker() {
TaskExample te = new TaskExample(this, b);
te.execute();
}
}

C# - WPF - Updating the UI from another class on another Thread

Assuming that ProcessClass is your own code that you can update, change the signiture of DoDomething() to

public async Task DoSomething(IProgress<string> progress)
{
progress.Report("Begin DoSomething()");

var counter = 0;
for(var i = 1; i < 100; i++)
{
counter += i;
await Task.Delay(100).ConfigureAwait(false);

progress.Report($"DoSomething() - i = {i}");
}

progress.Report($"DoSomething() Completed, answer is {counter}");
}

Now your button click handler can be written

private async void btnStart_Click(object sender, RoutedEventArgs e)
{
// usually you would update some other control such as a TextBlock
// for the feedback, rather than the button content
var progress = new Progress<string>(s => btnStart.Content = s);
ProcessClass processClass = new ProcessClass();
await processClass.DoSomething(progress).ConfigureAwait(false);
}

Update UI item from another class and thread

You should check the Invoke for UI thread

private void UpdateChat(string message)
{
if(this.InvokeRequired)
{
this.Invoke(new MethodInvoker(delegate {
lstChat.Items.Insert(lstChat.Items.Count, message);
lstChat.SelectedIndex = lstChat.Items.Count - 1;
lstCat.Refresh();
}));
} else {
lstChat.Items.Insert(lstChat.Items.Count, message);
lstChat.SelectedIndex = lstChat.Items.Count - 1;
lstCat.Refresh();
}
}

C# - How to update Main UI from a thread in another class

You can simply Invoke the ProgressUpdate event from the FlushData() method.

Simply call:

If (ProgressUpdate !=null )
{
ProgressUpdate(this,new YourEventArgs())
}

this is the source instance where the event originated from.

You could just create YourEventArgs by inheriting from EventArgs class.

  public class YourEventArgs : EventArgs
{
//Put any property that you want to pass back to UI here.
}

When the event gets raised in the UI:

RaiseEvent.ProgressUpdate += (s, e) =>
{
Dispatcher.BeginInvoke((Action)(() =>
{
lstTest.Items.Add("this is a test : ");
//Add items to your UI control here...
}));
};

e will be of type YourEventArgs.

On a side note, you should never touch UI thread from a diffent thread (like background worker thread in your example). Since your event-handler already does the Dispatcher.BeginInvoke, that's safe.

Also, your ProgressUpdate event should be inside of your class SetXmlData.

How do I update the GUI from another thread?

For .NET 2.0, here's a nice bit of code I wrote that does exactly what you want, and works for any property on a Control:

private delegate void SetControlPropertyThreadSafeDelegate(
Control control,
string propertyName,
object propertyValue);

public static void SetControlPropertyThreadSafe(
Control control,
string propertyName,
object propertyValue)
{
if (control.InvokeRequired)
{
control.Invoke(new SetControlPropertyThreadSafeDelegate
(SetControlPropertyThreadSafe),
new object[] { control, propertyName, propertyValue });
}
else
{
control.GetType().InvokeMember(
propertyName,
BindingFlags.SetProperty,
null,
control,
new object[] { propertyValue });
}
}

Call it like this:

// thread-safe equivalent of
// myLabel.Text = status;
SetControlPropertyThreadSafe(myLabel, "Text", status);

If you're using .NET 3.0 or above, you could rewrite the above method as an extension method of the Control class, which would then simplify the call to:

myLabel.SetPropertyThreadSafe("Text", status);

UPDATE 05/10/2010:

For .NET 3.0 you should use this code:

private delegate void SetPropertyThreadSafeDelegate<TResult>(
Control @this,
Expression<Func<TResult>> property,
TResult value);

public static void SetPropertyThreadSafe<TResult>(
this Control @this,
Expression<Func<TResult>> property,
TResult value)
{
var propertyInfo = (property.Body as MemberExpression).Member
as PropertyInfo;

if (propertyInfo == null ||
!@this.GetType().IsSubclassOf(propertyInfo.ReflectedType) ||
@this.GetType().GetProperty(
propertyInfo.Name,
propertyInfo.PropertyType) == null)
{
throw new ArgumentException("The lambda expression 'property' must reference a valid property on this Control.");
}

if (@this.InvokeRequired)
{
@this.Invoke(new SetPropertyThreadSafeDelegate<TResult>
(SetPropertyThreadSafe),
new object[] { @this, property, value });
}
else
{
@this.GetType().InvokeMember(
propertyInfo.Name,
BindingFlags.SetProperty,
null,
@this,
new object[] { value });
}
}

which uses LINQ and lambda expressions to allow much cleaner, simpler and safer syntax:

// status has to be of type string or this will fail to compile
myLabel.SetPropertyThreadSafe(() => myLabel.Text, status);

Not only is the property name now checked at compile time, the property's type is as well, so it's impossible to (for example) assign a string value to a boolean property, and hence cause a runtime exception.

Unfortunately this doesn't stop anyone from doing stupid things such as passing in another Control's property and value, so the following will happily compile:

myLabel.SetPropertyThreadSafe(() => aForm.ShowIcon, false);

Hence I added the runtime checks to ensure that the passed-in property does actually belong to the Control that the method's being called on. Not perfect, but still a lot better than the .NET 2.0 version.

If anyone has any further suggestions on how to improve this code for compile-time safety, please comment!



Related Topics



Leave a reply



Submit