I have an application in which I can move from 1 user control to many user controls. When I was moving to many controls, I was getting flickering issues.
To solve the flickering I enabled Double Buffering via -
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
It indeed solved the issue of flickering. But introduced a new problem, In which sometimes my user control isn't painted completely. A black window is appeared. To solve this, I need to minimize and maximize the software and it appears properly.
What I think is that, while double buffering is painting all the user
controls at one shot, One or more user control is still generating its
controls. And at the time of master painting, that control was not
ready.
Here is the image for getting proper idea -
As shown in image, There is one form, containing 4 user controls in this particular scenario. Each user control further, contains other controls/user controls.
What is probably going wrong in this issue?
I solved this by changing TrasnsparencyKey in form design from black to another color but not black, try one color that you are not using.
My requirements:
A control with a background can have sprites drawn on it.
It needs to be possible to move sprites around programmatically and by setting up dragging events.
The sprite's image may have alpha transparency; sprites must correctly alpha-blend with both the background and each other.
Drawing order must match the logical order of sprites - clicking on a sprite that appears to be on top should initiate dragging that sprite, never one that appears underneath it.
Attempt 1
The obvious approach is to create a custom control to represent the Sprite. Let's try it:
public partial class Sprite : Control
{
public Sprite()
{
InitializeComponent();
For testing purposes, we'll make it so clicking a Sprite just brings it to the front, no dragging yet:
this.Click += Sprite_Click;
}
void Sprite_Click(object sender, EventArgs e)
{
this.BringToFront();
}
So we can identify each sprite and verify the drawing order, let's set a 'frame' color for each:
public Color FrameColor { get; set; }
And draw the sprites as a translucent white body with a solid-colour frame - we should then be able to verify that the background appears shaded behind a single space, shaded more strongly where sprites overlap, and the borders overlap as expected:
protected override void OnPaint(PaintEventArgs pe)
{
Graphics g = pe.Graphics;
g.FillRectangle(
new SolidBrush(Color.FromArgb(128, 255, 255, 255)),
DisplayRectangle
);
g.DrawRectangle(new Pen(FrameColor, 10), DisplayRectangle);
}
And then we can design a form with a dark background, set up a few Sprites on it, make them overlap, give them different frame colours, and test.
Naturally, it doesn't work. Controls by default have a background colour, which appears to default to white, or something close to it. So these sprites overlap properly, but they have opaque white middles.
Attempt 2
Well, surely we can just set that background colour to transparent? A bit of Googling in Microsoft's documentation tells us that we certainly can, but not directly (as someone else on SO found out the hard way). It needs a bit of configuration:
// in constructor
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;
Okay, so now the background shows through each Sprite, but the Sprite borders don't show through each other. What gives? A little more Googling tells us that this 'transparent background colour' support is actually a bit of a hack; basically, the parent Control implements its background painting to copy image data from the parent component and composite with that, ignoring everything else.
To be totally honest, I expected the system to be designed such that this would just work automatically - i.e., any time any drawing occurs, it composites with whatever's underneath it on screen, and the only reason controls aren't constantly showing through each other is because of their explicitly opaque backgrounds. But no such luck. I guess the whole system was designed back when people didn't want that sort of thing by default, for performance reasons.
Anyway.
Attempt 3
Well, if the background painting is what's causing the problem, maybe we can just disable it completely?
protected override void OnPaintBackground(PaintEventArgs ignored) { }
Nope. If we click on sprites, we can see them re-order, and composite with each other - but they don't composite with the background image. And, more strangely, when the parent window is invalidated (by resizing the form, or minimizing and restoring it), the sprites turn opaque again.
Attempt 4
After even more Googling, we find StackOverflow answers like this and this, articles like this etc. And they all point at the same low-level hack:
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle = cp.ExStyle | 0x20;
return cp;
}
}
For this to work, we also need to disable painting the background, but of course it no longer matters if we set up that "transparent" background, since that painting logic has been suppressed. (Curiously, it's also possible to suppress painting the background by setting an Opaque option in the ControlStyles; while that sounds like the opposite of what we want, it seems to work about as well.)
Well. It almost works. The Sprites composite both with themselves and the background. But now a very curious thing happens: the drawing order is wrong, and not even consistent. Invalidating the window in different ways (resizing vs. minimizing and restoring) will bring a different Sprite to the front visually; but in general it doesn't correspond to what was clicked and in what order. If we add explicit invalidation logic:
// in click event handler
Parent.Invalidate(this.Bounds, true);
then clicking a sprite actually appears to send it to the back visually - although again, the drawing order may change if we resize the window or minimize and restore it.
What gives? How can I solve this once and for all? Options I've considered:
Burn the controls to the ground; make a parent control keep track of a list of sprite Images, track mouse drag and click events, and do all the picking and rendering logic itself. Should be guaranteed to work, but is a ridiculous amount of work for the job.
Make the controls not draw at all, by overriding both background and foreground painting, and have them expose a property with their desired Image. Let a parent handle all the rendering, but fall back on the built-in Control logic for picking, mouse drags etc. Less work, but seems iffy (maybe there's some hidden thing that still forces Controls to draw something?), is a fairly tightly coupled design, and might still have performance issues if a naive approach is taken to invalidation (?).
After even more research, I appear to have an answer.
First, some explanation about drawing order: as far as I can tell now, it was consistent in Attempt 4, but invalidating by resizing the window causes gradual invalidations of different areas, which produces misleading results. The order is always top to bottom, which is the opposite of what you want for compositing, but which enables optimizations when you treat everything as opaque by clipping the invalid regions. (I'm not sure how much CPU effort this really saves given that the clipping paths can potentially get quite complex, but I guess it also helps reduce flickering when you aren't using double-buffering, since you avoid repainting a given pixel on any given refresh.)
But as it happens, if you read between the lines, you discover that there is another flag in the "extended window styles" (the 0x20 flag that we set in Attempt 4 is WS_EX_TRANSPARENT), called WS_EX_COMPOSITED (value 0x02000000), that reverses the drawing order to be what we want instead. Or you could have just read the documentation more closely in the first place. Oops.
(You'd think they could have just designed it to take care of all this logic automatically - detect which sprites have a translucent or transparent background colour, draw the ones with opaque backgrounds top-down first for speed, then the others bottom-up for correctness while clipping around the opaque ones. Oh well.)
A quick note here: because compositing naturally involves repainting the same area multiple times, this flag also sets up double-buffering that applies across all children of the window created with that flag (as far as I can tell, in Windows API parlance, every control is a "window", as well as the forms themselves). So it seems that people often use it to avoid flickering on forms with lots of controls, even when they don't intend for controls to overlap. However, ironically, WS_EX_COMPOSITED might actually cause huge amounts of flickering with certain controls, such as TabPage. (I'm running 8.1 and was able to reproduce the described problem very clearly.) The documentation states
This cannot be used if the window has a class style of either CS_OWNDC or CS_CLASSDC.
So maybe that has something to do with it. We can avoid this by restricting the "composited area" to a region using a simple control such as a Panel. At least in my tests, that will fix the problem as long as the TabPage isn't a child of the Panel, even if it overlaps.
It also seems that this might not work under XP, but hey, it's 2014 now.
Anyway, on to the code.
What we need to do is set a CreateWindowEx flag, with the same CreateParams technique, in some ancestor of the Sprite, such as the Form they're on:
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle = cp.ExStyle | 0x02000000;
return cp;
}
}
We still need the WS_EX_TRANSPARENT setting, and disabling of background paint on the Sprite controls, from attempts 3 and 4. We don't need to set up a transparent BackColor, because we're not going to paint the BackColor. I have no idea why allowing a fully transparent BackColor to be painted messes things up, but it apparently does.
This will apparently work even if there are intervening controls, i.e. if we put the Sprite controls inside a Panel on the Form. (If we want to restrict the effect to a Panel, as discussed above, we'd have to subclass Panel; but my project already does so :) ) Everything works perfectly in my test project, and I don't even need to explicitly Invalidate anything.
I am using a library in which enables me to animate movement of WinForm elements (linked below), when I use it to move a transparent panel across the form (which has a picture background) there is an extraordinary amount of tearing.
I believe the fix is to get winforms to refresh at a rate of 30fps without being laggy. I tried settings a timer to do This.Refresh(); at 30 times a second but it just ended up with slow loading. So how would I achieve a nice refresh rate of the form?
The link described above is here -> http://code.google.com/p/dot-net-transitions/wiki/CodingWithTransitions#Creating_a_transition_that_works_on_a_single_property
Here is an example of the tearing. It might have something to do with the fact that the panel's backcolor is set to "transparent"
http://screencast.com/t/XIr3NkGI
I used this ages ago when I had trouble with controls on a form flickering, in my case there were many controls and when loading the form the controls would flicker quite badly.
In the end I used this:
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000;
return cp;
}
}
Paste that in your form.
It activates double buffering at the form level and for all controls within it.
HOWEVER, while it stopped my flickering issue it greatly reduced the speed at which the forms elements seemed to move as they don't get drawn until they are 100% ready.
I guess you can try and see if it suits your situation.
I have a PNG file that displays crosshairs in a picturebox on my winform. I have created a user control that contains another small picture box (for a green dot), and a label.
What i want to do is place the user control over the picture box so that only the dot and label are visible. and the square edges of the control are invisible.
The dot has white edges since the png is actually square, so i attempted to set the Transparency key to be white, but when i add the control to my picture box, everything that is white is transparent.
I thought that if i colored around the edge of my dot with a color, then i can match that with the transparency key, but the colors have to be exact (right?), and my only method of painting is in windows paint, so i can't know if have matched the colors. Any help?
The problem you are running into is that WinForms controls do not support true transparency, and getting them to do so can be a bit tricky. What they do is paint the child control under themselves (when transparency is enable via SetStyle), but this has its own quirks. You should first try something like this:
// in your custom control class...
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x00000020; //WS_EX_TRANSPARENT
return cp;
}
}
I have found this method to be more reliable than the following, but you should try this as well:
public MyTransparentControl()
{
SetStyle( ControlStyles.SupportsTransparentBackColor |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint, true );
BackColor = Color.Transparent;
}
As an alternative approach, have you considered using WPF instead of WinForms? I know this is not always an option, but if it is you will make your life a whole lot easier as WPF controls support transparency and control layering out of the box.
I added this to my form's constructor code:
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);
But it still shows ugly artifacts when it loads the controls, whenever they change (the form and its components change (need updating) often).
What do I need to do differently?
This only has an effect on the form itself, not the child controls. If you have a lot of them then the time they need to take turns painting themselves becomes noticeable, it leaves a rectangular hole where the control goes that doesn't get filled up until the child control gets it turn.
What you'd need to combat this is double-buffering the entire form and the controls. That's an option available since Windows XP which made the WS_EX_COMPOSITED style flag available. Paste this into your form to turn it on:
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
It doesn't speed up the painting at all, but the form snaps onto the screen after a delay. Eliminating the visible paint artifacts. Really fixing the delay requires not using controls. Which you'd do by using the OnPaint method to draw the 'controls' and making the OnMouseClick event smart about what 'control' got clicked by the user.