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

Two First Controls - A Shape and an inherited Label Control

By , 24 Aug 2007
Rate this:
Please Sign up or sign in to vote.

Introduction

This article describes the creation of a gradient Shape and an inherited Label control. For information on how to create a Control, check out the article "Divider Panel - A tutorial on creating a custom Windows Forms Control from Start to Toolbox" by Furty.

In .NET, there are many classes and interfaces for painting and manipulating graphics and text. The book of Charles Petzold "Programming Microsoft Windows with C#" has a good explanation of many useful methods for development in GDI+.

The Shape Source Code

Sample Image - maximum width is 600 pixels

Shape Properties

The Shape has seven added properties:

  • BackgroundGradientColor1 - First background gradient color
  • BackgroundGradientColor2 - Second background gradient color
  • BackgroundGradientAngle - Background gradient angle
  • SwirlColors - Swirl/twist colors in background gradient
  • Radius - Radius of rounded borders
  • ShapeBorderStyle - Style of the border to be drawn around the control
  • BorderColor - Color of the border to be drawn around the control

Shape Methods

The Shape has four simple methods: OnPaint, DrawShapeBackground, DrawBorder and GetPath.

  • This is the OnPaint method.

    protected override void OnPaint(PaintEventArgs e)
    {
        // AntiAlias provides better quality
        SmoothingMode sm = e.Graphics.SmoothingMode;
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        DrawShapeBackground(e.Graphics);
        if (_borderStyle == ShapeBorderStyles.ShapeBSFixedSingle)
            DrawBorder(e.Graphics);
        e.Graphics.SmoothingMode = sm;
    }
  • This is the DrawShapeBackground method.

    private void DrawShapeBackground(Graphics g)
    {
        Rectangle rect = this.ClientRectangle;
        rect.X++;
        rect.Y++;
        rect.Width -= 2;
        rect.Height -= 2;
        // Draw the gradient in the client rectangle
        GraphicsPath path = GetPath(rect, _radius);
        using (LinearGradientBrush br =
            new LinearGradientBrush(rect, _color1, _color2, _angle))
        {
            br.GammaCorrection = true;
            if (_swirlColors)
                br.SetBlendTriangularShape(.5f, .75f);
                br.WrapMode = WrapMode.Tile;
                g.FillPath(br, path);
        }
        Region rgn = new Region(path);
        this.Region = rgn; //Do invisible the container control
    }
    
  • This is the DrawBorder method.

    private void DrawBorder(Graphics g)
    {
        Rectangle rect = this.ClientRectangle;
        rect.X++;
        rect.Y++;
        rect.Width -= 3;
        rect.Height-= 3;
        using (GraphicsPath bp = GetPath(rect, _radius))
        {
            using (Pen p = new Pen(_borderColor))
            {
                g.DrawPath(p, bp);
            }
        }
    }
    
  • And this is the GetPath method.

    protected GraphicsPath GetPath(Rectangle rc, int r)
    //  Build the path with the round corners in the rectangle
    //  r is the radious of rounded corner.
    {
        int x = rc.X, y = rc.Y, w = rc.Width   , h = rc.Height ;
        r = r << 1;
        GraphicsPath path = new GraphicsPath();
        if (r > 0)
        //  If the radious of rounded corner is 
        //  greater than one side then
        //  do the side rounded
        {
            if (r > h) { r = h; }; //Rounded
            if (r > w) { r = w; }; //Rounded
            path.AddArc(x, y, r, r, 180, 90); 
                //Upper left corner
            path.AddArc(x + w - r, y, r, r, 270, 90); 
                //Upper right corner
            path.AddArc(x + w - r, y + h - r, r, r, 0, 90); 
                //Lower right corner
            path.AddArc(x, y + h - r, r, r, 90, 90); 
                //Lower left corner
            path.CloseFigure();
        }
        else
        //  If the radious of rounded corner is zero then the 
        //  path is a rectangle
        {
            path.AddRectangle(rc);
        }
            return path;
    }
    

The Label Source Code

Sample Image - maximum width is 600 pixels

We use the Shape as the base for the new Label control.

public partial class DOALabel : DOAShape

Label Properties

We add seven new properties:

  • Text - The text associated with the control
  • Font - The font used to display text in the control
  • Forecolor - The foreground color of this component, which is used to display the text
  • TextAlign - Text alignment (Left, Right or Center), only with Moving None
  • Moving - Text Moving (None, RightToLeft, DownToUp, LeftToRight, UpToDown)
  • MovingActive - Activate text movement
  • DimmedColor - Dims the text color when the mouse is passing over the control

For the Text, Font and ForeColor properties, we override the base properties.

public override string Text
{
    get { return base.Text; }
    set { base.Text = value ; this.Invalidate(); }
}

public override Font Font
{
    get { return base.Font; }
    set { base.Font = value; this.Invalidate(); }
}

public override Color ForeColor
{
    get { return base.ForeColor; }
    set { base.ForeColor = value; this.Invalidate(); }
}

Label Methods

This component uses a timer to update and invalidate the Draw event OnPaint.

