I want to update the position of certain UI elements in my WPF application in a loop. After each iteration, the UI should be rerendered to make the changes visible. The updating process can stopped at any point using a CancellationToken. Since the cancellation is performed by the user, the UI must remain responsive to input. I wrote the following method to do this:
public async Task DoStuff(CancellationToken token)
{
do
{
DoLayoutUpdate();
await Dispatcher.Yield(DispatcherPriority.Input);
} while (!token.IsCancellationRequested);
}
This mostly works: The UI is rerendered after each iteration and I can click the button to cancel the operation so input works as well. The problem is: if there is no input and nothing to rerender, the method gets stuck in the Yield. Presumably the thread is blocked waiting for input or render tasks.
If I increase the DispatcherPriority to Render, the method does not get stuck anymore but then the UI isn't updated and input isn't handled anymore.
How can I fix this?
Try await Task.Delay(10); or await Dispatcher.BeginInvoke(new Action(() => { }), System.Windows.Threading.DispatcherPriority.Input); instead of Dispatcher.Yield.
This should give the UI thread a chance to render while your loop executes.
If I increase the DispatcherPriority to Render, the method does not get stuck anymore but then the UI isn't updated and input isn't handled anymore.
Actually, the problem is that you changed the priority in the wrong direction. Setting the priority to DispatcherPriority.Background would allow WPF to finish its work and then eventually schedule the continuation to allow the method to resume execution after the await.
I.e.:
public async Task DoStuff(CancellationToken token)
{
do
{
DoLayoutUpdate();
await Dispatcher.Yield(DispatcherPriority.Background);
} while (!token.IsCancellationRequested);
}
Using a higher priority causes your continuation to be scheduled too soon, giving your loop all the dispatcher time, in preference over all the other things WPF needs to do.
Noting, of course, that calling Dispatcher.Yield() without a parameter will default to using DispatcherPriority.Background as well. Either way works fine.
The other ideas suggested in the accepted answer will work too, but they are a bit kludgy as compared to simply yielding with the correct requested continuation priority.
Or the bizarre monstrosity that works no matter what thread you're running on:
await Task.Run(() =>
{
Action action = () => { };
MainWindow.Dispatcher.Invoke(action,
System.Windows.Threading.DispatcherPriority.Background);
});
Dispatcher.Yield() works fine on the UI thread. But it is a static method that operates on Dispatcher.CurrentDispatcher, and there's no equivalent non-static member.
I have a windows forms application
on which I need to use a for loop having a large number of Remote Calls around 2000 - 3000 calls,
and while executing the for loop, I loose my control on form and form controls, as it becomes a large process and some time it shows "Not Responding" but if I wait for a long it comes back again, I think I need to use some threading model for that, is there any idea, how can I proceed to solve the issue?
You need to perform the long running operation on a background thread.
There are several ways of doing this.
You can queue the method call for execution on a thread pool thread (See here):
ThreadPool.QueueUserWorkItem(new WaitCallback(YourMethod));
In .NET 4.0 you can use the TaskFactory:
Task.Factory.StartNew(() => YourMethod());
And in .NET 4.5 and later, you can (and should, rather than TaskFactory.StartNew()) use Task.Run():
Task.Run(() => YourMethod());
You could use a BackgroundWorker for more control over the method if you need things like progress updates or notification when it is finished. Drag the a BackgroundWorker control onto your form and attach your method to the dowork event. Then just start the worker when you want to run your method. You can of course create the BackgroundWorker manually from code, just remember that it needs disposing of when you are finished.
Create a totally new thread for your work to happen on. This is the most complex and isn't necessary unless you need really fine grained control over the thread. See the MSDN page on the Thread class if you want to learn about this.
Remember that with anything threaded, you cannot update the GUI, or change any GUI controls from a background thread. If you want to do anything on the GUI you have to use Invoke (and InvokeRequired) to trigger the method back on the GUI thread. See here.
private voidForm_Load(object sender, EventArgs e)
{
MethodInvoker mk = delegate
{
//your job
};
mk.BeginInvoke(callbackfunction, null);
}
private void callbackfunction(IAsyncResult res)
{
// it will be called when your job finishes.
}
use MethodInvoker is the easiest way.
Obviously, you need to use background threads. I suggest you read this free e-book.
I have a button in xaml whose mouseclick is connected to a delegate command. In delegate command, I am calling another method which creates bit map and saves it to a pdf. This method takes few seconds(usually more than 10 seconds) during which the app goes to Not responding state where the UI is not accessible. During which time I wanted to show some animation which is already defined in xaml. But showing it just before calling the time-taking-function and hiding it afterwards doesnt work. It shows up at the end of time-taking-function execution if i dont use different thread.
So, I tried using creating a different thread and calling this time taking function. Animation loads fine But the function actually needs to access some UI objects in UI thread. It results in this exception:
"The calling thread cannot access this object because a different thread owns it."
Is there a way to achieve it ? i.e., to show the animation during this function execution ?
Some code snippet:
private void OnExportPDFCommand()
{
PanelLoading = true; // Flag to show animation
Task.Factory.StartNew(() =>
{
CreatePDF(); //time-taking-function
Application.Current.Dispatcher.Invoke(() => PanelLoading = false);
});
}
Please explain with a small example. I am new to WPF. Thanks.
You can make OnExportPDFCommand an async method and await the Task you start:
private async void OnExportPDFCommand()
{
PanelLoading = true; // Flag to show animation
await Task.Run(() =>
{
CreatePDF(); //time-taking-function
});
PanelLoading = false; // stop animation
}
The compiler turns this into a state machine. The control flow is returned to the caller (the UI) at the await keyword. When the Task finishes, execution is eventually resumed at the next statement PanelLoading = false.
This way, the call to PanelLoading is in the UI thread again and no exception should be raised.
If you need to make other calls to the UI (inside CreatePDF) you can use Application.Current.Dispatcher.Invoke(...) to invoke these calls on the UI thread.
As a side note: you might want to disable the button that starts this at the beginning of OnExportPDFCommand() and enable it again at the end. This way you can avoid to start the operation multiple times and get confused with your animation.
In WinForms, how do I force an immediate UI update from UI thread?
What I'm doing is roughly:
label.Text = "Please Wait..."
try
{
SomewhatLongRunningOperation();
}
catch(Exception e)
{
label.Text = "Error: " + e.Message;
return;
}
label.Text = "Success!";
Label text does not get set to "Please Wait..." before the operation.
I solved this using another thread for the operation, but it gets hairy and I'd like to simplify the code.
At first I wondered why the OP hadn't already marked one of the responses as the answer, but after trying it myself and still have it not work, I dug a little deeper and found there's much more to this issue then I'd first supposed.
A better understanding can be gained by reading from a similar question: Why won't control update/refresh mid-process
Lastly, for the record, I was able to get my label to update by doing the following:
private void SetStatus(string status)
{
lblStatus.Text = status;
lblStatus.Invalidate();
lblStatus.Update();
lblStatus.Refresh();
Application.DoEvents();
}
Though from what I understand this is far from an elegant and correct approach to doing it. It's a hack that may or may not work depending upon how busy the thread is.
Call Application.DoEvents() after setting the label, but you should do all the work in a separate thread instead, so the user may close the window.
Call label.Invalidate and then label.Update() - usually the update only happens after you exit the current function but calling Update forces it to update at that specific place in code.
From MSDN:
The Invalidate method governs what gets painted or repainted. The Update method governs when the painting or repainting occurs. If you use the Invalidate and Update methods together rather than calling Refresh, what gets repainted depends on which overload of Invalidate you use. The Update method just forces the control to be painted immediately, but the Invalidate method governs what gets painted when you call the Update method.
If you only need to update a couple controls, .update() is sufficient.
btnMyButton.BackColor=Color.Green; // it eventually turned green, after a delay
btnMyButton.Update(); // after I added this, it turned green quickly
I've just stumbled over the same problem and found some interesting information and I wanted to put in my two cents and add it here.
First of all, as others have already mentioned, long-running operations should be done by a thread, which can be a background worker, an explicit thread, a thread from the threadpool or (since .Net 4.0) a task: Stackoverflow 570537: update-label-while-processing-in-windows-forms, so that the UI keeps responsive.
But for short tasks there is no real need for threading although it doesn't hurt of course.
I have created a winform with one button and one label to analyze this problem:
System::Void button1_Click(System::Object^ sender, System::EventArgs^ e)
{
label1->Text = "Start 1";
label1->Update();
System::Threading::Thread::Sleep(5000); // do other work
}
My analysis was stepping over the code (using F10) and seeing what happened. And after reading this article Multithreading in WinForms I have found something interesting. The article says at the bottom of the first page, that the UI thread can not repaint the UI until the currently executed function finishes and the window is marked by Windows as "not responding" instead after a while. I have also noticed that on my test application from above while stepping through it, but only in certain cases.
(For the following test it is important to not have Visual Studio set to fullscreen, you must be able to see your little application window at the same time next to it, You must not have to switch between the Visual Studio window for debugging and your application window to see what happens. Start the application, set a breakpoint at label1->Text ..., put the application window beside the VS window and place the mouse cursor over the VS window.)
When I click once on VS after app start (to put the focues there and enable stepping) and step through it WITHOUT moving the mouse, the new text is set and the label is updated in the update() function. This means, the UI is repainted obviously.
When I step over the first line, then move the mouse around a lot and click somewhere, then step further, the new text is likely set and the update() function is called, but the UI is not updated/repainted and the old text remains there until the button1_click() function finishes. Instead of repainting, the window is marked as "not responsive"! It also doesn't help to add this->Update(); to update the whole form.
Adding Application::DoEvents(); gives the UI a chance to update/repaint. Anyway you have to take care that the user can not press buttons or perform other operations on the UI that are not permitted!! Therefore: Try to avoid DoEvents()!, better use threading (which I think is quite simple in .Net).
But (#Jagd, Apr 2 '10 at 19:25) you can omit .refresh() and .invalidate().
My explanations is as following: AFAIK winform still uses the WINAPI function. Also MSDN article about System.Windows.Forms Control.Update method refers to WINAPI function WM_PAINT. The MSDN article about WM_PAINT states in its first sentence that the WM_PAINT command is only sent by the system when the message queue is empty. But as the message queue is already filled in the 2nd case, it is not send and thus the label and the application form are not repainted.
<>joke> Conclusion: so you just have to keep the user from using the mouse ;-) <>/joke>
you can try this
using System.Windows.Forms; // u need this to include.
MethodInvoker updateIt = delegate
{
this.label1.Text = "Started...";
};
this.label1.BeginInvoke(updateIt);
See if it works.
After updating the UI, start a task to perform with the long running operation:
label.Text = "Please Wait...";
Task<string> task = Task<string>.Factory.StartNew(() =>
{
try
{
SomewhatLongRunningOperation();
return "Success!";
}
catch (Exception e)
{
return "Error: " + e.Message;
}
});
Task UITask = task.ContinueWith((ret) =>
{
label.Text = ret.Result;
}, TaskScheduler.FromCurrentSynchronizationContext());
This works in .NET 3.5 and later.
It's very tempting to want to "fix" this and force a UI update, but the best fix is to do this on a background thread and not tie up the UI thread, so that it can still respond to events.
Try calling label.Invalidate()
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invalidate(VS.80).aspx
Think I have the answer, distilled from the above and a little experimentation.
progressBar.Value = progressBar.Maximum - 1;
progressBar.Maximum = progressBar.Value;
I tried decrementing the value and the screen updated even in debug mode, but that would not work for setting progressBar.Value to progressBar.Maximum, because you cannot set the progress bar value above the maximum, so I first set the progressBar.Value to progressBar.Maximum -1, then set progressBar.Maxiumum to equal progressBar.Value. They say there is more than one way of killing a cat. Sometimes I'd like to kill Bill Gates or whoever it is now :o).
With this result, I did not even appear to need to Invalidate(), Refresh(), Update(), or do anything to the progress bar or its Panel container or the parent Form.
myControlName.Refresh() is a simple solution to update a control before moving on to a "SomewhatLongRunningOperation".
From: https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.update?view=windowsdesktop-6.0
There are two ways to repaint a form and its contents:
You can use one of the overloads of the Invalidate method with the Update method.
You can call the Refresh method, which forces the control to redraw itself and all its children. This is equivalent to setting the Invalidate method to true and using it with Update.
The Invalidate method governs what gets painted or repainted. The Update method governs when the painting or repainting occurs. If you use the Invalidate and Update methods together rather than calling Refresh, what gets repainted depends on which overload of Invalidate you use. The Update method just forces the control to be painted immediately, but the Invalidate method governs what gets painted when you call the Update method.
I had the same problem with property Enabled and I discovered a first chance exception raised because of it is not thread-safe.
I found solution about "How to update the GUI from another thread in C#?" here https://stackoverflow.com/a/661706/1529139 And it works !
When I want to update the UI in "real-time" (or based on updates to data or long running operations) I use a helper function to "simplify" the code a bit (here it may seem complex, but it scales upward very nicely). Below is an example of code I use to update my UI:
// a utility class that provides helper functions
// related to Windows Forms and related elements
public static class FormsHelperFunctions {
// This method takes a control and an action
// The action can simply be a method name, some form of delegate, or it could be a lambda function (see: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/lambda-expressions)
public static void InvokeIfNeeded(this Control control, Action action)
{
// control.InvokeRequired checks to see if the current thread is the UI thread,
// if the current thread is not the UI thread it returns True - as in Invoke IS required
if(control.InvokeRequired)
{
// we then ask the control to Invoke the action in the UI thread
control.Invoke(action);
}
// Otherwise, we don't need to Invoke
else
{
// so we can just call the action by adding the parenthesis and semicolon, just like how a method would be called.
action();
}
}
}
// An example user control
public class ExampleUserControl : UserControl {
/*
//
//*****
// declarations of label and other class variables, etc.
//*****
//
...
*/
// This method updates a label,
// executes a long-running operation,
// and finally updates the label with the resulting message.
public void ExampleUpdateLabel() {
// Update our label with the initial text
UpdateLabelText("Please Wait...");
// result will be what the label gets set to at the end of this method
// we set it to Success here to initialize it, knowing that we will only need to change it if an exception actually occurs.
string result = "Success";
try {
// run the long operation
SomewhatLongRunningOperation();
}
catch(Exception e)
{
// if an exception was caught, we want to update result accordingly
result = "Error: " + e.Message;
}
// Update our label with the result text
UpdateLabelText(result);
}
// This method takes a string and sets our label's text to that value
// (This could also be turned into a method that updates multiple labels based on variables, rather than one input string affecting one label)
private void UpdateLabelText(string value) {
// call our helper funtion on the current control
// here we use a lambda function (an anonymous method) to create an Action to pass into our method
// * The lambda function is like a Method that has no name, here our's just updates the label, but it could do anything else we needed
this.InvokeIfNeeded(() => {
// set the text of our label to the value
// (this is where we could set multiple other UI elements (labels, button text, etc) at the same time if we wanted to)
label.Text = value;
});
}
}
I have a windows forms application
on which I need to use a for loop having a large number of Remote Calls around 2000 - 3000 calls,
and while executing the for loop, I loose my control on form and form controls, as it becomes a large process and some time it shows "Not Responding" but if I wait for a long it comes back again, I think I need to use some threading model for that, is there any idea, how can I proceed to solve the issue?
You need to perform the long running operation on a background thread.
There are several ways of doing this.
You can queue the method call for execution on a thread pool thread (See here):
ThreadPool.QueueUserWorkItem(new WaitCallback(YourMethod));
In .NET 4.0 you can use the TaskFactory:
Task.Factory.StartNew(() => YourMethod());
And in .NET 4.5 and later, you can (and should, rather than TaskFactory.StartNew()) use Task.Run():
Task.Run(() => YourMethod());
You could use a BackgroundWorker for more control over the method if you need things like progress updates or notification when it is finished. Drag the a BackgroundWorker control onto your form and attach your method to the dowork event. Then just start the worker when you want to run your method. You can of course create the BackgroundWorker manually from code, just remember that it needs disposing of when you are finished.
Create a totally new thread for your work to happen on. This is the most complex and isn't necessary unless you need really fine grained control over the thread. See the MSDN page on the Thread class if you want to learn about this.
Remember that with anything threaded, you cannot update the GUI, or change any GUI controls from a background thread. If you want to do anything on the GUI you have to use Invoke (and InvokeRequired) to trigger the method back on the GUI thread. See here.
private voidForm_Load(object sender, EventArgs e)
{
MethodInvoker mk = delegate
{
//your job
};
mk.BeginInvoke(callbackfunction, null);
}
private void callbackfunction(IAsyncResult res)
{
// it will be called when your job finishes.
}
use MethodInvoker is the easiest way.
Obviously, you need to use background threads. I suggest you read this free e-book.