博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Label with Divider

Posted on 2010-10-28 10:29  gczhao  阅读(228)  评论(0编辑  收藏  举报
 
A simple divider with text.

Introduction

Sometimes it is necessary to visually separate controls in a dialog. GroupBox is one solution, but it is not appropriate if you just want a horizontal 3D line with a label like in some Options dialogs in Visual Studio. One possible solution is to create an additional empty label with BorderStyle set to Fixed3D and Height set to 2. However, this approach is cumbersome because you must take care of control placement order. Moreover, if you are going to change the label text (e.g., when localizing the application) or text alignment, you'll have to resize/reposition controls. Therefore, I have created this simple label that takes care of such details.

Background

Details regarding custom control creation has been explained elsewhere (e.g., in excellent step-by-step tutorial Divider Panel - A tutorial on creating a custom Windows Forms control from Start to Toolbox). Therefore, I will not go through them but only outline some specific points.

LabelWithDivider control inherits the Label class, overriding only its OnPaint method. In this method, line is drawn first, and then base class' OnPaint method is called that takes care of drawing the text:

Collapse | Copy Code
protected override void OnPaint(PaintEventArgs e) {
    PlaceLine(e.Graphics);
    base.OnPaint(e);
}

The order of method calls is actually not important since PlaceLine method calculates position and length of the line taking in account the text length as well as its horizontal and vertical alignments:

Collapse | Copy Code
protected void PlaceLine(Graphics g) {
    // evaluates text size
    SizeF textSize = g.MeasureString(this.Text, this.Font);
    int x0 = 0;           // first point x-coordinate
    int x1 = this.Width;  // second point x-coordinate
    // for different horizontal alignments recalculates x-coordinates
    switch (GetHorizontalAlignment()) {
    case HorizontalAlignment.Left:
        x0 = (int)textSize.Width + m_gap;
        break;
    case HorizontalAlignment.Right:
        x1 = this.Width - (int)textSize.Width - m_gap;
        break;
    case HorizontalAlignment.Center:
        x1 = (this.Width - (int)textSize.Width) / 2 - m_gap;
        break;
    }
    int y = (int)textSize.Height / 2;  
    // for different vertical alignments recalculates y-coordinate           
    if (TextAlign == ContentAlignment.MiddleLeft 
        || TextAlign == ContentAlignment.MiddleCenter 
        || TextAlign == ContentAlignment.MiddleRight)
        y = this.Height / 2;
    else if (TextAlign == ContentAlignment.BottomLeft 
        || TextAlign == ContentAlignment.BottomCenter 
        || TextAlign == ContentAlignment.BottomRight)
        y = this.Height - (int)(textSize.Height / 2) - 2;

    Draw3DLine(g, x0, y, x1, y);
    // for centered text, two line sections have to be drawn
    if (TextAlign == ContentAlignment.TopCenter 
        || TextAlign == ContentAlignment.MiddleCenter 
        || TextAlign == ContentAlignment.BottomCenter) {
        x0 = (this.Width + (int)textSize.Width) / 2 + m_gap;
        x1 = this.Width;
        Draw3DLine(g, x0, y, x1, y);
    }
}

protected HorizontalAlignment GetHorizontalAlignment() {
    if (TextAlign == ContentAlignment.TopLeft 
        || TextAlign == ContentAlignment.MiddleLeft 
        || TextAlign == ContentAlignment.BottomLeft) {
        if (RightToLeft == RightToLeft.Yes)
            return HorizontalAlignment.Right;
        else
            return HorizontalAlignment.Left;
    }
    if (TextAlign == ContentAlignment.TopRight 
        || TextAlign == ContentAlignment.MiddleRight 
        || TextAlign == ContentAlignment.BottomRight) {
        if (RightToLeft == RightToLeft.Yes)
            return HorizontalAlignment.Left;
        else
            return HorizontalAlignment.Right;
    }
    return HorizontalAlignment.Center;
}

protected void Draw3DLine(Graphics g, int x1, int y1, int x2, int y2) {
    g.DrawLine(SystemPens.ControlDark, x1, y1, x2, y2);
    g.DrawLine(SystemPens.ControlLightLight, x1, y1+1, x2, y2+1);
}

m_gap in the above code is the optional gap between text and line, which can be set to a non-zero value by Gap property:

Collapse | Copy Code
public int Gap {
    get { return m_gap; }
    set { 
        m_gap = value; 
        Invalidate();
    }
}

Note that setter calls Invalidate to ensure control is redrawn in the visual designer on value change.

Using the code

Control has been placed into class library (LabelWithDivider.dll) and all required attributes are set so that control can be added to toolbox and simply dragged and dropped onto a form. Gap property is available in Appearance category. And last but not least: LabelWitDivider is placed into System.Windows.Forms namespace.

History

  • November 12, 2004: ver. 1.0
  • November 13, 2004: ver. 1.1: LeftToRight bugfix.

License

This article, along with any associated source code and files, is licensed under The zlib/libpng License