Double buffering control causes grey to appear - c#

I have a class that accepts a Control and draws generated images to it. In my application code I'm using a Panel control for this - which seems to work well, except that I get a lot of flickering on the control as the image is redrawn. I think the solution is to enable double buffering on the control. I'm doing this with the following code:
System.Reflection.PropertyInfo aProp =
typeof(System.Windows.Forms.Control).GetProperty(
"DoubleBuffered",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance);
aProp.SetValue(drawControl, true, null);
After adding this code, the image is drawn, and then immeadiately replaced with the default grey button background colour - it's as if the system is drawing over my own content.
I remember seeing somewhere a property that tells the system to never draw the control (which would be perfect for me since I'm filling the entire control with content), but now I cannot find it. Is there such a property, and if so, how can I enable it?
I'm using C# with the .NET framework ver 4.0.

Try setting FlatStyle on the control to FlatStyle.Standard, if it's currently set to FlatStyle.System.
(Setting DoubleBuffered by reflection looks horrible too, but perhaps you need to do that for some reason)

Related

Controlling how, when, and if child controls are drawn (.NET)

I am writing an application in .NET that has a plugin interface. That plugin interface provides several ways to draw information (controls) onto the surface of the application window. While there are several reasons why I am doing this, the main reason is to provide custom colorization to text, either through the use of a graphic or directly manipulating the color of the text based on the background color. I do this through the use of a "text mask" which is a black and white bitmap that works as an "alpha" map to let the Paint method know where to apply the texture/color changes.
The plugin developer has the option of using regular text (such as with a label), mask text (which is drawn to the mask rather than as a regular control), OR letting the user decide. To go along with this, I have provided a modified label class that can either be drawn "normally' (when the text mask is not set for the control), or to the text mask when the User OR Developer decides (depending on what the plugin developer wishes to offer to the user). Here is the class's code so that you understand how this is being done:
public class MaskingLabel : Label
{
private static readonly SolidBrush maskBrush = new SolidBrush(Color.White);
public Bitmap Mask { get; set; }
public MaskingLabel() : base() { }
protected override void OnPaint(PaintEventArgs e)
{
if (Mask == null)
base.OnPaint(e);
else
{
Graphics g = Graphics.FromImage(Mask);
g.DrawString(Text, Font, maskBrush, Location);
}
}
}
The problem I am running into is that this approach requires that I handle controls in a very specific order so that the form is drawn correctly. I need to find the most efficient approach to get the tasks listed below done in the order given. I have thought of three possibilities discussed further down. For reference, this is the order in which tasks must be done:
All "MaskingLabel" controls that have the bitmap object set to the mask must be drawn first so that the mask is created before the next step.
The mask is applied to the background picture.
The resulting Bitmap is drawn in a way similar to the way a background would be drawn (except that it is modified first).
The rest of the controls are drawn as normal.
Is there a way for me to insure this happens without separating the controls manually? My first guess is no. As such, I have a few guesses below about how I should go about this. I was hoping someone with more in depth knowledge of GDI+ could offer some insight.
One idea that has occurred to me is to draw the masked controls during the OnPaintBackground method. However, I don't want to waste time by painting the controls twice. This means I would need to filter out which controls are drawn during the main Paint method which effectively leads us to option 2 (FAIK):
I can manually filter out the controls which draw to the mask so that they don't get added to the control. My question here though is would they get drawn at all? Can I manually force them to invoke the OnPaint method?
If doing that wouldn't work, then perhaps I can create a separate derived panel control to serve as a "backdrop" child control that acts as the background picture which can be forced to be drawn first?
EDIT (With Part of the answer):
I realized after posting this that I already have part of the solution built into my project. Still, I think it is a legitimate question to ask, so if anyone can add insight beyond what I have done in my description below, it is welcome.
Specifically, my project has only two controls that are added to the "root" form: a bar that goes to the top (docked at the top when it is shown), and a transparent panel that occupies the rest of the space (with a dock style set to fill). So my solution would be to add the mask controls to the main form and add all the rest to the panel. This only leaves one remaining issue to be resolved: How do I make sure that the panel and the bar are drawn last? (As part of step 4 in the first list?)

CustomControl "disappears" when dragged in panel/form with backgroundimage

I am writting a pin-up board in C# where I need to be able to drag around pieces of paper (notes) on the board. I have made the notes as customcontrols since I needed to be able to write on them as well. The background of the customcontrol (a note) is an image of a piece of paper. When I don't use a backgroundimage, for the board itself, everything works as intended. I am able to drag around the notes (customcontrols with a backgroundimage) just fine - no flickering. When I use a backgroundimage on the board (which I want to do, since a plain color background doesn't cut it for me), I am not able to drag the notes around smoothly anymore. When I start dragging the note disappears and is first redrawn when I stop moving the mouse.
I am using the following code on the panel (in its constructor) where I drag around notes, but it has only sorted out my initial screen flickering issue.
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true);
The image file used for the background is imported into the projects resources. I have tried to load the image file into a bitmap object and used this object as the backgroundimage of the panel, but that didn't change anything.
Below a link to an image of how a note should look when being dragged:
http://i.stack.imgur.com/9LnKj.jpg
Below an image of how the note actually looks when I start dragging it around:
http://i.stack.imgur.com/x0Lf1.png
Please ask if you need more details to be able to help me solve my issue. Any help and suggestions on what the problem might be is very appreciated! Thanks in advance.
Edit: The size of the note on the second image is dependent on how far I drag it from its initial point. I am able to get the note to disappear completely when I drag it further than the size of it. It gets redrawn when I stop moving the mouse.
Edit: I use the following code for moving around the notes:
private void NoteControl_MouseMove(object sender, MouseEventArgs e)
{
if (_dragme)
{
System.Drawing.Point newLocation = e.Location - mouseOffset;
this.Left += newLocation.X;
this.Top += newLocation.Y;
}
}
Since I have not been able to solve my issue with the custom control disappearing when getting dragged on an imaged background I have decided to create an asp.net version of my project rather than a windows form application. This also prevent the issue with cross platform compatibility. The dragging is now accomplished using jQuery

