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
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 :)
Okay so my form has a lot of Nested "Panels" with background images and a lot of buttons on it. Every time the form is shown (Not Minimize/Restore but Hide/Show) the controls flicker as they are rendered onto the screen.
I could use opacity with a minimum delay to render the controls before setting opacity to 100%. But I was wondering if there was a better way to do this. .Net being event driven I was hoping there would be a way to detect when all controls were shown properly on the Winform before "Show"ing the winform itself.
I've tried this::DoubleBuffered=true; on the Winform but it didn't work out as expected.
Someone may explain why, but putting this below method in a form helped me to avoid flickering problems in my panels which has multiple button and image controls. Need to mention that my controls were created (dynamically) at code behind.
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
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.
I'm trying to make a web style button with user control. I need to make User Control's background transparent. How to do this without making controls invisible. Also I'll need transparent Label and PictureBox.
Trying to make something like this:
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;
All three controls you list already have the ControlStyles.SupportsTransparentBackColor style turned on. Just set the BackColor property in the designer, Web tab. It is very unclear why that's not good enough for you.
It is otherwise an illusion, Windows Forms implements it by asking the Parent control to draw itself in the OnPaintBackground() method so it provides the background pixels. One notable thing that doesn't work is overlapping controls. You only see the Parent pixels, not the pixels of the overlapped control. That's fixable but the code is fugly.
The only other transparency option is Form.TransparencyKey. That's true transparency, implemented by using overlays in the video adapter. Problem is that this only works on toplevel windows. Forms, not controls.
These restrictions are inherent in the Windows Forms rendering model, using individual windows for the controls. A web browser doesn't have that same restriction, it emulates controls by drawing them. Layers of paint, that makes transparency trivial by just not painting. WPF uses that rendering model as well.
If you mean you want "partial transparency", see this for nice solution:
Opacity of Buttons/TextBoxes - VB.NET
(setting the background color using Alpha value)
I know the above sample is VB but it applies to C# as well. :)
You can use this code :
ImageButton() is my constructor.
public ImageButton()
{
InitializeComponent();
this.SetStyle(ControlStyles.Opaque, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
}
protected override CreateParams CreateParams
{
get
{
CreateParams parms = base.CreateParams;
parms.ExStyle |= 0x20;
return parms;
}
}
So, I am pretty unfamiliar with windows forms development.
I'm trying to create a "hey I'm busy doing stuff" component that just spins a shape around. I want this control to be transient and draw on top of any other controls. The class inherits directly from Control.
So I have this in the constructor:
SetStyle(ControlStyles.Opaque, true);
and this:
protected override CreateParams CreateParams
{
get
{
CreateParams parms = base.CreateParams;
parms.ExStyle |= 0x20;
return parms;
}
}
Which gets me a control that will draw on top of other controls.
Now my problem is this. I repaint the control a few times a second to give the appearane of a smooth animation. However, I can't figure out how to clear what was drawn in the previous frame. Using e.Graphics.Clear(Color.Transparent) in OnPaint turns the whole control black.
Is there a way to just clear the drawn contents of a control?
I've noticed that Resizing the the control will clear the background.
Things that Don't Work
Overriding OnPaintBackground to do nothing. Or just calling base.OnPaintBackground. Same results.
Okay, I found the solution here:
https://web.archive.org/web/20141227200000/http://bobpowell.net/transcontrols.aspx
The Parent controls actually must be invalidated in order to retain the transparent background.
You may have to override OnPaintBackground that this article presents: http://saftsack.fs.uni-bayreuth.de/~dun3/archives/creating-a-transparent-panel-in-net/108.html
You may also need to Invalidate the control when it needs to be cleared to force OnPaintBackground to be called.