Creating a Custom Control in Winforms - Part 2 Adding Paint

Continuing on from last time....

 

A grey square on a grey form is not very interesting….

Lets add some paint. I’ve talked a bit about painting before in Painting Best Practices, but we’re going to do it for real this time.

 

We’re going to paint red, yellow, green and blue squares, making use of the FillRectangle function from System.Drawing.

 

        protected override void OnPaint(PaintEventArgs e) {

            base.OnPaint (e);

            // build up the rectangles that we're going to paint

            int squareSize = this.Width/2;

            Rectangle redRect = new Rectangle(0,0, squareSize, squareSize);

            Rectangle greenRect = new Rectangle(0,redRect.Bottom, squareSize, squareSize);

            Rectangle blueRect = new Rectangle(redRect.Right, 0, squareSize, squareSize);

            Rectangle yellowRect = new Rectangle(redRect.Right, redRect.Bottom, squareSize, squareSize);

            // fill each rectangle with a solid color

            e.Graphics.FillRectangle(Brushes.Red, redRect);

            e.Graphics.FillRectangle(Brushes.Green, greenRect);

            e.Graphics.FillRectangle(Brushes.Yellow, yellowRect);

            e.Graphics.FillRectangle(Brushes.Blue,blueRect);

      

        }

 

The graphics class has lots of functions on it that will let you draw pretty much anything – lines, shapes, images, etc.

 

 

Common Painting Mistakes

 

Using the wrong coordinate system

 

If you look back at the code, you’ll notice that the rectangles that I’m filling all start off at 0,0. These coordinates match the upper-left hand corner of the ClientRectangle. All coordinates used within the OnPaint function should be relative to the ClientRectangle.

 

Example mistake:

Rectangle redRect = new Rectangle(Location.X,Location.Y, squareSize, squareSize);

 

The location property gives the distance from the parent’s upper left hand corner of the client area to the control. E.g. square1.Location = new Point(10, 23);

 

Instead of painting the red square in the upper right hand corner, it would offset the red rectangle to 10,23 – which depending on the size of the square, may not be visible.

 

Leaking Pens and Brushes

 

Note the use of “Brushes.” in the above function. For each known color in System.Drawing, there is a matching member Brush in Brushes or SystemBrushes. These are solid brushes that are cached for your use. They can be called at any time without the need to dispose them. If you need a brush that is not a known color, you can create a new SolidBrush with that color. Keep in mind that when you’re through with the brush, it is a best practice to dispose it. Here’s why.

 

Using the ClipRectangle as the rectangle to Paint into

 

The clip rectangle is the part of the control that needs to actually be repainted. For example if you had another window (e.g an open file dialog) obscuring the right half of the square, when the dialog goes away, only the right portion needs to be repainted. This is represented in the clip rectangle.

 

If we used

Rectangle redRect = new Rectangle(ClipRectangle.X, ClipRectangle.Y, squareSize, squareSize);

 

Then the red rectangle would wind up painting in the right half of the control! Not what we want at all.

 

For the most part the clip rectangle should be ignored in the paint method. Once the clipping rect is set up, the painting libraries are smart enough to only update the parts of the control that need to be updated. So you can keep using the same painting code regardless of the clip rectangle.

 

Making sure the square repaints when it is resized

 

The problem now with the control is that as the control is resized, only parts of the control repaint. Fortunately for this, there is a simple flag you need to use: ControlStyles.ResizeRedraw.

SetStyle(ControlStyles.ResizeRedraw, true);