Why do some Windows UI elements update immediately and others do not? - c#

We have a button click event that starts a long running task that updates the status bar labels and progress control to give user feedback. Before we moved the task to a seperate thread we noticed the status labels {label.text = "some message"; }(in general) would update immediately while the progress bar and some custom controls often would not update until the function finished and the main UI thread started sending messages again ( which we realized makes sense considering the main UI thread).
But this lead us to wonder - Do some windows controls repaint directly instead of being issued a WM_Paint message?

Your findings are pretty contradictory from what I know about .NET controls. The common rule is that changing the Text property or altering a property like ForeColor or BackColor merely causes the Invalidate() method to be called. Which ultimately causes a WM_PAINT message to be delivered when the UI thread starts pumping messages again. You can call the control's or the Form's Update() method to force any pending paints to be performed before entering the slow code. This is all entirely standard Windows behavior.
A special case is the ProgressBar control. The native Windows implementation for it updates the bar length directly. That's compat behavior, this control often is used in code that doesn't pump properly.
TextBox has special painting behavior, it partly paints directly to the window instead of going through the WM_PAINT message handler. That's legacy behavior way back from Windows 2 when it needed to work reasonably on a 386SUX machine. But that's not relevant in this case.
Of course, rather than fretting about this you should just never get yourself in a place where any of this matters.

Related

Create or load controls without freezing the window

When a browser is opened, before it's completely loaded, we can use the controls as others are being loaded (the address bar appears and, while the bookmarks are loaded, we can already type in it).
I'm making a personal browser, and I don't know how to perform that. I imagined creating the controls in another thread, but soon I discovered that that's not possible.
In my last question (where I discovered the above), I received an answer talking about Attribute, Reflection, async/await modifiers and observable collection as the closest solution to that and I'll study them yet. In this new question, I would like to receive others suggestions of how that could be made (allow the user to use the window and controls while others are being created/loaded).
Thanks in advance.
Actually I believe the process of loading the UI part of controls isn't the heavy one.
In the other hand, loading the data which is later bound to the control is the problem.
You can't draw controls outside UI thread, but you can load heavy data, preload resources or do calculation in a background thread.
While heavy controls' data is prepared to hit the UI in some background thread, UI will still be responsive.
For example, I guess Web browsers do HTML to DOM parsing in a background thread and they stream results in real time to the UI thread. That is, address bar and other UI components are responsive because UI thread isn't stressed.

Winform Control.Refresh - guaranteed synchronous?

Invoking the WinForms Control.Refresh method is described in MSDN as:
Forces the control to invalidate its client area and immediately redraw itself and any child controls.
I am debugging an intermittent issue that seems to occur when the attached display is switching scanout signals (effectively changing resolution), in which a Control.Refresh appears to not be generating the expected Control.OnPaint call. I am instrumenting the application to get more information, however I'm curious if this could actually be the runtime deciding not to invoke the OnPaint because it detects the display is momentarily blanked out.
This seems unlikely to me, and I expect I'll find some other smoking gun, but I'm posting on the off-chance somebody else has seen this in the wild, and has some recommendations for handling this.
No, it's not guaranteed. If the control does not have a screen to draw on, it won't fire the paint message.
But once the control is on the screen again, the paint message should fire again.
Are you trying to paint debug information? If the control isn't on the screen, what are you expecting to happen when you call refresh?

Windows form not repainting

I am new to C# but I have been programming with VB6 for a long time. I have a very simple project that runs without any errors. There is one problem however and it has to do with the Windows itself. If I open up any Window (from any other program) and that window overlaps my application...the moment I close that "other window", my application's Window does not repaint the portion that was overlapped by the "other window".
The same thing happens whenever the Bubbles screensaver comes on for Windows Vista. When I jiggle the mouse to cancel the screen saver, guess what??? I have bubbles scattered all over my Window. In VB, we had a simple "Window.Refresh" and voila! In C# however, this does not work. I have scoured the forum(s) and there seems to be a mention of "refreshing a thread". I'm kind of confused at this point. Why can't things be simple??
You can call Form.Invalidate to invalidate the client area of the form. That will cause it to receive a paint message, and redraw itself.
This is similar to the VB6 Refresh method.
However, this should happen automatically. If your application is doing some processing in the UI thread, however, it will prevent it from processing its messages until the work is completed. If this is the case, you should consider using BackgroundWorker (or some other method) to push the work onto a background thread.
There is nothing special you should need to do to redraw the Form in C# (Form should redraw fine when you bring the window back to the top).
Do you have any third party controls in play? Control.Refresh() does exist in C#, should you should be able to call it from the Form itself (usually this.Refresh() in your Form's codebehind), or any Control which has children (like Panel).

