I have added a SplitContainer inside another SplitContainer's Panel. Everything redraws fine when the splitters move. But resize of inner SplitContainer doesn't work correctly if the main SplitConainer's size changed by the main form's maximize/restore buttons.
If I move the splitter it redraws everything fine. I tried Invalidating everything in each SplitPanel SizeChanged event, but no luck. Do you have any idea?
I think this is the cause. The issue was gone after simplifying the complex layout into several simple layouts.
http://social.msdn.microsoft.com/forums/en-US/windowsuidevelopment/thread/25181bd5-394d-4b94-a6ef-06e3e4287527/
A little investigation showed that Windows stops sending WM_SIZE when it reaches some certain nesting level. In other words, it won't send WM_SIZE to your child windows if you try to resize them when you process WM_SIZE in the parent ones. Depending on the USER stuff/updates/serivice packs the maximum nesting level at which it stops propagating WM_SIZE may vary from 15 to 31 and even much higher (effectively unreachable) under latest XP 32bit/sp2.
But it still too little under XP x64 and still some similar ugly things happen to other messages under some builds of Vista.
So it is certainly a Windows bug.
Related
I'm working on an open-source .NET clone (GitHub) of DeskPins by Elias Fotinis (direct download off Google Drive). Its main function is to make other windows always-on-top. When they are on top, a pin icon is added to the title bar, which looks like this:
The pin icon moves with the window during drag and drop, and really looks like a part of it. It even responds to click event, which cancels always on top status and removes the icon from display.
Question. Is it possible to implement something like this in C# (and p/invoke, I'd assume)?
Research
I tried to run this project.
Add Your Control On Top Another Application # CodeProject
It's a WPF demo, which is supposed to add a custom control to the title bar. Does not seem to work on Windows 7 x64. Not sure if it's the OS or otherwise. Problem - z-order is not consistent, title bar appears on top of other windows as well, and it does not move with the window, it tries, but lots of visual artifacts and flicker.
Tried to apply this solution to #1:
Attach form window to another window in C#
Basically replacing relevant call to SetWindowLong with this pattern:
SetWindowLong(guestHandle, GWL_STYLE, GetWindowLong(guestHandle, GWL_STYLE) | WS_CHILD);
SetParent(guestHandle, hostHandle);
This change broke everything, so nothing was added to the title bar. Could be that it's not meant for title bars, only for the user area of the form.
In any case, if there is a simple solution, please share your wisdom. If not, I would appreciate any hints and/or links for me to investigate the topic further.
Here's how I've done this in DeskPins.
Each pin is a popup window with a custom HRGN defining its shape. I did experiment with injecting a DLL into processes using hooks and drawing on the caption, but that was too messy for me back then.
DeskPins creates the pin using WS_POPUP, WS_EX_TOPMOST and WS_EX_TOOLWINDOW, with 0 as the parent window. Then it immediately sends it a message (WM_PIN_ASSIGNWND), passing the target window to be pinned and the polling rate in msec. After that, the pin runs independently from DeskPins and they only send information messages to each other.
The pin handles WM_PIN_ASSIGNWND by setting the target window as its parent, making it top-most and starting a polling timer. To set the parent it uses:
SetWindowLong(hPin, GWL_HWNDPARENT, (LONG)hPinned);
Note that Microsoft warns against setting the parent like this and instead suggests using SetParent(). However, SetParent() does some internal processing which prevents it from working across processes. Using SetWindowLong(GWL_HWNDPARENT) essentially tricks the window manager to accept this. It's a bit of a hack, but, hey... it works.
The polling timer runs continuously and tests whether the target window is destroyed, hidden, moved or had its WS_EX_TOPMOST flag changed and responds appropriately.
I've been experiencing a very strange bug with a Winforms application, where showing a hidden form may result in that form's buttons & backgrounds coming up transparent. Buttons can be subsequently revealed by moving the mouse over them, as if the form hadn't been properly invalidated/drawn. For instance, a form that looks like this:
May come up like this:
Moving the mouse around causes the buttons to draw:
Here's a short video to show exactly how it behaves: http://screencast.com/t/XmFSPfLw. Explicitly calling Invalidate() or Refresh() in the Show event doesn't fix it.
What's strange is that this issue appears only on SOME computers, and I haven't been able to discern anything that those computers have in common.
I've seen it happen in different versions of Windows 7 (Embedded, x64, and x86) - but not all instances of any of those.
All systems are running the same version of .NET, and all have been fully updated.
On systems where it happens, it only happens when showing a form that'd been previously shown & hidden; it occurs when re-showing it (i.e. never the very first time the form is created & shown).
On systems where it happens, it doesn't happen for all forms, and isn't even consistent with the forms for which it happens (example of several subsequent hides/shows, sometimes it happens, sometimes it doesn't: http://screencast.com/t/liQ53p8Sce).
On systems where it happens, it only happens if the application is scaled to the full resolution of the screen. I use Control.Scale() to scale each form up to a user-specified resolution; if that's the same as the screen resolution, the bug may happen on some systems; if it's scaled up to anything less, I've never seen it happen (example: here's the same video as the first one above; with the form scaled to less than the full screen resolution, it doesn't happen: http://screencast.com/t/k87zJDeSYPGL).
I've been tearing my hair out trying to figure out what's different about the systems that exhibit the issue, and why it's happening in the first place. I literally had to have a client ship me a laptop that shows the issue, as I couldn't reproduce it on any systems I have here. I'm really at a loss as to the cause...
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 working on a simple active desktop replacement for a school that is migrating to Windows 7. The idea is to create a window with a few large buttons with pictures on so that young children who have trouble reading can use them.
I am using SetParent() to set my window's parent to 'Progman' so that it is always at the back and is not minimised when a user chooses to 'Show Desktop'. Everything works fine on Windows XP but on Windows 7 when users click 'Show Desktop' the window remains but the contents are replaced with the users Windows background. The buttons still work (you just can't see them) and if you resize or otherwise update the window they appear again. A picture of the problem:
Any ideas why this is happening? Does anyone know a way I could force a refresh of the window when 'Show Desktop' is pressed?
I suspect in your case your app is throwing an exception but not crashing. WPF apps have a tendency to do this if the exception is thrown during the ctor of some UI element. It can disrupt the rendering stack.
I tried to reproduce the problem but was unsuccessful. From my tests I was able to get the handle to 'Progman' and set the main window as the parent when using the Windows 7 basic theme (no Arrow glass).
When I used the Arrow theme, calling SetParent would cause the window to vanish. A little research turned up a possible fix. Instead of setting the parent as the 'Progman' window, you can try using the 'SysListView32' child (the child window used to hold the desktop icons).
The problem is obtaining 'SysListView32' isn't easy. It used to be a matter of traversing through 'Progman' to 'SHELLDLL_DefView' and then 'SysListView32', however, Windows 7 has changed that. 'SHELLDLL_DefView' is now a child of WorkerW.... some of the time.
Here is the best article I could find explaining this:
http://fernandomachadopirizen.wordpress.com/2010/08/09/give-me-a-handle-and-i-will-move-the-earth/
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.