Click here to Skip to main content
Click here to Skip to main content

GroupBox with an icon: the ImageGroupBox control

, 15 Mar 2006 CPOL
Rate this:
Please Sign up or sign in to vote.
This article presents an extended GroupBox that displays an icon in the header.

Sample Image - ImageGroupBox.jpg

Introduction

To enhance the design of our applications, most of us use images or icons. With .NET, it's very easy to do, because most of the controls provided by Microsoft already have an Image or Icon property. Unfortunately, it is not the case of the GroupBox control. In fact, with the standard GroupBox control, it is not possible to display an icon in the header section.

Background

I was very surprised to see first that the GroupBox has no Icon nor Image property, and next that no code was available on this topic on the Internet. Of course, great controls like The Grouper did what I was looking for (and much, much more). But I wanted something more simple. So I started to write my own control, thinking that it would be easy ... Here it is.

How does it work?

Of course, the GroupBox must be overridden, and the only property added to the base GroupBox is the Icon property, defined like this:

private Icon m_Icon = null;

[Description("Icon before the text"), 
    AmbientValue((string)null), 
    Category("Appearance"),Localizable(true)]
public Icon Icon {
    get { return m_Icon; }
    set { if(m_Icon != value) { m_Icon = value; 
          this.Invalidate(false); } }
}

For working with visual styles, it's better to use the VisualStyleRenderer class:

private VisualStyleRenderer m_Renderer = null;

Next, the OnPaint method is overridden too. It allows custom painting, exactly what we need to introduce the drawing of the icon. The basic idea of this code is simple, as you can see:

protected override void OnPaint(PaintEventArgs e) {
    // This override only draws the control
    // if the icon is not null. Otherwise,
    // the base method is called
    if(m_Icon != null && (Application.RenderWithVisualStyles 
       && (base.Width >= 10)) && (base.Height >= 10)) {
        // Draw the entire control
        this.DrawGroupBox(e.Graphics);
    } else base.OnPaint(e);
}

The main code lies in the DrawGroupBox method. This method draws the entire control, with the help of three methods: DrawIcon, DrawText, and DrawBackground.

private void DrawGroupBox(Graphics grfx) {
    // Set the enabled state of the control
    GroupBoxState state = base.Enabled ? 
        GroupBoxState.Normal : GroupBoxState.Disabled;

    // The rectangle that bounds the control
    Rectangle bounds = new Rectangle(0,0,base.Width,base.Height);
    // Set the rectangle to display the Text
    Size txtsize = TextRenderer.MeasureText(grfx,text, 
                   font,new Size(bounds.Width-14,bounds.Height));
    // The optimized height of the header
    int headerheight = Math.Max(m_Icon.Height,txtsize.Height);
    // Define the rectangle for the icon
    Rectangle iconrect = new Rectangle(9, 
              (headerheight - m_Icon.Height) / 2, 
              m_Icon.Width,m_Icon.Height);
    // Define the rectangle of the text
    Rectangle textrect = new Rectangle(new 
              Point(iconrect.Right,(headerheight - 
              txtsize.Height) / 2),txtsize);
    // Define the rectangle that defines the inner container
    Rectangle displayrect = bounds; displayrect.Y += 
              headerheight / 2; displayrect.Height 
              -= headerheight / 2;

    // Draw the icon
    DrawIcon(grfx,m_Icon,iconrect,state);
    // Draw the text
    DrawText(grfx,this.Text,this.Font,textrect, 
             m_Renderer.GetColor(ColorProperty.TextColor), 
             this.BackColor,txtflags);
    // Draw the background
    DrawBackground(grfx,displayrect,textrect,m_Icon.Width);
    // Clean up
    grfx.Dispose();

Then, the three distinct objects are painted in the following functions:

  • the icon: it is done with the Graphics.DrawIcon method for the Enabled state, and the Control.ControlPaint.DrawImageDisabled method for the Disabled state:
    // Draw the icon m_Icon in the iconrect Rectangle
    private void DrawIcon(Graphics grfx,Icon icon, 
                 Rectangle rc,GroupBoxState state) {
        if(state == GroupBoxState.Disabled) {
            using(Image image = m_Icon.ToBitmap()) {
                // Draw the disabled icon
                ControlPaint.DrawImageDisabled(grfx,image, 
                              rc.Left,rc.Top,Color.Empty);
            }
        } else {
        // Draw the enabled icon
        grfx.DrawIcon(icon,rc);
        }
    }
  • the text: this can be done with the Graphics.DrawString method, but for respect of the base properties (like RightToLeft), it is better to use the TextRenderer.DrawText method:
    // Draw the text with the Graphics object grfx,
    // in the bounds Rectangle
    private void DrawText(Graphics grfx,string text, 
            Font font,Rectangle bounds, 
            Color txtcolor,Color backcolor) {
        TextRenderer.DrawText(grfx,text,font, 
                  bounds,txtcolor,backcolor);
    }
  • the rounded rectangle: it's easy to do with the VisualStyleRenderer class. This class encapsulates the visual styles handling, and prevents writing big amounts of lines of code. Because the upper border of the rounded rectangle must not overlay the header of the control, only three parts are drawn, gathered in such a manner that they seem to form a rectangle:
    private void DrawBackground(Graphics grfx, 
                Rectangle bounds,Rectangle headerrect, int iconwidth) {
        // Initializing the bounds of each part
        Rectangle leftrect = bounds; leftrect.Width = 7;
        Rectangle middlerect = bounds; middlerect.Width = 
                Math.Max(0,headerrect.Width + iconwidth);
        Rectangle rightrect = bounds;
        if((txtflags & TextFormatFlags.Right) == TextFormatFlags.Right) {
            leftrect.X = bounds.Right - 7;
            middlerect.X = leftrect.Left - middlerect.Width;
            rightrect.Width = middlerect.X - bounds.X;
        } else {
            middlerect.X = leftrect.Right;
            rightrect.X = middlerect.Right;
            rightrect.Width = bounds.Right - rightrect.X;
        }
        middlerect.Y = headerrect.Bottom;
        middlerect.Height -= headerrect.Bottom - bounds.Top;
    
        // Draw the left part
        m_Renderer.DrawBackground(grfx,bounds,leftrect);
        // Draw the middle part
        m_Renderer.DrawBackground(grfx,bounds,middlerect);
        // Draw the right part
        m_Renderer.DrawBackground(grfx,bounds,rightrect);
    }

I recommend you to see the entire code (downloadable at the top of this page) for all the details, particularly for the TextFormatFlags, omitted here for simplicity.

This approach is not the only possible, of course. After I wrote the ImageGroupBox control, I saw the XP Style Collapsible GroupBox that uses the GroupBoxRenderer.DrawGroupBox method.

Points of Interest

As you could see, overriding the OnPaint method involves redrawing the entire control. And it's not a so little work when the visual styles must be respected! But in fact, the tools based on the VisualStyleRenderer and TextRenderer classes, help. That's what I thought interesting in this approach. It's a way of using underground classes to product nice results, without writing too many lines of code.

License

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

Share

About the Author

bsargos
Software Developer
France France
No Biography provided

Comments and Discussions

 
Generalchild control Dock Fill issue Pinmembervijay karra13-May-09 6:05 
AnswerRe: child control Dock Fill issue Pinmemberstixoffire18-May-09 7:09 
Check this one out - see if it fits your style...
It is basically a merge of two projects on code project:
Thsi Icon groupbox, and the Collapsable Group box
 
Collapsible GroupBox With Icon[^]

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141223.1 | Last Updated 15 Mar 2006
Article Copyright 2006 by bsargos
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid