I have no idea why this is happening, but I created the below code which is a gradient panel, the panel is then docked to the left of the screen.
When the form is re-sized it displays correctly, however if you minimize the form and then restore it you get a big red X instead of the gradient.
Can anyone spot the error?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.Windows.Forms;
public class GradientPanel : Panel
{
private Color ColorA = Color.LightBlue;
private Color ColorB = Color.Red;
private LinearGradientMode GradientFillStyle = LinearGradientMode.ForwardDiagonal;
private Brush gradientBrush;
public Color colourStart
{
get { return ColorA; }
set { ColorA = value; Invalidate(); }
}
public Color colourEnd
{
get { return ColorB; }
set { ColorB = value; Invalidate(); }
}
public LinearGradientMode colourGradientStyle
{
get { return GradientFillStyle; }
set { GradientFillStyle = value; Invalidate(); }
}
public GradientPanel()
{
handlerGradientChanged = new EventHandler(GradientChanged);
ResizeRedraw = true;
}
private EventHandler handlerGradientChanged;
protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e)
{
gradientBrush = new LinearGradientBrush(ClientRectangle, ColorA, ColorB, GradientFillStyle);
e.Graphics.FillRectangle(gradientBrush, ClientRectangle);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (gradientBrush != null) gradientBrush.Dispose();
}
base.Dispose(disposing);
}
protected override void OnResize(EventArgs eventargs)
{
Invalidate();
//base.OnResize(eventargs);
}
protected override void OnSizeChanged(EventArgs e)
{
Invalidate();
//base.OnSizeChanged(e);
}
private void GradientChanged(object sender, EventArgs e)
{
if (gradientBrush != null) gradientBrush.Dispose();
gradientBrush = null;
Invalidate();
}
}
I would avoid creating a new LinearGradientBrush
in the OnPaintBackground
handler.
My guess is you could be hitting your GDI object limit (which you can check in task manager) as I don't think you are disposing your brushes correctly.
Move this to the constructor, or in a function that is called when the colours and styles properties change (and dispose of the previous before creating the new one)
Here is an example of what you can do while still allowing the properties to change the colours etc...
Edit properties:
public Color colourStart
{
get { return ColorA; }
set { ColorA = value; RefershBrush(); }
}
public Color colourEnd
{
get { return ColorB; }
set { ColorB = value; RefershBrush(); }
}
public LinearGradientMode colourGradientStyle
{
get { return GradientFillStyle; }
set { GradientFillStyle = value; RefershBrush(); }
}
Add function:
private void RefershBrush()
{
//I think this IF block should work with no problems, been a while working with brush
if(gradientBrush != null)
{
gradientBrush.Dispose();
}
gradientBrush = new LinearGradientBrush(ClientRectangle, ColorA, ColorB, GradientFillStyle);
Invalidate();
}
I'm doing something similar, but even cleaning up the LinearGradientBrush
didn't fix it for me. Looking at the console output, I noticed "A first chance exception of type System.ArgumentException
occurred in System.Drawing.dll." I believe this is because the ClientRectangle is 0,0 when the component is minimized. Adding this code seemed to fix it for me:
protected override void OnPaintBackground(PaintEventArgs e)
{
base.OnPaintBackground(e);
if (this.gradientBrush != null)
this.gradientBrush.Dispose();
if (this.ClientRectangle.Width > 0 && this.ClientRectangle.Height > 0)
{
this.gradientBrush = new LinearGradientBrush(this.ClientRectangle,
FROM_GRADIENT_COLOR, TO_GRADIENT_COLOR, LinearGradientMode.Horizontal);
e.Graphics.FillRectangle(this.gradientBrush, this.ClientRectangle);
}
}
It doesn't look like you are disposing of the Brush correctly.
Refactored code:
public class GradientPanel : Panel {
private Color ColorA = Color.LightBlue;
private Color ColorB = Color.Red;
private LinearGradientMode GradientFillStyle = LinearGradientMode.ForwardDiagonal;
public GradientPanel() {
DoubleBuffered = true;
ResizeRedraw = true;
}
public Color colourStart {
get { return ColorA; }
set { ColorA = value; Invalidate(); }
}
public Color colourEnd {
get { return ColorB; }
set { ColorB = value; Invalidate(); }
}
public LinearGradientMode colourGradientStyle {
get { return GradientFillStyle; }
set { GradientFillStyle = value; Invalidate(); }
}
protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e) {
using (var gradientBrush = new LinearGradientBrush(ClientRectangle, ColorA, ColorB, GradientFillStyle)) {
e.Graphics.FillRectangle(gradientBrush, ClientRectangle);
}
}
}
You don't have to invalidate on every Resize or SizeChanged because you already have the control set with the ResizeRedraw=True
property. I added the DoubleBuffered=True
property as well to control flickering.
I placed your Gradient brush in a Using()
block so that it will get disposed.