Force Gui Update from UI Thread

Force GUI update from UI Thread

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.

How to force a UI update during a lengthy task on the UI thread

Usually when a function is taking place, any updates to the UI are blocked until the end of the function. This is because the frame does not get pushed until the end of the function. You can force this update by calling a method like this;

    void AllowUIToUpdate()
{
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Render, new DispatcherOperationCallback(delegate (object parameter)
{
frame.Continue = false;
return null;
}), null);

Dispatcher.PushFrame(frame);
//EDIT:
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background,
new Action(delegate { }));
}

EDIT: I added in an extra line to the AllowUIToUpdate function, and now everything functions as expected!

Force WPF Immediate UI update from UI thread

Before taking screenshot call this GridParent.UpdateLayout(); here GridParent can be parent control or like you are doing Application.Current.MainWindow.UpdateLayout();.

I've tried it. it will make sure UI is rendered before taking screenshot.

Also, i used this method to take screenshot.

private void SaveSnap()
{
RenderTargetBitmap renderTargetBitmap =
new RenderTargetBitmap((int) GridParent.ActualWidth, (int) GridParent.ActualHeight, 96, 96,
PixelFormats.Pbgra32);
renderTargetBitmap.Render(GridParent);
PngBitmapEncoder pngImage = new PngBitmapEncoder();
pngImage.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
using (Stream fileStream = File.Create("Img.png"))
{
pngImage.Save(fileStream);
}
}

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!

Why we cant update UI from worker thread? same as other variables/object

The answer -I think - you are looking for (why and how the exception is raised) is because there is explicit code inside the Control class's Handle property that will check if the code that wants to access the property is running on the same thread that created the Control (the User-interface thread).

You can check the reference source for the Handle property here. The actual thread check occurs inside the implementation of the InvokeRequired property, that you can also check here.

Early versions of the .net Framework did not include this check, so it was very easy to access the user interface from a different thread. The reason why we can't shouldn't do it is because there is a great portion of the Win32 API code base that is not thread-safe, so calling it from a single thread is the only way to guarantee no concurrency problems will happen in a multi-threaded application.

The Handle property will be accessed before the control internally calls any related Win32 API function that interacts with it (you can think of the handle as the 'this' reference inside the WIN32 API), so it was a very good candidate for centralizing the cross-thread check.

If I understand correctly from your comments, you want to simulate the same behavior (to raise an exception) when a worker thread tries to update a certain value (not necessarily a user interface element) 'owned' by a different thread. In this case you could adopt the same strategy that the framework is using.

If you analyze the code for InvokeRequired, it is simply comparing the current thread's ID against the value returned by SafeNativeMethods.GetWindowThreadProcessId() and returning true if there is a mismatch. This causes the cross-thread exception to be raised in the Handle property's getter accessor. In your case you could store the ID of the thread that is allowed to access the variable or resource and manually raise the exception if there is a mismatch between this stored ID and the ID of the thread attempting to access the guarded value.

Force WPF UI thread to update on a task

You can simply use a storyboard. Create one in the resource of the object(such as Window/Page or whatever you have) and then call the storyboard from code behind.

Here's a sample :

 <Window.Resources>
<Storyboard x:Key="FadeAnim">
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:0.4"/>
</Storyboard>
</Window.Resources>

And call it from code behind like this :

 Storyboard sb = this.FindResource("FadeAnim") as Storyboard;
Storyboard.SetTarget(sb, this.YourButton);
sb.Begin();

Update UI control from another thread while the UI is busy for time-consuming work

I recommend you use BackgroundWorker and put the construction and presentation of the Form2 object in the BackgroundWorker's RunWorkerCompleted event handler, which executes on the UI thread when the BackgroundWorker is finished. Definitely don't try to update the UI from a background thread. The GUI classes are not threadsafe.

GUI not updating until code is finished

An ugly hack is to use Application.DoEvents. While this works, I'd advise against it.

A better solution is to use a BackgroundWorker or a seperate thread to perform long running tasks. Don't use the GUI thread because this will cause it to block.

An important thing to be aware of is that changes to the GUI must be made on the GUI thread so you need to transfer control back to the GUI thread for you label updates. This is done using Invoke. If you use a BackgroundWorker you can use ReportProgress - this will automatically handle calling Invoke for you.



Related Topics



Leave a reply



Submit