Double buffering with Panel

Double buffering the whole form can be done by setting the value of the "AllPaintingInWmPaint", "UserPaint" and "DoubleBuffer" ControlStyles to "true" (this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true)).
But this can't happen with a System.Windows.Forms.Panel because the class doesn't allow me to do so. I have found one solution: http://bytes.com/topic/c-sharp/answers/267635-double-buffering-panel-control . I have also tried this: Winforms Double Buffering . It's laggy, even when it's used on a small drawing, I have some custom resources that I'm using in the form and other things because of which I won't turn the whole form into one drawing. And the second one seems to cause problems. Are there other ways to do that?
I'm asking this because I don't want the drawing on the panel to flash all the time when the form is being resized. If there is a way to get rid of the flashing without double buffering, I'll be happy to know.
Use a PictureBox if you don't need scrolling support, it is double-buffered by default. Getting a double-buffered scrollable panel is easy enough:
using System;
using System.Windows.Forms;
class MyPanel : Panel {
public MyPanel() {
this.DoubleBuffered = true;
this.ResizeRedraw = true;
}
}
The ResizeRedraw assignment suppresses a painting optimization for container controls. You'll need this if you do any painting in the panel. Without it, the painting smears when you resize the panel.
Double-buffering actually makes painting slower. Which can have an effect on controls that are drawn later. The hole they leave before being filled may be visible for a while, also perceived as flicker. You'll find counter-measures against the effect in this answer.
I should have posted my solution a long time ago...
Well, here is my solution:
Bitmap buffer = new Bitmap(screenWidth, screenHeight);//set the size of the image
System.Drawing.Graphics gfx = Graphics.FromImage(buffer);//set the graphics to draw on the image
drawStuffWithGraphicsObject(gfx);//draw
pictureBox1.Image = buffer;//set the PictureBox's image to be the buffer
Makes me feel like a complete idiot for finding this solution years after asking this question.
I have tried this with a Panel, but it has proven to be slower when applying the new image. Somewhere I had read, that it is better to use Panel instead of PictureBox. I don't know if I have to add something to the code to speed things up for the Panel, though.
If acceptable you can stop refreshing the panel while resizing and enable it again after, this way you get rid of the ugly flickering.

Resizing window causes black strips

