Transparent picturebox flickers - c#

So I'm coding a project that is Plants vs. Zombies made of pure c# using no game engines and here I have a graphic problem.
I need to render a transparent picturebox over another transparent picturebox and I had to define a new control that is really transparent and everything goes fine with the transparency aspect but here is a problem:
Flicker :|
I have so much of them because of the RecreateHandle(); method I use when I change the image of my control to make animations and when it moves to have the real transparency.
Here is my code I wonder if any one could help !
public class TransparentControl : Control
{
private Image _image;
public TransparentControl()
{
SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.SupportsTransparentBackColor, true);
base.BackColor = Color.FromArgb(0, 0, 0, 0);
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
protected override void OnMove(EventArgs e)
{
//RecreateHandle();
}
protected override void OnPaint(PaintEventArgs e)
{
if (_image != null)
{
e.Graphics.DrawImage(_image, (Width / 2) - (_image.Width / 2), (Height / 2) - (_image.Height / 2));
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
//Do not paint background
}
//Hack
public void Redraw()
{
//RecreateHandle();
}
public Image Image
{
get
{
return _image;
}
set
{
_image = value;
//RecreateHandle();
}
}
}

One way to reduce flickering is to create two different bitmaps. One to draw to, and one to display.
Image BackBuffer;
Image BrontBuffer;
private void RotateImages()
{
lock (this.BackBuffer)
{
var temp = this.BackBuffer;
this.BackBuffer = this.FrontBuffer;
this.FrontBuffer = temp;
}
}
Draw everything into your BackBuffer, then show the FrontBuffer.
Note that you should declare the FrontBuffer with the same width/height values as the BackBuffer, in pretty much exactly the same spot that you declare the BackBuffer.
Use the method RotateImages() immediately after (before should work, too) you display the front buffer.

Related

How to create a fadeIn/Out Custom Control?

I know how to create a fadeIn/Out winform.
The problem is, I don't know how to create a Custom Control which will lose its full transparency when mouse leaves its area and gets transparent when mouse gets back on it.
A Custom Control doesn't have Opacity and TransparentKey properties.
This tutorial explains how to add Opacity property to a User Control.
public UserControl1()
{
InitializeComponent();
// Enables support for transparent back color for inner controls.
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
}
// This enables transparent backgrounds for the control.
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
// A property to adjust the opacity level.
private int _opacity = 255;
[Description("Set opacity of control. 0 to 255")]
public int Opacity
{
get
{
return this._opacity;
}
set
{
this._opacity = value;
this.Refresh();
}
}
// Override default OnPaintBackground event.
protected override void OnPaintBackground(PaintEventArgs e)
{
e.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(this.Opacity, this.BackColor)), this.ClientRectangle);
}
But it doesn't work on my Custom Control or I just don't know how to use it.
By FadeIn/Out, I mean a control which gets transparent when mouse leave it until user points back to it

How do I set the background image to None, or some other default value, in an image button?

I'm building off this question and creating a simple ImageButton class that represents a button containing only an image. I implemented (at least I believe I did) the suggestions in this answer, but I'm still getting an exception with this code:
public class ImageButton : Button
{
// Overrides the property
public override Image BackgroundImage
{
get { return base.BackgroundImage; }
set
{
base.BackgroundImage = value;
if (value != null) this.Size = value.Size;
}
}
// Shadows the property (note the -new- keyword)
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new Size Size
{
get
{
return base.Size;
}
set
{
base.Size = value;
}
}
public ImageButton()
{
this.BackgroundImage = base.BackgroundImage;
this.BackgroundImageChanged += new EventHandler(ImageButton_BackgroundImageChanged);
}
void ImageButton_BackgroundImageChanged(object sender, EventArgs e)
{
this.Size = this.BackgroundImage.Size;
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawImage(BackgroundImage, 0, 0); // <-- Error occurs here
}
protected override void OnPaintBackground(PaintEventArgs e)
{
// Do nothing
}
}
When I try to add this control to the designer, I get
The control ImageButton has thrown an unhandled exception in the
designer and has been disabled.
Exception: Value cannot be null. Parameter name: image
Stack trace: ImageButton.OnPaint(PaintEventArgs e) in
ImageButton.cs:line48
Line 48 is this line:
e.Graphics.DrawImage(BackgroundImage, 0, 0);
I realise that this error is thrown because BackgroundImage is not set to a value, but I'm unsure how to do so in the code. In the actual application, this class will never be added to the designer, but rather added programmatically. How can I fix this exception?
this.BackgroundImage = base.BackgroundImage;
Yes, sure, guaranteed exception. You hope that somebody has set the BackgroundImage property before the constructor runs. That's not possible, the constructor runs before any property on the control can be set.
The next thing that goes wrong is that the Paint event will be raised in the designer as well. Which happens immediately after you drop the control on the form. That's a Kaboom, neither the user nor your code has given the BackgroundImage property a value yet. So just fix the method:
protected override void OnPaint(PaintEventArgs e)
{
if (BackgroundImage != null) e.Graphics.DrawImage(BackgroundImage, 0, 0);
}
to remove the background image just write:
this.BackgroundImage = null;
I don't know C#, but my guess for this would be to have a transparent 1x1px image stored somewhere and assign that on init. That way, there'd be an image but nothing will be seen.
Or you could try adding some check around line 48, like this:
if(BackgroundImage!=null){
e.Graphics.DrawImage(BackGroundImage,0,0);
}
Maybe add something like this:
else{ //No background image
//Draw some dummy/placeholder
}
Yet another method would be to encapsulate the DrawImage call within a try{} block and catch the resulting expression. You should be able to safely use an empty catch{}block if it's that one.
What if you don't draw when there is no image set, also call base in the beginning of the function.
base.OnPaint(e);
There is a lot of redundant code in your implementation: try this revised version...
public class ImageButton : Button
{
public ImageButton()
{
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);
}
protected override void OnPaint(PaintEventArgs pevent)
{
//base.OnPaint(pevent); <-- NO LONGER NEEDED (WE DO NOT WANT THE SYSTEM TO PAINT THE BUTTON);
if (this.BackgroundImage != null)
{
pevent.Graphics.DrawImage(this.BackgroundImage, 0, 0);
}
else
{
//Just fill in black (for example)
pevent.Graphics.FillRectangle(new SolidBrush(Color.Black), this.ClientRectangle);
}
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
base.OnPaintBackground(pevent);
pevent.Graphics.FillRectangle(new SolidBrush(this.BackColor), this.ClientRectangle);
}
public override Image BackgroundImage
{
get
{
return base.BackgroundImage;
}
set
{
base.BackgroundImage = value;
this.Size = base.BackgroundImage.Size;
this.Refresh();
}
}
}