Animated gif on button stops after moving (redrawing) the form

I have an animated gif placed on the button. It's animating ok (most of the times :P) but when the windows is redrawn (repainted) the animation stops. I have tried to refresh the button (button.Refresh() ) while handling Paint event but it didn't solve the issue. \
Anyone knows how to fix this?
Perhaps I am mistaken, but I think the issue is that it stops animating not when the form is redrawn, but when the animated object is obscured by another window. This is intended behavior; the bug is that in Windows Vista and Windows 7, the display is composited, so even though the window was 'obscured' it was never truly obscured, and it will never receive paint messages when un-obscured which will kick the animation back in.
This bug appears to affect any ButtonBase-derived control with an animated object.
The issue is the Control.IsWindowObscured function. It will return true. You can see in the ButtonBase.cs file, at System.Windows.Forms.ButtonBase.OnFrameChanged, there is a line of code at the very end that says:
if (IsWindowObscured) {
StopAnimate();
return;
}
and therein lies the problem.
FYI, OnFrameChanged is called from the ImageAnimator thread. This is the callback that is specified in ImageAnimator.Animate(image, eventhandler). ButtonBase sets this up in the private void Animate(bool animate) function. The ImageAnimator thread polls every 50ms and checks to see if a new frame is necessary for any of the images it is monitoring; if so, it sets a flag to have the control invalidated and the new frame drawn.
Since this is inaccessible to us, I don't think there is much we can do about it. As a workaround, I implemented a timer in my form that invalidates the control every 500ms, so it will force it to restart if it had previously stopped. It is quite annoying that we can't override or even access it. I'm afraid the only solution is the hack above, or to create or use a control created by yourself or a third-party.
To clarify -- this is only a problem on Windows Vista or Windows 7 using desktop composition. The issue is that windows are never truly obscured, like they are when not using desktop composition. They are always buffered by the window manager. (There are special layered windows in windows 2000+, but ignore that for now). Previously, the parts of a window were not available if they were not on the screen or obscured by another window. When they came back into view, by changing focus or position etc, then the system will notify that area to repaint itself. However when using desktop composition, the repainting is never required, since the actual contents of the window are buffered elsewhere. This is why the window previews work in the taskbar, and Flip-3d, for example. The side effect is that code that expects to get a paint message when it is visible once again after being obscured will fail. The ButtonBase code expects to recieve a paint message once it comes back into view, which will start the animation again. And therefore, this optimization became a bug.
The issue should be reported in Microsoft Connect, though it is unlikely to be resolved.

Automatically show loader on long operation

I'm trying to show a loader animation when my application it's blocked for more than 500ms.
I want that to be automatic, I don't want to add any piece of code before every long operation.
I know that in WinForms it was possible (I used this: https://snipplr.com/view/24851/), but It does not work in WPF.
I've found that I can do that with the mouse cursor(display Hourglass when application is busy)
I've tried to launch a window with a spinner.
I've found this answer (https://stackoverflow.com/a/21411656/10820863), that works detecting long operation.
Problem is that if I launch a window from a thread that is not the main one, I got a ThreadException because it's not the main thread. If I use Application.Current.Dispatcher.BeginInvoke
method, the window appears only when UI is not blocked anymore.
So, how can I automatically detect long operation and show a loading window/page/image/whatever if it lasts for more than 500ms?
[EDIT]
I don't want to add code to every long function, evaluating case for case which function can be long.
I'd prefer to have an automatic method that do that for me.

Categories