Click here to Skip to main content
15,884,425 members
Articles / Programming Languages / C#

Rotated Text Control for .NET (C#)

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
21 Oct 2019CPOL3 min read 11.8K   732   8   5
A Label-like control for the .NET Framework that allows the text to be rotated

Introduction

This article demonstrates a .NET Framework control for adding rotated text to a form. The control reproduces the behavior of the built-in Label control but allows the text to be rotated. It allows the user to set the text, angle of rotation, text alignment, and auto-size properties in the designer. With these features in place, the control will resize itself and align the text automatically. It will exhibit appropriate behavior for the settings of the Anchor or Dock properties.

Below are some examples of the label. Each black outline is a label. The text in each label is set to a different angle, text alignment, and font.

Examples of rotated labels.

Background

The .NET Framework does not seem to have a built-in control for displaying rotated text. There are several examples online of how to produce basic rotated text, however, there doesn't seem to be a fully developed control for doing this. That is, there isn't a control that allows for rotated text and that obeys the Anchor, Dock, TextAlign, and AutoSize properties.

Using the Control

Since the control was designed to feel built-in, its use should be intuitive. It is recommended to add it as part of a control library. In the case of the example code, you will find it in the Toolbox under the Controls Components. Then it can be dragged-and-dropped onto a form.

Rotated text label shown in the toolbox.

The control can then be used exactly like a standard Label control, with one difference. In the Properties window, you will find an entry for the Angle, as shown highlighted below. The angle rotates the text in a clockwise direction.

Angle entry in the Properties window.

Demonstration

A demonstration project is included in the downloads section. It has two RotatedLabels that can be updated using the controls on the left. The top label is anchored on all four sides and the bottom label has the docking set to Fill. When started, the form appears like the following:

The example form when it is initially opened.

As the controls are updated, the labels will also update accordingly. The labels will also update when the form is resized.

An example of the form after changing control settings.

Code

The code contains a few properties. Most of them override the base class to allow the text to be updated when a property changes. An Angle property is added to store the text angle.

Most of the work takes place in the OnPaint event.

C#
protected override void OnPaint(System.Windows.Forms.PaintEventArgs paintEventArgs)
{
    // Get the text size.
    SizeF textSize         = paintEventArgs.Graphics.MeasureString(this.Text, this.Font,
                                                                   this.Parent.Width);

    // When you rotate the text, the width and height will now have an X and a Y component
    // because they no longer fall along the X or Y axis.  These are the components of the
    // width and height after rotation.
    int x                  = Math.Abs((int)Math.Ceiling(textSize.Height * Math.Sin(_radians)));
    int y                  = Math.Abs((int)Math.Ceiling(textSize.Height * Math.Cos(_radians)));
    Point rotatedHeight    = new Point(x, y);

    x                      = Math.Abs((int)Math.Ceiling(textSize.Width * Math.Cos(_radians)));
    y                      = Math.Abs((int)Math.Ceiling(textSize.Width * Math.Sin(_radians)));
    Point rotatedWidth     = new Point(x, y);

    // Size of the rotated text.  This is the size of the bounding box around the text.
    // SetControlSize will update the control size or leave it alone based on how auto 
    // sizing is set.
    Size textBoundingBox   = new Size(rotatedWidth.X + rotatedHeight.X,
                                      rotatedWidth.Y + rotatedHeight.Y);
    SetControlSize(textBoundingBox);

    // Two offsets (translations) are used.  The first is to accounts for the effects of the
    // rotation.  The second accounts for the effects of the alignment.  It's a lot easier to
    // calculate them separately and add together than to try to account for them both at the
    // same time.
    Point rotationOffset   = CalculateOffsetForRotation(ref rotatedHeight, ref rotatedWidth,
                                                        ref textBoundingBox);
    Point alignmentOffset  = CalculateOffsetForAlignment(ref textBoundingBox);

    // Apply the transformation and rotation to the graphics.
    paintEventArgs.Graphics.TranslateTransform(rotationOffset.X + alignmentOffset.X,
                                               rotationOffset.Y + alignmentOffset.Y);
    paintEventArgs.Graphics.RotateTransform(_angle);

    // Draw the text and let the base class do its painting.
    paintEventArgs.Graphics.DrawString(this.Text, this.Font, new SolidBrush(this.ForeColor),
                                       0f, 0f);
    base.OnPaint(paintEventArgs);
}

The first step...

C#
SizeF textSize = paintEventArgs.Graphics.MeasureString(this.Text, this.Font,
                                                       this.Parent.Width);

...calculates the size of the text. That is, it calculates the height and width of a "bounding box," a box that surrounds the text.

Rotated text label shown in the toolbox.

The height and width are placed into textSize such that textSize.X = H and textSize.Y = W. The next lines...

C#
int x                 = Math.Abs((int)Math.Ceiling(textSize.Height * Math.Sin(_radians)));
int y                 = Math.Abs((int)Math.Ceiling(textSize.Height * Math.Cos(_radians)));
Point rotatedHeight   = new Point(x, y);

x                     = Math.Abs((int)Math.Ceiling(textSize.Width * Math.Cos(_radians)));
y                     = Math.Abs((int)Math.Ceiling(textSize.Width * Math.Sin(_radians)));
Point rotatedWidth    = new Point(x, y);

...calculate the X and Y values for the rotated height and width. These are shown in the following image:

Rotated text label shown in the toolbox.

The variables correlate to the dimensions in the image as follows:

  • rotatedHeight.X = h'x
  • rotatedHeight.Y = h'y
  • rotatedWidth.X = w'x
  • rotatedWidth.Y = w'y

From the above image, it is easy to see that the bounding box for the rotated text has height H' = h'y + w'y and W' = h'x + w'x. This is represented in the code as:

C#
Size textBoundingBox = new Size(rotatedWidth.X + rotatedHeight.X,
                                rotatedWidth.Y + rotatedHeight.Y);

The functions CalculateOffsetForRotation and CalculateOffsetForAlignment then use the three variables, textBoundingBox, rotatedHeight, and rotatedWidth to reposition the coordinate system of the graphics so that the text is drawn in the correct location.

At the end of OnPaint, the graphics are used to draw the text and the base class's OnPaint event is called.

Limitations

Because of the way Windows handles drawing rotated text, the rotated version may appear slightly different than the horizontal version.

References and Acknowledgements

This class is partially based on the concepts posted by users Javed Akram and Buddy at:

History

  • October 2019 - First release
  • October 2019 - Added examples to the introduction

License

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


Written By
Engineer APS Technology
United States United States
Lance is a research and development engineer in the area of drilling dynamics in the oil service industry. He holds a Ph.D. in engineering with his graduate work focused is in the area of computational dynamics and mechanics. He has been coding for over 20 years.

Comments and Discussions

 
GeneralMy vote of 5 Pin
TJM.CODE13-Feb-20 13:20
TJM.CODE13-Feb-20 13:20 
PraiseVery nice. Pin
asiwel25-Oct-19 13:14
professionalasiwel25-Oct-19 13:14 
QuestionNice Control, But... Pin
Kevin Marois21-Oct-19 8:35
professionalKevin Marois21-Oct-19 8:35 
AnswerRe: Nice Control, But... Pin
dandy7221-Oct-19 12:46
dandy7221-Oct-19 12:46 
AnswerRe: Nice Control, But... Pin
MikesRuthlessYa22-Oct-19 23:37
MikesRuthlessYa22-Oct-19 23:37 

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.