True Transparent Picturebox in VS2010

In my WinForm Application, I am needing to layer some images. However, I'm having trouble getting a transparent control to place the image in. I have done some research and came up with the following class:
public class TransparentPicture : PictureBox
{
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
// do nothing
}
protected override void OnMove(EventArgs e)
{
RecreateHandle();
}
}
This seems to work fine until I close Visual Studios and reopen the Solution. Then my controls all disappear in the designer. They show when I run the program, but I need them to show in designer too where I can continue to design my application.
I know this isn't everything I need to do, because these controls are always causing my program to freeze up for a few seconds and stuff.
So my question is..does anybody know where I can find code for a transparent control, or how to fix the one I've thrown together?
Make the TransparentPicture be a regular PictureBox, until an IsTransparent property is set to true.
Set the property to false on design time, and to true in FormLoad event (which will only happen when you actually run the application).
That way, on design time, they will behave as regular picture boxes, but when you run the application, they will become transparent.
public class TransparentPicture : PictureBox
{
public bool IsTransparent { get; set; }
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
if (this.IsTransparent)
{
cp.ExStyle |= 0x20;
}
return cp;
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
if (!this.IsTransparent)
{
base.OnPaintBackground(e);
}
}
protected override void OnMove(EventArgs e)
{
if (this.IsTransparent)
{
RecreateHandle();
}
else
{
base.OnMove(e);
}
}
}
Then, on your FormLoad event, you should do:
for (var i = 0; i < this.Controls.Count; i++)
{
var tp = this.Controls[i] as TransparentPicture;
if (tp != null)
{
tp.IsTransparent = true;
}
}
Or if you only have a few:
tp1.IsTransparent = tp2.IsTransparent = tp3.IsTransparent = true;

Transparent label control is not refreshing properly

I've created a transparent label control and the transparency works great. However, I find when I update the control's Text field, the original Text doesn't clear before it paints the new text. So if I change the control's Text field several times, it soon becomes unreadable.
Any clues? Thanks!
public partial class TransLabel : Label
{
public TransLabel()
{
InitializeComponent();
this.SetStyle(ControlStyles.Opaque, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
this.Font = new Font("Franklin Gothic Book", 12f, FontStyle.Regular);
this.ForeColor = Color.White;
this.BackColor = Color.Transparent;
}
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
this.Invalidate(); // seems to have no effect
this.Refresh(); // seems to have no effect
}
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
//do nothing
}
protected override void OnMove(EventArgs e)
{
RecreateHandle();
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle = 0x00000020; //WS_EX_TRANSPARENT
return cp;
}
}
}
Try changing your Text property to this:
public override string Text {
get {
return base.Text;
}
set {
base.Text = value;
if (this.Parent != null)
this.Parent.Invalidate(this.Bounds, false);
}
}
Since WinForms doesn't have true support for transparency, I think you have to invalidate the parent container.
Also, when inheriting a control, you usually don't have an InitializeComponent() method.

A problem with overriding OnPaint when DoubleBuffered set to true

I have created a custom control which derives from Panel. I use it to display an Image using BackgroundImage property. I override the OnClick method and set isSelected to true then call Invalidate method and draw a rectangle in overrided OnPaint.
Everything goes just fine until I set DoubleBuffered to true. The rectangle is drawn and then it is erased and I can't get why this is happening.
public CustomControl()
: base()
{
base.DoubleBuffered = true;
base.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.ResizeRedraw, true);
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
PaintSelection();
}
private void PaintSelection()
{
if (isSelected)
{
Graphics graphics = CreateGraphics();
graphics.DrawRectangle(SelectionPen, DisplayRectangle.Left, DisplayRectangle.Top, DisplayRectangle.Width - 1, DisplayRectangle.Height - 1);
}
}
In your PaintSelection, you should not create a new Graphics object, because that object will draw to the front buffer, which is then promptly overdrawn by the contents of the back buffer.
Paint to the Graphics passed in the PaintEventArgs instead:
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
PaintSelection(pe.Graphics);
}
private void PaintSelection(Graphics graphics)
{
if (isSelected)
{
graphics.DrawRectangle(SelectionPen, DisplayRectangle.Left, DisplayRectangle.Top, DisplayRectangle.Width - 1, DisplayRectangle.Height - 1);
}
}

Categories

Resources