Creating semi-transparent panels/controls. Is there a foolproof way? - c#

I am trying to create a semi-transparent control derived from System.Windows.Forms.Panel. [Edit: Basically what I want to achieve is this]:
I have gone through numerous web articles as well as SO questions and have come up with this:
class SeeThroughPanel : Panel
{
public SeeThroughPanel()
{
}
protected override CreateParams CreateParams {
get {
var cp = base.CreateParams;
cp.ExStyle |= 0x00000020;
return cp;
}
}
protected override void OnPaint(PaintEventArgs e)
{
//base.OnPaint(e);
e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(50, 0, 0, 0)), this.ClientRectangle);
}
}
Which results this (Note that this code too does not make it really semi-transparent. The button is displayed in full color, unlike in above image):
However, as soon as I do something that causes repaint of any other controls within the bounds of the semi-transparent panel, it is overdrawn (In this case, I have scrolled the underlying control):
So my question is, how can I avoid this? There has to be some way. After all, web browsers handle this kind of rendering all the time.

I have done a few tests now.
You are mixiing several problems into one, so let's take them apart:
No WebBrowser is written in C# using Winforms. So this is no reason why this must be possible.
Your button almost certainly is not in the Form's Controls collection. You need to either script it to be or use a little UI trick (*) to place it over another Control without adding it to it.
When you scroll you will see whatever the scrolling control report as its surface.
Here are two screenshots:
This is after startup:
..and this is after I scrolled a little to the right:
I have use this code to make sure the Z-order is right:
button1.Parent = this;
panel1.BringToFront();
button1.BringToFront();
seeThroughPanel1.BringToFront();
Note how the the space of the button is spared; this shows how the old surface is being used.
To work around this you would have to get at the current Form surfacse (maybe by Control.DrawToBitmap) and then use the right part of that to paint your 'semi-transparent' panel. Of course it would have to hide before you capture the form's current surface.
Of all terrible ideas I have had, this seems to be one of the worst..
* The trick is to move it over the container with the keyboard, not the mouse; with this trick it just moves without chnging its parent container. Also useful for placing a Control on top of the tabs of a TabControl... But I was too lazy for that, so I coded it..

The transparency feature of the Windows Forms control leaves much to be desired and is a blatant fudge. The control is not really transparent, it just pretends to be by looking at the background of it's parent control and copying the appropriate portion of the image or background onto it's own surface during the OnPaintBackground method.
This means that a "transparent" control placed on top of another on the same parent will in fact obscure the other child controls. Figure 1 shows this effect in action.
The panel control to the right of the form obscures the PictureBox control and shows only the background of the parent.
In order to make a truly transparent control we need to do a couple of things. Firstly, it's necessary to change the behaviour of the window by giving it a WS_EX_TRANSPARENT style. This is accomplished by overriding the CreateParams property so that the correct window style is included when the control is instantiated. The listing below shows this property override.
protected override CreateParams CreateParams
{
get
{
CreateParams cp=base.CreateParams;
cp.ExStyle|=0x00000020; //WS_EX_TRANSPARENT
return cp;
}
}
The second thing we need to do is to invalidate the parent of the control, not the control itself, whenever we need to update the graphics. This ensures that whatever is behind the control gets painted before we need to do our own graphics output. To do this, a routine such as that shown in the following listing is needed.
protected void InvalidateEx()
{
if(Parent==null)
return;
Rectangle rc=new Rectangle(this.Location,this.Size);
Parent.Invalidate(rc,true);
}
Finally, we need to ensure that the background draw routine does not mess up the recently repainted parent-form content by stubbing out the OnPaintBackground method.
protected override void OnPaintBackground(PaintEventArgs pevent)
{
//do not allow the background to be painted
}
Now the control is ready for the rest of its modification. It is important to note at this point that transparent controls are not suitable for double buffering using the standard SetStyle method. The memory bitmap which is provided to your code has an opaque background and does not allow the carefully retained parent pixels to show through.
To complete this article, a simple control that does nothing but paint moving ellipses on its surface is shown in the following listing.
using System;
using System.Drawing;
using System.Windows.Forms;
internal class SeeThroughPanel : Panel
{
public SeeThroughPanel()
{
}
protected void TickHandler(object sender, EventArgs e)
{
this.InvalidateEx();
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x00000020; //WS_EX_TRANSPARENT
return cp;
}
}
protected void InvalidateEx()
{
if (Parent == null)
{
return;
}
Rectangle rc = new Rectangle(this.Location, this.Size);
Parent.Invalidate(rc, true);
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
}
private Random r = new Random();
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(128, 0, 0, 0)), this.ClientRectangle);
}
}

