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.
Related
I'm just starting out with MonoMac in Xamarin Studio, and I've run into the strangest problem:
I a window with an NSButton and a NSTextField on it. By this point I've cut out the event handler on the button, so it doesn't DO anything, except highlight when I click it. The button creation code looks like this:
nsButton = new NSButton(new System.Drawing.RectangleF(0, 0, 100, 100));
nsButton.BezelStyle = NSBezelStyle.RoundRect;
nsButton.Font = NSFont.SystemFontOfSize(
NSFont.SystemFontSizeForControlSize(NSControlSize.Regular));
nsButton.StringValue = text;
...and then it gets added to the window like so:
nsView.AddSubview(control.Handle as NSView);
(because in this part of the code, control.Handle is typed as object, and nsView is the main view on the window).
All runs and works fine at first. But, if I click repeatedly on that button, eventually the window just closes. No error, no exception, and the app itself doesn't quit; menus continue to respond and cheerfully log messages when I use them. But the window is simply -- gone.
It's extremely repeatable: it happens after 21 clicks. If I add an event handler that updates the NSTextField (e.g. hello.Caption="Foo";), then it happens after 19 clicks. It doesn't matter whether I click fast or slow; it's always the same number of clicks. Note that there is no code in the project to close the window, and the window doesn't even have a close box; I know of no legitimate way to close it short of quitting the app.
I am baffled here, and don't know how to debug this further. Does Xamarin have some sort of evaluation limit that closes your windows after so-many events? Is it a framework bug? Any insight will be greatly appreciated.
But, if I click repeatedly on that button, eventually the window just
closes. No error, no exception, and the app itself doesn't quit; menus
continue to respond and cheerfully log messages when I use them. But
the window is simply -- gone.
This "disappearing without a trace" sometimes occurs when an application crashes in native code badly enough. This can occur due to bugs in the binding code or mistakes made in calling the native APIs that corrupt internal cocoa state. I believe you are using MonoMac, and that this particular issue has been fixed in Xamarin.Mac.
You can sometimes get more information from the output window or by attaching lldb to your process.
This turned out to be the same issue as this one, in a slightly different guise.
In short, I wasn't keeping a reference to the NSWindow object, but instead was letting it go out of scope. So the GUI window would stick around for a while, but eventually (after some number of events or other code creating behind-the-scenes garbage) it is noticed and disposed of by the garbage collector. The window is then torn down.
It's all perfectly reasonable once you think of it, and happens under both Xamarin and MonoMac (just at slightly different times).
The simple solution, of course, is to retain a reference to the window until you're truly done with it. Problem solved!
(And yes, I feel a bit sheepish, but hopefully this question will get found by future Mac C# developers, and save them some grief.)
I have a Windows form application that seems to pause draw functions when it is minimized. I need to know whether this just some draw function quirk, or if it extends to all code running. In either case, is there a workaround to this issue?
If it's minimized, there's nothing visible to paint, and so there's no need for WM_PAINT messages to be sent or processed. Nothing else should be affected, unless you've done something wrong in your code to cause it to be.
See also C# Windows Form Application update when minimized for some related information.
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?
We have a C# .NET 3.5 UI client application that runs in a multiple monitor desktop environment (typically 4 screens) on Windows 7. Every so often, after running several of these applications, the screen stops redrawing.
Controls continue to be reactive to clicks or keypress and values can be updated programmatically, but the entire form is not redrawn to reflect any changes. For example buttons that are enabled/disabled based on state may be remain grayed out, but be reactive to clicks or vice versa. Buttons do not animate when clicked.
Workaround: minimizing and restoring the window appears to clear the problem. After this, the application begins to draw correctly.
The must frustrating aspect of this problem is that programmatically, everything appears to be running normally. No exceptions are caught in our logs. Nothing was visible in the system event logs. We have not found a way to detect this condition is happening yet.
Other miscellaneous aspects: logging uses log4net, server communication layer uses ZMQ
Update:
Calling form Invalidate() and Update() does not fix the problem.
When dragging the window between screens, it shows different values on each screen.
Minimize/restore still resolves the issue.
I can't be sure of anything without seeing the app and the code, but my best guess is someone calls .SuspendLayout() before a complicated update, and an exception (probably swallowed) prevents the code from ever reaching the corresponding .ResumeLayout(). To test this, try adding a button that calls .ResumeLayout() for the form.
It seems the solution is there:
1) http://blogs.msdn.com/b/alejacma/archive/2009/08/11/controls-won-t-get-resized-once-the-nesting-hierarchy-of-windows-exceeds-a-certain-depth-x64.aspx
2) http://support.microsoft.com/kb/2664641/en-us
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).