Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C#
Article

GroupBox with an icon: the ImageGroupBox control

Rate me:
Please Sign up or sign in to vote.
4.97/5 (18 votes)
15 Mar 2006CPOL2 min read 233K   2.4K   74   32
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:

C#
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:

C#
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:

C#
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.

C#
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:
    C#
    // 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:
    C#
    // 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:
    C#
    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)


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

Comments and Discussions

 
GeneralMy vote of 5 Pin
Member 1268921616-Aug-16 21:50
Member 1268921616-Aug-16 21:50 
QuestionVery good / An improvement Pin
Tomb011-Aug-13 21:09
Tomb011-Aug-13 21:09 
QuestionSize of code Pin
twinbee2-Apr-12 8:34
twinbee2-Apr-12 8:34 
SuggestionAdd Bitmap rather than Icon Pin
tgeukens28-Oct-11 14:49
tgeukens28-Oct-11 14:49 
QuestionCode use and Article Pin
stixoffire18-May-09 5:52
stixoffire18-May-09 5:52 
Generalchild control Dock Fill issue Pin
vijay karra13-May-09 5:05
vijay karra13-May-09 5:05 
AnswerRe: child control Dock Fill issue Pin
stixoffire18-May-09 6:09
stixoffire18-May-09 6:09 
Generallicence Pin
Petr Nosek1-Dec-08 10:34
Petr Nosek1-Dec-08 10:34 
GeneralRe: licence Pin
bsargos1-Dec-08 12:07
bsargos1-Dec-08 12:07 
GeneralRe: licence Pin
tgis.top26-Sep-10 22:55
tgis.top26-Sep-10 22:55 
GeneralCool ,学习一下 Pin
xfhuju-bjktd8-Dec-07 3:37
xfhuju-bjktd8-Dec-07 3:37 
Smile | :)
GeneralRe: Cool ,学习一下 Pin
bsargos1-Dec-08 12:07
bsargos1-Dec-08 12:07 
GeneralColor Pin
Drunken #BeAvEr#20-Sep-07 8:54
Drunken #BeAvEr#20-Sep-07 8:54 
GeneralCant see the icon Pin
raheel_siddiqui7-Aug-07 22:44
raheel_siddiqui7-Aug-07 22:44 
Generaloutlook style groupbox Pin
OwfAdmin5-Aug-07 9:20
OwfAdmin5-Aug-07 9:20 
GeneralRe: outlook style groupbox Pin
bsargos1-Dec-08 12:11
bsargos1-Dec-08 12:11 
GeneralNice work - but problem on my pc Pin
datacore24-May-07 23:12
datacore24-May-07 23:12 
GeneralRe: Nice work - but problem on my pc Pin
bsargos25-May-07 4:24
bsargos25-May-07 4:24 
GeneralRe: Nice work - but problem on my pc Pin
datacore28-May-07 21:11
datacore28-May-07 21:11 
SuggestionRe: Nice work - but problem on my pc Pin
Vyacheslav Shevelyov23-Jan-12 23:22
Vyacheslav Shevelyov23-Jan-12 23:22 
QuestionBug on Windo2003 Server SO Pin
Nicola Costantini23-Oct-06 6:14
Nicola Costantini23-Oct-06 6:14 
AnswerRe: Bug on Windo2003 Server SO Pin
bsargos24-Oct-06 5:19
bsargos24-Oct-06 5:19 
Generalsomething wrong Pin
edisom11cao29-Jun-06 15:31
edisom11cao29-Jun-06 15:31 
GeneralRe: something wrong Pin
edisom11cao29-Jun-06 17:59
edisom11cao29-Jun-06 17:59 
GeneralRe: something wrong Pin
Olivier DALET25-Jul-06 21:12
Olivier DALET25-Jul-06 21:12 

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.