Related

form slow loading when go to another form in C# [duplicate]

In my application i am constantly moving from one control to another. I have created no. of user controls, but during navigation my controls gets flicker. it takes 1 or 2 sec to update. I tried to set this
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
or
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
but it didn't help... Each control has same background image with different controls.
So what is the solution for it..
Thanks.
It is not the kind of flicker that double-buffering can solve. Nor BeginUpdate or SuspendLayout. You've got too many controls, the BackgroundImage can make it a lot worse.
It starts when the UserControl paints itself. It draws the BackgroundImage, leaving holes where the child control windows go. Each child control then gets a message to paint itself, they'll fill in the hole with their window content. When you have a lot of controls, those holes are visible to the user for a while. They are normally white, contrasting badly with the BackgroundImage when it is dark. Or they can be black if the form has its Opacity or TransparencyKey property set, contrasting badly with just about anything.
This is a pretty fundamental limitation of Windows Forms, it is stuck with the way Windows renders windows. Fixed by WPF btw, it doesn't use windows for child controls. What you'd want is double-buffering the entire form, including the child controls. That's possible, check my code in this thread for the solution. It has side-effects though, and doesn't actually increase painting speed. The code is simple, paste this in your form (not the user control):
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
There are many things you can do to improve painting speed, to the point that the flicker isn't noticeable anymore. Start by tackling the BackgroundImage. They can be really expensive when the source image is large and needs to be shrunk to fit the control. Change the BackgroundImageLayout property to "Tile". If that gives a noticeable speed-up, go back to your painting program and resize the image to be a better match with the typical control size. Or write code in the UC's OnResize() method to create a properly sized copy of the image so that it doesn't have to be resized every time the control repaints. Use the Format32bppPArgb pixel format for that copy, it renders about 10 times faster than any other pixel format.
Next thing you can do is prevent the holes from being so noticeable and contrasting badly with the image. You can turn off the WS_CLIPCHILDREN style flag for the UC, the flag that prevents the UC from painting in the area where the child controls go. Paste this code in the UserControl's code:
protected override CreateParams CreateParams {
get {
var parms = base.CreateParams;
parms.Style &= ~0x02000000; // Turn off WS_CLIPCHILDREN
return parms;
}
}
The child controls will now paint themselves on top of the background image. You might still see them painting themselves one by one, but the ugly intermediate white or black hole won't be visible.
Last but not least, reducing the number of child controls is always a good approach to solve slow painting problems. Override the UC's OnPaint() event and draw what is now shown in a child. Particular Label and PictureBox are very wasteful. Convenient for point and click but their light-weight alternative (drawing a string or an image) takes only a single line of code in your OnPaint() method.
This is a real issue, and the answer Hans Passant gave is great for saving the flicker. However, there are side effects as he mentioned, and they can be ugly (UI ugly). As stated, "You can turn off the WS_CLIPCHILDREN style flag for the UC", but that only turns it off for a UC. The components on the main form still have issues.
Example, a panel scroll bar doesn't paint, because it is technically in the child area. However the child component doesn't draw the scroll bar, so it doesn't get painted until mouse over (or another event triggers it).
Also, animated icons (changing icons in a wait loop) doesn't work. Removing icons on a tabPage.ImageKey doesn't resize/repaint the other tabPages appropriately.
So I was looking for a way to turn off the WS_CLIPCHILDREN on initial painting so my Form will load nicely painted, or better yet only turn it on while resizing my form with a lot of components.
The trick is to get the application to call CreateParams with the desired WS_EX_COMPOSITED/WS_CLIPCHILDREN style. I found a hack here (https://web.archive.org/web/20161026205944/http://www.angryhacker.com/blog/archive/2010/07/21/how-to-get-rid-of-flicker-on-windows-forms-applications.aspx) and it works great. Thanks AngryHacker!
I put the TurnOnFormLevelDoubleBuffering() call in the form ResizeBegin event and TurnOffFormLevelDoubleBuffering() call in the form ResizeEnd event (or just leave it WS_CLIPCHILDREN after it is initially painted properly.)
int originalExStyle = -1;
bool enableFormLevelDoubleBuffering = true;
protected override CreateParams CreateParams
{
get
{
if (originalExStyle == -1)
originalExStyle = base.CreateParams.ExStyle;
CreateParams cp = base.CreateParams;
if (enableFormLevelDoubleBuffering)
cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
else
cp.ExStyle = originalExStyle;
return cp;
}
}
public void TurnOffFormLevelDoubleBuffering()
{
enableFormLevelDoubleBuffering = false;
this.MaximizeBox = true;
}
If you are doing any custom painting in the control (i.e. overriding OnPaint) you can try the double buffering yourself.
Image image;
protected override OnPaint(...) {
if (image == null || needRepaint) {
image = new Bitmap(Width, Height);
using (Graphics g = Graphics.FromImage(image)) {
// do any painting in image instead of control
}
needRepaint = false;
}
e.Graphics.DrawImage(image, 0, 0);
}
And invalidate your control with a property NeedRepaint
Otherwise the above answer with SuspendLayout and ResumeLayout is probably what you want.
Put the code bellow in your constructor or OnLoad event and if you're using some sort of custom user control that having sub controls, you'll need to make sure that these custom controls are also double buffered (even though in MS documentation they say it's set to true by default).
If you're making a custom control, you might want to add this flag into your ctor:
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
Optionally you can use this code in your Form/Control:
foreach (Control control in Controls)
{
typeof(Control).InvokeMember("DoubleBuffered",
BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
null, control, new object[] { true });
}
We iterating through all the controls in the form/control and accessing their DoubleBuffered property and then we change it to true in order to make each control on the form double buffered. The reason we do reflection here, is because imagine you have a control that has child controls that are not accessible, that way, even if they're private controls, we'll still change their property to true.
More information about double buffering technique can be found here.
There is another property I usually override to sort this problem:
protected override CreateParams CreateParams
{
get
{
CreateParams parms = base.CreateParams;
parms.ExStyle |= 0x00000020; // WS_EX_COMPOSITED
return parms;
}
}
WS_EX_COMPOSITED - Paints all descendants of a window in bottom-to-top painting order using double-buffering.
You can find more of these style flags here.
Hope that helps!
Try BeginUpdate/EndUpdate OR SuspendLayout/ResumeLayout methods.
See following
How to fix nested winform control flicker issues
Flickering during updates to Controls in WinForms (e.g. DataGridView)
Just to add to the answer Hans gave:
(TLDR version: Transparency is heavier than you think, use only solid colors everywhere)
If WS_EX_COMPOSITED, DoubleBuffered and WS_CLIPCHILDREN did not solve your flicker (for me WS_CLIPCHILDREN made it even worse), try this: go through ALL your controls and all your code, and wherever you have Any transparency or semi-transparency for BackColor, ForeColor, or any other color, just remove it, use only solid colors. In most of the cases where you think you just have to use transparency, you don't. Re-design your code and controls, and use solid colors.
I had terrible, terrible flickering and the program was running sluggish. Once I removed transparency it sped up significantly, and there is 0 flicker.
EDIT: To add further, I just discovered that WS_EX_COMPOSITED doesn't have to be window-wide, it could be applied just to specific controls! This saved me a lot of trouble. Just make a custom control inherited from whatever control you need, and paste the already posted override for WS_EX_COMPOSITED. This way you get low-level double-buffer on this control only, avoiding the nasty side-effects in the rest of the application!
On the main form or user control where background image resides set the BackgroundImageLayout property to Center or Stretch. You will notice a big difference when the user control is rendering.
I tried to add this as a comment but I don't have enough points. This is the only thing that's ever helped my flickering problems so many thanks to Hans for his post. For anyone that's using c++ builder like myself here's the translation
Add the CreateParams declaration to your application's main form .h file e.g.
class TYourMainFrom : public TForm
{
protected:
virtual void __fastcall CreateParams(TCreateParams &Params);
}
and add this to your .cpp file
void __fastcall TYourMainForm::CreateParams(TCreateParams &Params)
{
Params.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
TForm::CreateParams(Params);
}
I know this question is very old, but want to give my experience on it.
I had a lot of problems with Tabcontrol flickering in a form with overrided OnPaint and/or OnPaintBackGround in Windows 8 using .NET 4.0.
The only think that worked has been NOT USE the Graphics.DrawImage method in OnPaint overrides, in other words, when draw was done directly to the Graphics provided by the PaintEventArgs, even painting all the rectangle, the flickering dissapeared. But if call the DrawImage method, even drawing a clipped Bitmap, (created for double buffering) the flicker appears.
Hope it helps!
I combined this flicker fix and this font fix, then I had to add a bit of my own code to start a timer on paint to Invalidate the TabControl when it goes offscreen and back, etc..
All three make this:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class TabControlEx:TabControl
{
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
private const int WM_PAINT = 0x0f;
private const int WM_SETFONT = 0x30;
private const int WM_FONTCHANGE = 0x1d;
private System.Drawing.Bitmap buffer;
private Timer timer = new Timer();
public TabControlEx()
{
timer.Interval = 1;
timer.Tick += timer_Tick;
this.SetStyle(ControlStyles.UserPaint | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
}
void timer_Tick(object sender, EventArgs e)
{
this.Invalidate();
this.Update();
timer.Stop();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_PAINT) timer.Start();
base.WndProc(ref m);
}
protected override void OnPaint(PaintEventArgs pevent)
{
this.SetStyle(ControlStyles.UserPaint, false);
base.OnPaint(pevent);
System.Drawing.Rectangle o = pevent.ClipRectangle;
System.Drawing.Graphics.FromImage(buffer).Clear(System.Drawing.SystemColors.Control);
if (o.Width > 0 && o.Height > 0)
DrawToBitmap(buffer, new System.Drawing.Rectangle(0, 0, Width, o.Height));
pevent.Graphics.DrawImageUnscaled(buffer, 0, 0);
this.SetStyle(ControlStyles.UserPaint, true);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
buffer = new System.Drawing.Bitmap(Width, Height);
}
protected override void OnCreateControl()
{
base.OnCreateControl();
this.OnFontChanged(EventArgs.Empty);
}
protected override void OnFontChanged(EventArgs e)
{
base.OnFontChanged(e);
IntPtr hFont = this.Font.ToHfont();
SendMessage(this.Handle, WM_SETFONT, hFont, (IntPtr)(-1));
SendMessage(this.Handle, WM_FONTCHANGE, IntPtr.Zero, IntPtr.Zero);
this.UpdateStyles();
}
}
I'm not the creator but from what I understand the bitmap does all the bug bypassing.
This was the only thing that definitively solved TabControl (with Icons) flicker for me.
difference result video: vanilla tabcontrol vs tabcontrolex
http://gfycat.com/FineGlitteringDeermouse
ps. you will need to set HotTrack = true, because this fixes that bug too
Did you try Control.DoubleBuffered Property?
Gets or sets a value indicating whether this control should redraw its surface using a secondary buffer to reduce or prevent flicker.
Also this and this might help.
There is no need of any Double buffering and all that stuff guys...
A Simple solution...
If you are using MDI Interface, just paste the code below in the main form. It will remove all flickering from the pages. However some pages which require more time for loading will showup in 1 or 2 secs. But this is better than showing a flickering page in which each item comes one by one.
This is the only best solution for whole application. See the code to put in the main form:
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}

Prebuffering Form when minimized [duplicate]

In my application i am constantly moving from one control to another. I have created no. of user controls, but during navigation my controls gets flicker. it takes 1 or 2 sec to update. I tried to set this
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
or
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
but it didn't help... Each control has same background image with different controls.
So what is the solution for it..
Thanks.
It is not the kind of flicker that double-buffering can solve. Nor BeginUpdate or SuspendLayout. You've got too many controls, the BackgroundImage can make it a lot worse.
It starts when the UserControl paints itself. It draws the BackgroundImage, leaving holes where the child control windows go. Each child control then gets a message to paint itself, they'll fill in the hole with their window content. When you have a lot of controls, those holes are visible to the user for a while. They are normally white, contrasting badly with the BackgroundImage when it is dark. Or they can be black if the form has its Opacity or TransparencyKey property set, contrasting badly with just about anything.
This is a pretty fundamental limitation of Windows Forms, it is stuck with the way Windows renders windows. Fixed by WPF btw, it doesn't use windows for child controls. What you'd want is double-buffering the entire form, including the child controls. That's possible, check my code in this thread for the solution. It has side-effects though, and doesn't actually increase painting speed. The code is simple, paste this in your form (not the user control):
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
There are many things you can do to improve painting speed, to the point that the flicker isn't noticeable anymore. Start by tackling the BackgroundImage. They can be really expensive when the source image is large and needs to be shrunk to fit the control. Change the BackgroundImageLayout property to "Tile". If that gives a noticeable speed-up, go back to your painting program and resize the image to be a better match with the typical control size. Or write code in the UC's OnResize() method to create a properly sized copy of the image so that it doesn't have to be resized every time the control repaints. Use the Format32bppPArgb pixel format for that copy, it renders about 10 times faster than any other pixel format.
Next thing you can do is prevent the holes from being so noticeable and contrasting badly with the image. You can turn off the WS_CLIPCHILDREN style flag for the UC, the flag that prevents the UC from painting in the area where the child controls go. Paste this code in the UserControl's code:
protected override CreateParams CreateParams {
get {
var parms = base.CreateParams;
parms.Style &= ~0x02000000; // Turn off WS_CLIPCHILDREN
return parms;
}
}
The child controls will now paint themselves on top of the background image. You might still see them painting themselves one by one, but the ugly intermediate white or black hole won't be visible.
Last but not least, reducing the number of child controls is always a good approach to solve slow painting problems. Override the UC's OnPaint() event and draw what is now shown in a child. Particular Label and PictureBox are very wasteful. Convenient for point and click but their light-weight alternative (drawing a string or an image) takes only a single line of code in your OnPaint() method.
This is a real issue, and the answer Hans Passant gave is great for saving the flicker. However, there are side effects as he mentioned, and they can be ugly (UI ugly). As stated, "You can turn off the WS_CLIPCHILDREN style flag for the UC", but that only turns it off for a UC. The components on the main form still have issues.
Example, a panel scroll bar doesn't paint, because it is technically in the child area. However the child component doesn't draw the scroll bar, so it doesn't get painted until mouse over (or another event triggers it).
Also, animated icons (changing icons in a wait loop) doesn't work. Removing icons on a tabPage.ImageKey doesn't resize/repaint the other tabPages appropriately.
So I was looking for a way to turn off the WS_CLIPCHILDREN on initial painting so my Form will load nicely painted, or better yet only turn it on while resizing my form with a lot of components.
The trick is to get the application to call CreateParams with the desired WS_EX_COMPOSITED/WS_CLIPCHILDREN style. I found a hack here (https://web.archive.org/web/20161026205944/http://www.angryhacker.com/blog/archive/2010/07/21/how-to-get-rid-of-flicker-on-windows-forms-applications.aspx) and it works great. Thanks AngryHacker!
I put the TurnOnFormLevelDoubleBuffering() call in the form ResizeBegin event and TurnOffFormLevelDoubleBuffering() call in the form ResizeEnd event (or just leave it WS_CLIPCHILDREN after it is initially painted properly.)
int originalExStyle = -1;
bool enableFormLevelDoubleBuffering = true;
protected override CreateParams CreateParams
{
get
{
if (originalExStyle == -1)
originalExStyle = base.CreateParams.ExStyle;
CreateParams cp = base.CreateParams;
if (enableFormLevelDoubleBuffering)
cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
else
cp.ExStyle = originalExStyle;
return cp;
}
}
public void TurnOffFormLevelDoubleBuffering()
{
enableFormLevelDoubleBuffering = false;
this.MaximizeBox = true;
}
If you are doing any custom painting in the control (i.e. overriding OnPaint) you can try the double buffering yourself.
Image image;
protected override OnPaint(...) {
if (image == null || needRepaint) {
image = new Bitmap(Width, Height);
using (Graphics g = Graphics.FromImage(image)) {
// do any painting in image instead of control
}
needRepaint = false;
}
e.Graphics.DrawImage(image, 0, 0);
}
And invalidate your control with a property NeedRepaint
Otherwise the above answer with SuspendLayout and ResumeLayout is probably what you want.
Put the code bellow in your constructor or OnLoad event and if you're using some sort of custom user control that having sub controls, you'll need to make sure that these custom controls are also double buffered (even though in MS documentation they say it's set to true by default).
If you're making a custom control, you might want to add this flag into your ctor:
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
Optionally you can use this code in your Form/Control:
foreach (Control control in Controls)
{
typeof(Control).InvokeMember("DoubleBuffered",
BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
null, control, new object[] { true });
}
We iterating through all the controls in the form/control and accessing their DoubleBuffered property and then we change it to true in order to make each control on the form double buffered. The reason we do reflection here, is because imagine you have a control that has child controls that are not accessible, that way, even if they're private controls, we'll still change their property to true.
More information about double buffering technique can be found here.
There is another property I usually override to sort this problem:
protected override CreateParams CreateParams
{
get
{
CreateParams parms = base.CreateParams;
parms.ExStyle |= 0x00000020; // WS_EX_COMPOSITED
return parms;
}
}
WS_EX_COMPOSITED - Paints all descendants of a window in bottom-to-top painting order using double-buffering.
You can find more of these style flags here.
Hope that helps!
Try BeginUpdate/EndUpdate OR SuspendLayout/ResumeLayout methods.
See following
How to fix nested winform control flicker issues
Flickering during updates to Controls in WinForms (e.g. DataGridView)
Just to add to the answer Hans gave:
(TLDR version: Transparency is heavier than you think, use only solid colors everywhere)
If WS_EX_COMPOSITED, DoubleBuffered and WS_CLIPCHILDREN did not solve your flicker (for me WS_CLIPCHILDREN made it even worse), try this: go through ALL your controls and all your code, and wherever you have Any transparency or semi-transparency for BackColor, ForeColor, or any other color, just remove it, use only solid colors. In most of the cases where you think you just have to use transparency, you don't. Re-design your code and controls, and use solid colors.
I had terrible, terrible flickering and the program was running sluggish. Once I removed transparency it sped up significantly, and there is 0 flicker.
EDIT: To add further, I just discovered that WS_EX_COMPOSITED doesn't have to be window-wide, it could be applied just to specific controls! This saved me a lot of trouble. Just make a custom control inherited from whatever control you need, and paste the already posted override for WS_EX_COMPOSITED. This way you get low-level double-buffer on this control only, avoiding the nasty side-effects in the rest of the application!
On the main form or user control where background image resides set the BackgroundImageLayout property to Center or Stretch. You will notice a big difference when the user control is rendering.
I tried to add this as a comment but I don't have enough points. This is the only thing that's ever helped my flickering problems so many thanks to Hans for his post. For anyone that's using c++ builder like myself here's the translation
Add the CreateParams declaration to your application's main form .h file e.g.
class TYourMainFrom : public TForm
{
protected:
virtual void __fastcall CreateParams(TCreateParams &Params);
}
and add this to your .cpp file
void __fastcall TYourMainForm::CreateParams(TCreateParams &Params)
{
Params.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
TForm::CreateParams(Params);
}
I know this question is very old, but want to give my experience on it.
I had a lot of problems with Tabcontrol flickering in a form with overrided OnPaint and/or OnPaintBackGround in Windows 8 using .NET 4.0.
The only think that worked has been NOT USE the Graphics.DrawImage method in OnPaint overrides, in other words, when draw was done directly to the Graphics provided by the PaintEventArgs, even painting all the rectangle, the flickering dissapeared. But if call the DrawImage method, even drawing a clipped Bitmap, (created for double buffering) the flicker appears.
Hope it helps!
I combined this flicker fix and this font fix, then I had to add a bit of my own code to start a timer on paint to Invalidate the TabControl when it goes offscreen and back, etc..
All three make this:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class TabControlEx:TabControl
{
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
private const int WM_PAINT = 0x0f;
private const int WM_SETFONT = 0x30;
private const int WM_FONTCHANGE = 0x1d;
private System.Drawing.Bitmap buffer;
private Timer timer = new Timer();
public TabControlEx()
{
timer.Interval = 1;
timer.Tick += timer_Tick;
this.SetStyle(ControlStyles.UserPaint | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
}
void timer_Tick(object sender, EventArgs e)
{
this.Invalidate();
this.Update();
timer.Stop();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_PAINT) timer.Start();
base.WndProc(ref m);
}
protected override void OnPaint(PaintEventArgs pevent)
{
this.SetStyle(ControlStyles.UserPaint, false);
base.OnPaint(pevent);
System.Drawing.Rectangle o = pevent.ClipRectangle;
System.Drawing.Graphics.FromImage(buffer).Clear(System.Drawing.SystemColors.Control);
if (o.Width > 0 && o.Height > 0)
DrawToBitmap(buffer, new System.Drawing.Rectangle(0, 0, Width, o.Height));
pevent.Graphics.DrawImageUnscaled(buffer, 0, 0);
this.SetStyle(ControlStyles.UserPaint, true);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
buffer = new System.Drawing.Bitmap(Width, Height);
}
protected override void OnCreateControl()
{
base.OnCreateControl();
this.OnFontChanged(EventArgs.Empty);
}
protected override void OnFontChanged(EventArgs e)
{
base.OnFontChanged(e);
IntPtr hFont = this.Font.ToHfont();
SendMessage(this.Handle, WM_SETFONT, hFont, (IntPtr)(-1));
SendMessage(this.Handle, WM_FONTCHANGE, IntPtr.Zero, IntPtr.Zero);
this.UpdateStyles();
}
}
I'm not the creator but from what I understand the bitmap does all the bug bypassing.
This was the only thing that definitively solved TabControl (with Icons) flicker for me.
difference result video: vanilla tabcontrol vs tabcontrolex
http://gfycat.com/FineGlitteringDeermouse
ps. you will need to set HotTrack = true, because this fixes that bug too
Did you try Control.DoubleBuffered Property?
Gets or sets a value indicating whether this control should redraw its surface using a secondary buffer to reduce or prevent flicker.
Also this and this might help.
There is no need of any Double buffering and all that stuff guys...
A Simple solution...
If you are using MDI Interface, just paste the code below in the main form. It will remove all flickering from the pages. However some pages which require more time for loading will showup in 1 or 2 secs. But this is better than showing a flickering page in which each item comes one by one.
This is the only best solution for whole application. See the code to put in the main form:
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}

Black flashbacks when resize wpf element host

I have a WPF UserControl that displayed in a Winform as Element Host.
When I change the screen size quickly or resize it I see a black flashbacks in the background until the UserControl comes to the appropriate size.
I read about it in several places.
All the places I read them talk it happens only in the UserControl Load and bring solutions accordingly.
One question I've seen talking about it happening on Resize. But the solution offered there is as well to Load.
Black background on resizing elementhost
I tried on Resize of the screen to perform the following: UserControl.CreateGraphics (); , it does not leaves black lines like the above answer said.
I guess it's because I used it in Resize and not on Load.
Besides, I could not find anything.
If anyone encountered this and found a solution I would love to answer.
We usually face flickering issues while developing windows application with forms having many controls on it. A very neat way to get rid of this flickering is double buffering the whole form and its child controls. However, this would not speed up the control painting but it will hold the screen for a while and just show the updated screen instead of flickering. To implement this we need to enable the WS_EX_COMPOSITED flag. Just add the following code to your form's code.
C#:
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000;
// Turn on WS_EX_COMPOSITED
return cp;
}
}
VB .net:
Protected Overrides ReadOnly Property CreateParams() As CreateParams
Get
Dim cp As CreateParams = MyBase.CreateParams
cp.ExStyle = cp.ExStyle Or &H2000000
Return cp
End Get
End Property
What helped me in the end it following lines of code (in Winform):
protected override void OnResize(EventArgs e)
{
this.SuspendLayout();
base.OnResize(e);
this.ResumeLayout();
}