I have a form, which sets these styles in constructor:
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
And I draw some rectangles in Paint event. There are no controls on the form. Hovewer, when I resize the form, there are black strips at right and bottom of the form. Is there any way to get rid of them? I've tried everything, listening for WM_ERASEBKGND in WndProc, manually drawing the form on WM_PAINT, implementing custom double buffer, etc. Is there anything else I could try?
I've found this:
https://connect.microsoft.com/VisualStudio/feedback/details/522441/custom-resizing-of-system-windows-window-flickers
and it looks like it is a bug in DWM, but I just hope I can do some workaround.
Please note that I must use double buffering, since I want to draw pretty intense graphic presentation in the Paint event. I develop in C# .NET 2.0, Win7.
Status Update 1
I've managed to get rid of most of the black stripes by implementing the resize functionality by myself. Hovewer there are still some minor glitches. Is there any way to do resize and paint operation at once? Here is a pseudo-code of what I need to do:
IntPtr hDC;
var size = new Size(250, 200);
IntPtr handle = API.PaintAndResizeBegin(this.Handle /* Form.Handle */,
size.Width, size.Height, out hDC);
using (var g = Graphics.FromHdc(hDC)) {
this.backBuffer.Render(g, size);
}
API.PaintAndResizeCommit(handle);
Is there any way to implement the above code?
The second solution could be to back-buffer whole form, including non-client area. But how to do that? I don't want to paint the non-client area by myself, as I want to keep the nice aero effect on Vista/7. Any help will be deeply appreciated.
Status Update 2
It looks like this problem is unsolvable, since it is omnipresent on Windows, in every application. We can just hope that MS will take some inspiration in Mac OS X and will provide appropriate APIs in new Windows.
I've found the function which can paint and resize window at the same time - UpdateLayeredWindow.
So now it should be possible to create resizable windows, which do not have any strips while being resized. However, you need to paint the window content yourself, so it is a little inconvenient. But I think that using WPF and UpdateLayeredWindow, there shouldn't be any problem.
Update
Found problems. :-) When using UpdateLayeredWindow, you must paint the window's border yourself. So, if you want standard window painted using UpdateLayeredWindow with nice glass effect in win7, you are screwed.
On Microsft Connect is even a thread about this problem, where Microsoft says it is a bug by design, and if it ever gets fixed, then probably in Win8 or some newer system. So there isn't much we could do about this.
I found that it is best not to do any custom rendering directly on the Form surface. Instead, put a docked PictureBox on the form, create Bitmap object that will be displayed in the PictureBox, draw everything onto that using the System.Drawing.Graphics.FromImage(Image) method.
I used that method with a game loop to make a simple shooter game (Crimsonland-style) and got pretty good performance (with anti-aliased lines), above 100 FPS.

Windows application very slow when using transparency

I am writing a windows application with C# in visual studio .net 2005.
In the form , there are some control with transparent Background; the form opens maximised and with full screen background.
The application runs very slow with high CPU usage.
Why is this?
1. Solution using property DoubleBuffered
Sidenote: Only works if you have access to the control as DoubleBuffered is a protected property of Control. Similar as solution 2 (see code behind).
// from within the control class
this.DoubleBuffered = true;
// from the designer, in case you are utilizing images
control.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None;
System.Windows.Forms.Control.DoubleBuffered
System.Windows.Forms.Control.BackgroundImageLayout
2. Alternative solution using SetStyle + OptimizedDoubleBuffer:
Sidenote: The control paints itself, window message WM_ERASEBKGND is ignored to reduce flicker, and the control is first drawn to a buffer rather than directly to the screen.
control.SetStyle(UserPaint | AllPaintingInWmPaint | OptimizedDoubleBuffer, true);
System.Windows.Forms.Control.SetStyle(ControlStyles, Boolean)
System.Windows.Forms.Control.ControlStyles
3. Alternative solution using SetStyle + DoubleBuffer:
Sidenote: Similar as OptimizedDoubleBuffer, due to legacy reasons it remained in the codebase.
control.SetStyle(UserPaint | AllPaintingInWmPaint | OptimizedDoubleBuffer, true);
System.Windows.Forms.Control.SetStyle(ControlStyles, Boolean)
System.Windows.Forms.Control.ControlStyles
Thats because the GDI+ transparency implemented in .NET 2 is not ideally implemented, as Bob Powell explains.

Categories