I'm trying to create a full screen overlay over the entire screen, which will change the colors / saturation etc. of the entire screen and add some Text and effects. Basically I want to "replace" the entire screen, while the user still has the ability to interact normally with Windows.
Solution 1
The problem however is, that if I simply create a TopMost window over the entire screen, the user will not be able to interact with whatever is beneath this window.
Solution 2
Simply drawing on the desktop buffer doesn't solve the issue either. It will give nasty effects when windows are moved and also will result in heavy motion blur effects as well. This will never look good.
Solution 3
Hooking the "desktop draw event" is not possible in C# as I would have to inject a DLL into explorer.exe. This is not at all a pretty solution and will not work with C#. Also anti virus programs will likely detect it as something harmfull.
The closest thing I saw was this, but it the user will not be able to "click through" the overlay. In the example the overlay is mostly entirely transparent. Using a color other than the transparency key will result in the problem of Solution 1.
Question: How can I overlay the entire screen with effects efficiently?
UNTESTED
It sounds like your problem is clicking. In order to pass through your mouse messages, have you considered intercepting the mouse click on your top window, (assuming this has been set to be a transparent window), then hiding your window, fire the same mouse message using the user32 import SendMessage, and then showing your window again? If you don't want to hide your window, you can probably just call SendMessage directly to the various other window handles.
The following link shows how to cycle through the windows on the desktop of your application:
http://support.microsoft.com/kb/183009
I think you can also override the CreateParams if you have already made your form transparent. Doing so may allow the mouse events to pass through.
// This may be even simpler
protected override CreateParams CreateParams
{
get
{
CreateParams createParams = base.CreateParams;
createParams.ExStyle |= 0x00000020; // WS_EX_TRANSPARENT
return createParams;
}
}
You can make the form itself transparent in newer windows forms like:
public void MakeSeeThru()
{
frmTransparentForm.Opacity = 0.83;
}
The above only works on Form. To do this on child controls takes more work. First you have to set the style to support transparent back color.
public TransparentControl()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent.
}
If the control is transparent, this does not stop you from overriding the OnPaintBackground and OnPaint methods and do your custom drawing if you like. It just draws with the default as a see-through background.
I've done this before and there is a bit of a blur if this moves around. However if the entire thing is a huge transparent form that covers your desktop there should be no motion blur.
If that has performance problems and you want to custom draw the desktop, you can grab a screenshot like:
ScreenCapture sc = new ScreenCapture();
// capture entire screen, and save it to a file
Image img = sc.CaptureScreen();
You will need to hide your form first though, so that will likely cause a flicker.
Related
I have a multiple TableLayoutPanels on MDI forms. Each table layout has multiple controls (including ObjectListView) and only the top-most is set to visible.
I implemented the solution as given in this SO answer and it works great when opening a form for the first time, because there is no flickering on the layout's child controls whilst resizing.
I also added:
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
foreach (CoTableLayoutPanel tlp in this.Controls)
{
if (tlp != null)
{
tlp.BeginUpdate();
tlp.Size = firstLayout.Size;
tlp.EndUpdate();
}
}
}
I do the above so that when I switch layouts within the form they are already all the correct size and I avoid further flickering whilst resizing. This also works fine.
However, when I have an ObjectListView control on this derived table layout, and I switch between forms, the control is partially drawn (especially the border), a black background is shown quickly and also the last column is resized everytime (column's FillsFreeSpace property).
If I use the standard TableLayoutPanel, the list control behaves as expected, ie. no artifacts, no black background shown, no column resizing. However, I get the flickering back when I open the form.
The ObjectListView has properties such as Invalidate and Refresh, and I've tried to call these on the form's OnGotFocus method. The problem still persists.
Is this a problem of the ObjectListView or can I fix the problem from within the derived table layout?
EDIT
The problem is caused by this method:
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= WS_EX_COMPOSITED;
return cp;
}
}
Commenting out this method will make the ObjectListView work as expected but the flickering returns.
Any workaround to the above method?
What appears as black is just the unpainted part of the window. If you have a lot of controls on a form then repainting them all takes enough time to produce noticeable artifacts. It is black when you use a layered window, you get one when you use the Opacity or TransparencyKey property. If you don't then the artifact tends to be a lot less objectionable because the unpainted parts tend to be white.
Most programmers assume it is a flicker issue, but it is not and the DoubleBuffered property cannot solve it. Suppressing it would require double-buffering all of the controls, using the same buffer. Roughly the approach taken by WPF.
Double-buffering everything is what the WS_EX_COMPOSITED style flag does. Purely done by the operating system, .NET is not involved. It is an early version of Aero, first available on XP. The OS creates a bitmap for the toplevel window and tells its controls to draw into that bitmap instead of directly to the video framebuffer. When the painting is done, it blits the bitmap in one whack to the framebuffer. Does not make the painting any faster, but the user perceives it as very smooth. If you use the DoubleBuffered property now then you want to turn that off.
I discovered the technique for Winforms btw, published it first in a forum post on the MSDN forums, 10 years ago already. It has been copied many, many times since, spread like a wildfire. I have not gotten a lot of negative feedback about it, it solves the problem for the vast majority of programmers. The only troublemaker I know of is TabControl, specifically when it has too many tabs and displays the left/right navigation glyphs. Its visual styles renderer has never not been a problem, when those glyphs show up, it start repainting itself over-and-over again. Looks like a very rapid flicker, you can't miss it. Easy to work around.
So no, forge ahead and use my solution. It is a good one and doing it any other way would be highly untrivial. You'd basically have to re-invent WPF to completely eliminate it, using windowless controls that draw themselves on the parent window's surface. VB6 did this btw, one reason why VB6 UI looks so dated. Replacing controls with code can get you there too (Label and PictureBox are particularly wasteful) but that tends to take a lot of code and you cannot possibly beat the 3 lines of code solution :)
I'm working on a project that has a mainform and then many other forms laid over it. Whenever I create a new form and lay it over the mainForm, I had an issue with flicker. All of my forms are set to not have a FormBorderStyle, but whenever I load the form, the border would flash for just a second before disappearing. I did some research on this and found a function that I was able to use that reduced the flickering by overriding the create parameters (see below)
protected override CreateParams CreateParams
{
//this is used to reduce the flicker created when loading a new form
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
And this works great to get rid of that flicker that occurs whenever I create the form, however it forces the form to need to resize. So what ends up happening is my screen flickers differently. This time it loads the form up to the original size, and THEN fills the screen, whereas I need it to load up all the information, and THEN display it to the screen.
Does anyone know of a function that basically tells the form to wait until a certain point before showing itself?
I'm trying to make the program full screen and take up the entire space of the main monitor, and so I can't really do anything with starting the form out larger or something like that since it would just resize on smaller screens, and that still wouldn't get rid of the load - resize aspects.
I DO have all of the load functions overridden to change locations and sizes of all the forms so it fills the screen up correctly, could that be causing it?
I have some questions regarding flickering and the general flow of UI updates
when multiple objects are involved.
Background:
We use a C# assembly inside a Progress ABL application to drive the UI.
Additionally we use Infragistics controls, as they can easily be styled.
We have a kind of self made MDI. (tab) panels contain multiple user controls which
are used as "Forms".
(Note: Real forms could not be used, because a non-toplevel form has problems handling mouse clicks correctly)
The tabs are realized by an Infragistics UltraDockManager.
On such an UserControl "form" a Progress .Net Control is placed which embeds real Progress Frames.
So we could re-use existing Progress Forms instead of rebuilding them in .NET from scratch.
To make things worse we use a tiled backgroundimage on the (tab) panels.
Now we have the problem that the contents of the panel (derived from Infragistics.Win.Misc.UltraPanel)
are flickering heavily. (Especially when the main application is resized)
Some controls (especially the Progress controls) are deleted and redrawn.
I could get rid of the flickering by using WS_CLIPCHILDREN and WS_EX_COMPOSITED
(as mentioned by Hans Passant in How to fix the flickering in User controls ) in the panel and the UserControl.
But the application was noticeably slower than before, so I had to removed the code again.
I tried setting the Styles of the panel and the UserControl, with absolutely no effect.
this.SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.ResizeRedraw, true);
Trying to ignore WM_ERASEBKGND in the panel and the UserControl seems not to have an effect either.
protected override void WndProc(ref Message m)
{
if (m.Msg != WM_ERASEBKGND)
{
base.WndProc(ref m);
}
}
What would be the correct way to handle the redrawing of all those objects to reduce the flickering?
I thought about using SendMessage and WM_SETREDRAW. But I am not sure where to put it
in this scenario and whether it would really help or not.
When the application is resized each UserControl seems to be repainted, regardless of its visibility. (Could be scrolled half way out of view)
Why isn't there some kind of clipping going on? The panel should know that some areas don't need to be refreshed or am I mistaken?
I would appreciate any suggestion.
We finally managed to get rid of the flickering.
There were two factors which caused the problem:
Use of Color:Transparent
Use of the AutoScroll feature of the panels
We removed the transparent color and disabled the AutoScroll during resize.
try using this in the program
It may not give the speed in between the redraw events
but it will certainly will look graceful as redrawn activity will take place after all the painting is done
you just need to add it into your main form.
protected override System.Windows.Forms.CreateParams CreateParams
{
get
{
System.Windows.Forms.CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000;
return cp;
}
}
have a look at this and this
They are about calling SuspendLayout on Resize
I've created a semi-transparent form (60% opacity with black background color) that my app launches, maximized, over the entire screen. Basically, it casts a gray color on the entire desktop.
When the user mouses-over a window on the desktop, I want to get that window's handle (hWnd).
The easy way to do this, which is working for me, is:
Temporarily hide my form (OR, temporarily set my form's opacity to 0.0)
Call [GetCursorPos][1]
Call [WindowFromPoint][2]
Show my form again
The problem with this approach is that my form / the screen blinks, which I don't like.
I've tried to fix this in two ways:
I figure there should be a way to get the hWnd of the window directly underneath my form by calling ChildWindowFromPointEx (passing-in the hWnd of the desktop and CWP_SKIPTRANSPARENT), but it doesn't seem to work. I also played with [ChildWindowFromPoint][4] and [RealChildWindowFromPoint][5] with no success. (P.S. Raymond Chen discusses the differences between these calls, here and it seems to me that ChildWindowFromPointEx is designed to do exactly what I need)
I tried preventing the entire desktop from refreshing (kind of "freezing" the screen momentarily) by using (1) SendMessage(GetDesktopWindow(), WM_SETREDRAW, false, 0) before I hide my form and (2) SendMessage(GetDesktopWindow(), WM_SETREDRAW, true, 0) after I hide my form. This didn't work quite right: some areas of the screen would freeze, some weird black blocks would appear, etc. I do know, however, that (1) does work, because one time I called (1) and didn't call (2) and my desktop appeared completely frozen (had to reboot, even TaskMgr didn't render correctly). I also tried using SuspendLayout and ResumeLayout on my form, but I don't think they are meant to handle my case.
Any help would be greatly appreciated.
You can do the checking yourself since your need to customise beyond that what the standard functions offer.
Call EnumWindows() to get a list of top-level windows.
Remove your semi-transparent window from this list.
For each window in the list use PtInRegion() to determine whether or not the mouse is over the window. Remove any windows that don't fit the bill.
Use GetNextWindow(), starting from one of the remaining windows to walk the z-order and find out which of the candidates is at the top.
In my project, I create a form with the opacity controlled by the user. If the form was fully transparent the mouse events 'fell through' (without my intervention), otherwise my form handled them.
After reading this question and overriding the CreateParams property to set the WS_EX_TRANSPARENT flag on my form, it now allows mouse events to fall through when the opacity is any value <255.
This is exactly what I want, but it concerns me that I don't understand why it works.
From what I've read, WS_EX_TRANSPARENT is meant to make the form appear transparent by 'stealing bits' from the form below it in its Paint method, therefore:
The Paint method of my form and all the controls in it should never be called, right? As WS_EX_TRANSPARENT should cause Windows to override them, so why does it affect input handling but not the drawing of my form?
The opacity should have no impact on the handling of mouse events, as if Paint is being overridden the 'local' opacity should not matter, no?
Could someone explain, what this flag really does? How does it work?
WS_EX_TRANSPARENT makes your events (like mouse clicks) fall through your window, amongst other things. Opacity is a separate concept, it instructs window manager to apply alphablending when drawing your form. Those two things are not related, but when you combine them you get the effect you need in your case.
So:
Paint method of your form is called normally as it should, opacity has nothing to do with it.
Opacity does not have anything to do with mouse events, as described in the first paragraph.
It makes the window invisible to mouse events, or -as Microsoft puts it- it doesn't obscure the windows below. I believe it doesn't actually steal pixels from the windows below, but Windows itself will probably blend those two pictures together, using the level of transparancy you supplied.
Transparent windows can be useful for showing some progress or a splash screen, but you'll have to program a way to close them, because just click the X won't work since the mouse click will pass through it.
So not only does it change the level of visual transparency, but it modifies the behaviour too. I wonder where you would have read otherwise.
[edit]
Don't Windows in C# just have an opacity property?