Controls not being drawn at the same time

I have a form which I'm bringing up using ShowDialog which contains a couple of text boxes, labels and a button. The problem I'm having is that the text boxes are being drawn before the form itself and the other controls are drawn.
I am overriding the OnPaint method I'm not sure if this could be causing the problem:
protected override void OnPaint(PaintEventArgs e)
{
ControlPaint.DrawBorder(e.Graphics, e.ClipRectangle, Color.Black, ButtonBorderStyle.Solid);
base.OnPaint(e);
}
It's only a slight delay but it's visible and annoying. Thank you.
The form is double buffered by the way.
EDIT: I have pinpointed the issue to be the fact that the form does not have a FormBorderStyle. With the FormBorderStyle set to Sizable, this issue does not occur. However please note that having FormBorderStyle.None as my border style is necessary, so I have not found a solution yet.
Try adding this to the dialog box form:
protected override CreateParams CreateParams
{
get
{
// Activate double buffering at the form level. All child controls will be double buffered as well.
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED
return cp;
}
}

How to make a PictureBox truly transparent

I've got a PictureBox, which can be moved vertically. The image displayed in the PictureBox is a transparent GIF, so when viewed in an image viewer, it has no background.
The problem is that when I move the PictureBox in the application, the PictureBox's background moves around too strangely - almost as if the PictureBox has a background itself.
Before:
After (while moving):
Some code:
path = "C:\\note.gif";
note.Image = Image.FromFile(path);
note.Visible = true;
note.BackColor = Color.Transparent;
panel.Controls.Add(note);
I've also tried making the picturebox double buffered, but that doesn't work either.
While WinForms is poorly suited to transparency in usercontrols, it is possible. See this article here. In it the author suggests deriving from Panel rather then UserControl and overridding the OnPaintBackground method to do nothing. this will stop your background from being drawn
protected override void OnPaintBackground(PaintEventArgs pevent)
{
//do nothing
}
protected override void OnMove(EventArgs e)
{
RecreateHandle();
}
// Override the CreateParams property:
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle = 0x00000020; //WS_EX_TRANSPARENT
return cp;
}
}
Finally, overriding the OnPaint function you can draw your picturebox.
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
//Do your drawing here
}
Using this you could create a custom picturebox with transparency, although note you will get flicker and blurring if you move it around the screen in real-time.
Using this and similar techniques we managed to get a WinForms app, Premex XPort to render with a similar branding to their website. This involved multiple transparent controls, painting hacks and all sorts to get it to display correctly.
In conclusion, the reason why Winforms does this poorly is in Win32 based technologies one control owns one pixel on the screen. There is no way to truly composite pixels with transparency as you would expect in HTML or WPF. Later Windows technologies (WPF) do this particularly well so if you really wish to make heavy use of transparency in your app I would suggest moving to this platform, at least in part (WPF can be hosted within WinForms and vice versa).
Best regards,
Win32 controls are not truly transparent.
Your choices are:
Change to WPF
Use PictureBox.Region to clip the control's unwanted ("transparent") areas
See if there are 3rd party controls which'll do what you want

Categories

Resources