private void timer1_Tick(object sender, System.EventArgs e)
{
    this.Update();
    this.Invalidate();
}

OnPaint calls the base class and the DrawText method.

protected override void OnPaint(PaintEventArgs pe)
{
    // Calling the base class OnPaint
    base.OnPaint(pe);
    iRect = base.ClientRectangle;
    DrawText(pe);
}

This is DrawText method.

protected void DrawText(PaintEventArgs pe)
{
    //This is a workaround to get MeasureString to work properly
    pe.Graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
    SizeF sz = pe.Graphics.MeasureString(base.Text, base.Font);
    switch (_moving)
    {
        case MoveType.None:
            NoMove();
            break;
        case MoveType.RightToLeft:
            MoveRightToLeft();
            break;
        case MoveType.DownToUp:
            MoveDownToUp();
            break;
        case MoveType.LeftToRight:
            MoveLeftToRight();
            break;
        case MoveType.UpToDown:
            MoveUpToDown();
            break;
    }
    //Rectangle bounds for the text
    txtRect = new Rectangle(this.pointX, this.pointY,
        (int)sz.Width + 1, (int)sz.Height);

    //If the mouse is passing over the text it is selected and will be dimmed
    //otherwise nothing.
    Brush brText = new SolidBrush(base.ForeColor);
    Brush brTextDimmed = new SolidBrush(_dimmedColor);
    if (_isSelected)
        pe.Graphics.DrawString(base.Text,
            base.Font,
            brTextDimmed,
            txtRect);
    else
        pe.Graphics.DrawString(base.Text,
            base.Font,
            brText,
            txtRect);
}

The methods NoMove(), MoveRigntToLeft(), MoveDownToUp(), MoveLeftToRight() and MoveUpToDown()update, for each MoveType, the coordinates of the text rectangle.

protected void NoMove()
{
    //Align text
    switch (_textAlign)
    {
        case TextAlignment.Left:
            pointX = (int)this.iRect.X;
            break;
        case TextAlignment.Center:
            pointX = (this.iRect.Width - this.txtRect.Width ) / 2;
            break;
        case TextAlignment.Right:
            pointX = (this.iRect.Width - this.txtRect.Width);
            break;
        }
        pointY = (this.iRect.Height - this.txtRect.Height) / 2;
    }

    protected void MoveRightToLeft()
    {
        if (pointX < -this.txtRect.Width)
        { 
            pointX = this.iRect.X + this.iRect.Width; 
        }
        else
        { 
            pointX -= 2; 
        }
        pointY = ( this.iRect.Height - this.txtRect.Height) / 2;
    }

    protected void MoveDownToUp()
    {
        pointX = (this.iRect.Width - this.txtRect.Width) / 2;
        if (pointY < -this.txtRect.Height)
        { 
            pointY = (int)this.iRect.Y + this.iRect.Height; 
        }
        else
        { 
            pointY -= 2; 
        }
    }

    protected void MoveLeftToRight()
    {
        if (pointX > this.iRect.X + this.iRect.Width)
        { 
            pointX = this.iRect.X - this.txtRect.Width; 
        }
        else
        { 
            pointX += 2; 
        }
        pointY = (this.iRect.Height - this.txtRect.Height) / 2;
    }

    protected void MoveUpToDown()
    {
        pointX = (this.iRect.Width - this.txtRect.Width) / 2;
        if (pointY > this.iRect.Y + this.iRect.Height)
        { 
            pointY = (int)this.iRect.Y - this.iRect.Height; 
        }
        else
        { 
            pointY += 2; 
        }
}

If the mouse is passing over the text, it will be dimmed. For that, we use the events OnMouseEnter and OnMouseLeave.

protected override void OnMouseEnter(EventArgs e)
{
    base.OnMouseEnter(e);
    _isSelected = true;
    this.Invalidate();
}

protected override void OnMouseLeave(EventArgs e)
{
    base.OnMouseLeave(e);
    _isSelected = false;
    this.Invalidate();
}

History

  • 01/03/2007 First Version
  • 15/05/2007 Second Version
  • 23/08/2007 Third Version
    • Better border management
    • The missing last character bug was fixed (alandgraf@gmx.at)
    • The code was simplified

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Daniel Ortiz Acuna
Web Developer
Argentina Argentina
No Biography provided

Comments and Discussions

 
QuestionText not positioning correctly PinmemberMember 1025523310-Oct-13 5:39 
GeneralThe missing chatacter PinmemberDaniel Ortiz Acuna28-Jul-07 5:54 
GeneralThis would be an outstanding control, but... PinmemberApuhjee2-Jun-07 9:34 
AnswerFix of the missing character bug Pinmemberalandgraf@gmx.at13-Jun-07 0:27 
GeneralRe: This would be an outstanding control, but... PinmemberDaniel Ortiz Acuna17-Aug-07 16:20 

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 | Mobile
Web01 | 2.8.140421.2 | Last Updated 24 Aug 2007
Article Copyright 2007 by Daniel Ortiz Acuna
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid