Click here to Skip to main content
16,020,565 members
Articles / Desktop Programming / Windows Forms

Draw Over WinForms Controls

Rate me:
Please Sign up or sign in to vote.
4.86/5 (27 votes)
26 Aug 2010CPOL4 min read 206.1K   12.2K   83   49
Use the Graphics object to draw almost anything on top of your form's controls
Form1

GraphicalOverlayDemo1.PNG GraphicalOverlayDemo2.PNG

Form2

GraphicalOverlayDemo3.PNG GraphicalOverlayDemo4.PNG GraphicalOverlayDemo5.PNG

Introduction

The GraphicalOverlay component draws on top of a form's controls. It is essentially like laying a piece of glass over the form and drawing on the glass. I use this component to draw lines between controls to help users make sense of complex relationships in the UI. I use it sparingly, but sometimes you just need a big red arrow that says: This affects that! When done correctly, the result can be quite elegant.

Using the Code

To use the GraphicalOverlay component, follow these steps:

  1. Copy the following two files to your project's directory: GraphicalOverlay.cs, GraphicalOverlay.designer.cs.
  2. Add GraphicalOverlay.cs to your project. Visual Studio will automatically include GraphicalOverlay.designer.cs.

    Compile the project. This will create a GraphicalOverlay component in the Visual Studio toolbox.

  3. Drag a GraphicalOverlay component from the toolbox onto your form's design surface. The component will automatically be named graphicalOverlay1.
  4. In your form, create a paint event handler for graphicalOverlay1 (graphicalOverlay1_Paint()).
  5. In your form's constructor, after the InitializeComponents(); line, add the following line:
    C#
    public Form1()
    {
        InitializeComponent();
    
        graphicalOverlay1.Owner = this;
    }
  6. In graphicalOverlay1_Paint() (see step 5), use e.Graphics to draw anything you like, using form-relative coordinates.
    C#
    private void graphicalOverlay1_Paint(object sender, PaintEventArgs e)
    {
        // This event will fire for the form and each control on the form.
        // The graphical overlay component will have already transformed the
        // graphics object to use the form's coordinate system, 
        // so no control-specific calculations are required.
    
        Rectangle rect = this.ClientRectangle;
        rect.Inflate(-10, -10);
    
        using(Pen pen = new Pen(Color.Red, 5))
            e.Graphics.DrawEllipse(pen, rect);
    
        using(Font font = new Font("Arial", 14))
            e.Graphics.DrawString("Now is the time.", 
                                  font, Brushes.Green, 60, 110);
    
        [...]
    }
  7. To draw control-relative graphics, use the Coordinates() method to get the control's form-relative coordinates. See the Form1.graphicalOverlay1_Paint() event handler in the demo code for an example.
    C#
    private void graphicalOverlay1_Paint(object sender, PaintEventArgs e)
    {
        [...]
        // To draw relative to a control, use the Coordinates method.
        using (Pen pen = new Pen(Color.Blue, 3))
            e.Graphics.DrawEllipse(pen, pictureBox1.Coordinates());
    }

Points of Interest

The component contains the following event handlers:

  • Form_Resize
  • Control_Paint

When the graphicalOverlay1.Owner property is set, the component connects its Form_Resize event handler to the owner form's Resize event. Then, the component attaches its Control_Paint event handler to the Paint event for each of the owner form's controls (including the owner form itself).

As each control is repainted, the component handles the Paint event, transforms the coordinates of the e.Graphics object, then fires its own Paint event which will be handled by the form's graphicalOverlay1_Paint event handler.

Because the e.Graphics object received by graphicalOverlay1_Paint() has been transformed to use the form's coordinate system, all of the drawing logic is form-relative. Just draw as if you're drawing within the form's client area.

This approach makes it difficult to draw relative to the controls, however. So, to make control-relative drawing easier, I have added an extension method called Coordinates() to the System.Windows.Forms.Control class. The Coordinates() method converts the control's location to form-relative coordinates. Just draw using Coordinates(control) instead of control.Location.

Because the graphical overlay can be drawn over all of the form's controls, redrawing it requires the entire form to be invalidated. Calling the component's Invalidate() method will invalidate the form and each of the form's controls.

Limitations

The component responds to each control's Paint event. The TextBox control does not fire a Paint event, so the component cannot draw over TextBox controls. It may be possible to use text box controls from a third party, but I have not tested any of them.

The component can only draw over a control's client area. Some controls include a border that is not part of their client area, so the component cannot draw over the border. One solution to this problem is to turn off the control's border and draw it using the component.

The component can only draw within a form's client area. It cannot draw over the form's title bar or borders, and it cannot draw between forms. To simulate drawing over the form's borders, you would need to turn off the form's border and draw it with the component--and re-implement all of the lost functionality.

Demos

I have included two demo forms to help you understand how to implement your own graphicalOverlay1_Paint event handler.

Form1.cs is the one with the picture with red and blue rings and green text.

Note: While the Form1 demo is running, resize the form a few times.

Form2.cs is the demo with the group boxes and radio buttons.

Note: In order to switch between the Form1 and Form2 demos, you must modify the Program.cs file to run Form1 or Form2. By default, the demo will run Form2.

In the Form2 demo, the code in graphicalOverlay1_Paint() contains a lot of hard coded values. I could have calculated these values, but it would only have made it harder to read the code. The hard coded values may not work exactly right if your UI culture is different from mine or if your fonts are configured differently from mine. The screenshots at the beginning of the article will show you what they are supposed to look like.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
AnswerRe: Adding component at runtime Pin
RocketteScientist24-Oct-08 10:09
RocketteScientist24-Oct-08 10:09 
AnswerRe: Adding component at runtime Pin
RocketteScientist30-Oct-08 21:09
RocketteScientist30-Oct-08 21:09 
GeneralA screendump would have been nice... Pin
Johnny J.14-May-08 23:54
professionalJohnny J.14-May-08 23:54 
GeneralRe: A screendump would have been nice... Pin
Johnny J.14-May-08 23:55
professionalJohnny J.14-May-08 23:55 
GeneralRe: A screendump would have been nice... Pin
johannesnestler15-May-08 4:50
johannesnestler15-May-08 4:50 
GeneralRe: A screendump would have been nice... Pin
RocketteScientist15-May-08 19:31
RocketteScientist15-May-08 19:31 
GeneralRe: A screendump would have been nice... Pin
RocketteScientist15-May-08 19:27
RocketteScientist15-May-08 19:27 
QuestionWhat do you gain by this? Pin
je_gonzalez14-May-08 19:19
je_gonzalez14-May-08 19:19 
Would it not be simpler to override the OnPaint method for the form and do the same thing there?

I know that you also have to override the OnResize and call the form's Invalidate() so things get redrawn properly on a resize, but you do this anyway in the code.
AnswerRe: What do you gain by this? Pin
RocketteScientist15-May-08 19:38
RocketteScientist15-May-08 19:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.