Rotate and transform the text with stringformat

Answered Rotate and transform the text with stringformat

  • Tuesday, October 18, 2011 7:23 AM
     
     

    I want to Rotate the text with given degree and ALSO WANT TO PLACE THIS ROTATED TEXT WITH ALL THE STRING ALIGNMENTS.

    I can rotate the text using "RotateTransform" and "TranslateTransform". If I am selecting the STRING ALIGNMENT as BOTTOM LEFT, the text is not seen in the control's boundary. Can anyone please help me to get the rectangle if the Rotation angle is changed so that the text will be properly fit in the controls's boundary.

All Replies

  • Tuesday, October 18, 2011 7:27 AM
     
     

    ??

    Rotate text? What is that suppose to mean Exactly?

    Show us an example how it should look like.


    Mitja
  • Tuesday, October 18, 2011 7:30 AM
     
     
  • Tuesday, October 18, 2011 7:30 AM
     
     

    Are you like Mitja asked to show what you mean, the best way would be in an image.

    You can paste in images into this forum if you use this button when you are in edit mode. 


    Success
    Cor
  • Tuesday, October 18, 2011 9:01 AM
     
     

  • Tuesday, October 18, 2011 10:55 AM
     
     

    ...

    I can rotate the text using "RotateTransform" and "TranslateTransform". If I am selecting the STRING ALIGNMENT as BOTTOM LEFT, the text is not seen in the control's boundary. Can anyone please help me to get the rectangle if the Rotation angle is changed so that the text will be properly fit in the controls's boundary.


    Hi,

    what language?

    could you provide some code where it does not work? Where do you rotate your graphics-context? At its center? At its origin? do you observe the bigger size needed when rotating (esp: at the center)? ...

    Regards,

      Thorsten


  • Tuesday, October 18, 2011 11:20 AM
     
     

    Im am following the code given in above  codeproject link. The rotated text can be drawn properly. but if I want to draw this rotated text in CENTER - LEFT  position using string alignment, it is not drawn properly. In fact it is going out of the boundary of the control. I want to draw this rotated text in all of the string alignment position. (Top left, top center, top right, center left, center middle, center right, bottom left, bottom center, bottom right), The example shows only draw the text at one location.

     

  • Tuesday, October 18, 2011 12:04 PM
     
     

    For these, you'll have to rotate the graphics-context at different locations. Not always width / 2, height / 2 -> which is the center point, but for instance af textstart (0) and height / 2 for left(x) center(y), I think.

    Regards,

      Thorsten

  • Tuesday, October 18, 2011 12:14 PM
     
     Answered Has Code

    ... something like this:

    (I only implemented the logic for MiddleLeft MiddleMiddle and MiddleRight for use with the TextAlign-Property)

     

        public class MyLabel : Label
        {
            private float _rotation = 0F;
            public float Rotation
            {
                get { return _rotation; }
                set
                {
                    _rotation = value;
                    CheckSize();
                    this.Invalidate();
                }
            }
    
            public MyLabel()
            {
                this.SetStyle(ControlStyles.UserPaint, true);
                this.AutoSize = false;
                //this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            }
    
            private void CheckSize()
            {
                SizeF sz;
                using (Graphics g = this.CreateGraphics())
                    sz = g.MeasureString(this.Text, this.Font);
    
                RectangleF r = new RectangleF(0, 0, sz.Width, sz.Height);
    
                using (System.Drawing.Drawing2D.GraphicsPath gPath = new System.Drawing.Drawing2D.GraphicsPath())
                {
                    gPath.AddRectangle(r);
    
                    double rot = _rotation / 180.0 * Math.PI;
                    gPath.Transform(new System.Drawing.Drawing2D.Matrix((float)Math.Cos(rot), -(float)Math.Sin(rot), (float)Math.Sin(rot), (float)Math.Cos(rot), 0, 0));
    
                    RectangleF rr = gPath.GetBounds();
    
                    if (this.ClientSize.Width < rr.Width)
                    {
                        this.ClientSize = new Size((int)Math.Ceiling(rr.Width), this.ClientSize.Height);
                    }
    
                    if (this.ClientSize.Height < rr.Height)
                    {
                        this.ClientSize = new Size(this.ClientSize.Width, (int)Math.Ceiling(rr.Height));
                    }
                }
            }
    
            protected override void OnPaint(PaintEventArgs e)
            {
                this.AutoSize = false;
                System.Drawing.Drawing2D.GraphicsContainer con = e.Graphics.BeginContainer();
    
                float xAdd = 0;
                float yAdd = 0;
    
                SizeF sz = e.Graphics.MeasureString(this.Text, this.Font);
    
                //only cl,cc,cr implemented
                switch (this.TextAlign)
                {
                    case ContentAlignment.MiddleLeft:
                        xAdd = (this.ClientSize.Width / 2F) - sz.Width / 2f;
                        break;
    
                    case ContentAlignment.MiddleRight:
                        xAdd = -((this.ClientSize.Width / 2F) - sz.Width / 2f);
                        break;
    
                }
    
                if (this.Rotation != 0f)
                {
                    System.Drawing.Drawing2D.Matrix mx = e.Graphics.Transform;
                    mx.RotateAt(this.Rotation, new PointF(this.ClientSize.Width / 2F - xAdd, this.ClientSize.Height / 2F - yAdd), System.Drawing.Drawing2D.MatrixOrder.Append);
                    e.Graphics.Transform = mx;
                }
    
    
                e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
                using (SolidBrush sb = new SolidBrush(this.ForeColor))
                    e.Graphics.DrawString(this.Text, this.Font, sb, new PointF((this.ClientSize.Width - sz.Width) / 2F - xAdd, (this.ClientSize.Height - sz.Height) / 2F - yAdd));
    
                e.Graphics.EndContainer(con);
            }
    
            protected override void OnTextChanged(EventArgs e)
            {
                base.OnTextChanged(e);
    
                CheckSize();
                this.Invalidate();
            }
        }
    

    Regards,

     

      Thorsten


  • Tuesday, October 18, 2011 12:18 PM
     
     

    The LayoutRectangle passed to the DrawString method is also rotated. If you always want to rotate at the center of the text and also want to specify an alignment, it is getting more complicated, and you'll have to do some calculations on your own. That might involve some trigonometrie.

    Have a look at the following illustration showing a top-left aligned text.
    - The black rectangle is the containg control.
    - The red rectangle is the rotated surrounding rectangle of the String.
    - The green rectangle is the unrotated surrounding rectangle of the String.

    Version A: The Text always touches the borders of the containing control.
    Version B: The Text' center is always the center of the green rectangle. The width and height of the green rectangle is equal to the length of the diagonal of the red rectangle.

    With both versions you will have to rotate the Text at it's center and also apply a transformation matrix. The difference between the versions is the calculation of the rotation center. Version A is more complicated to calculate.

    So, if I understood your concerns correctly, which version is your favorite?


    Armin
  • Tuesday, October 18, 2011 1:04 PM
     
     

    Thanks for the reply

    Can you suggest the remaining calculation for the text alignment?

    Any hint please?

  • Tuesday, October 18, 2011 1:39 PM
     
     

    I have made the code

     

    switch (this.TextAlign)

                {

                    case ContentAlignment.MiddleLeft:

                        xAdd = (this.ClientSize.Width / 2F) - sz.Width / 2f;

                        break;

     

                    case ContentAlignment.MiddleRight:

                        xAdd = -((this.ClientSize.Width / 2F) - sz.Width / 2f);

                        break;

     

                    case ContentAlignment.TopCenter:

                        yAdd = (this.ClientSize.Height / 2F) - sz.Height * 2F ;

                        break;             

     

                    case ContentAlignment.TopLeft:

                        xAdd = (this.ClientSize.Width / 2F) - sz.Width / 2f;

                        yAdd = (this.ClientSize.Height / 2F) - sz.Height * 2F;

                        break;

     

                    case ContentAlignment.TopRight:

                        xAdd = -((this.ClientSize.Width / 2F) - sz.Width / 2f);

                        yAdd = (this.ClientSize.Height / 2F) - sz.Height * 2F;

                        break;

     

                    case ContentAlignment.BottomLeft :

                        xAdd = (this.ClientSize.Width / 2F) - sz.Width / 2f;

                        yAdd = -((this.ClientSize.Height / 2F) - (sz.Height * 2F));

                        break;

     

                    case ContentAlignment.BottomRight:

                        xAdd = -((this.ClientSize.Width / 2F) - sz.Width / 2f);

                        yAdd = -((this.ClientSize.Height / 2F) - (sz.Height * 2F));

                        break;

     

                    case ContentAlignment.BottomCenter :

                        yAdd = -((this.ClientSize.Height / 2F) - (sz.Height * 2F));

                        break;

                }

    Is this code is correct?

     


    • Edited by Amolpbhavsar Tuesday, October 18, 2011 1:58 PM
    •  
  • Tuesday, October 18, 2011 2:04 PM
     
     

    Hi,

    I think, yes, it is alright. The code is correct when the results are correct.

    Regards,

      Thorsten

  • Wednesday, October 19, 2011 4:44 AM
     
     
    Sometimes text gets cut when a long string is set as text even though the control size is enough bigger then the size of the text. Can you please look in to it?
  • Wednesday, October 19, 2011 11:31 AM
     
     

    Hi,

    could you give an example? I used Text = "this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;";

    Regards,

      Thorsten

  • Wednesday, October 19, 2011 12:10 PM
     
     

    ... ok, i see now. ThereÄ's two things to do:

    First, your calculation isnt right sz.Height * 2F should be sz.Height / 2F in the switch

    Second, you'll have to do some verifying that the rotation and drawing of the string is in a range from 0 to label.clientsize.width - the surrounding rect of the rotated string width. Same for height.

     

    I do not have the time to do this right now. Maybe tomorrow.

    Regards,

      Thorsten

  • Wednesday, October 19, 2011 1:08 PM
     
     Answered Has Code

    ... I did play around with it a bit, here's the new paint-method:

            protected override void OnPaint(PaintEventArgs e)
            {
                this.AutoSize = false;
                System.Drawing.Drawing2D.GraphicsContainer con = e.Graphics.BeginContainer();
    
                float xAdd = 0;
                float yAdd = 0;
    
                double rot = _rotation / 180.0 * Math.PI;
                SizeF sz = e.Graphics.MeasureString(this.Text, this.Font);
    
                RectangleF rr = new RectangleF(0, 0, sz.Width, sz.Height);
                RectangleF r = new RectangleF(0, 0, sz.Width, sz.Height);
    
                using (System.Drawing.Drawing2D.GraphicsPath gPath = new System.Drawing.Drawing2D.GraphicsPath())
                {
                    gPath.AddRectangle(r);
    
                    gPath.Transform(new System.Drawing.Drawing2D.Matrix((float)Math.Cos(rot), -(float)Math.Sin(rot), (float)Math.Sin(rot), (float)Math.Cos(rot), 0, 0));
    
                    rr = gPath.GetBounds();
                }
    
                switch (this.TextAlign)
                {
                    case ContentAlignment.MiddleLeft:
                        xAdd = (this.ClientSize.Width - rr.Width) / 2F;
                        break;
    
                    case ContentAlignment.MiddleRight:
                        xAdd = -(this.ClientSize.Width - rr.Width) / 2F;
                        break;
    
                    case ContentAlignment.TopCenter:
                        yAdd = (this.ClientSize.Height - rr.Height) / 2F;
                        break;
    
                    case ContentAlignment.TopLeft:
                        xAdd = (this.ClientSize.Width - rr.Width) / 2F;
                        yAdd = (this.ClientSize.Height - rr.Height) / 2F;
                        break;
    
                    case ContentAlignment.TopRight:
                        xAdd = -(this.ClientSize.Width - rr.Width) / 2F;
                        yAdd = (this.ClientSize.Height - rr.Height) / 2F;
                        break;
    
                    case ContentAlignment.BottomLeft:
                        xAdd = (this.ClientSize.Width - rr.Width) / 2f;
                        yAdd = -(this.ClientSize.Height - rr.Height) / 2F;
                        break;
    
                    case ContentAlignment.BottomRight:
                        xAdd = -(this.ClientSize.Width - rr.Width)  / 2f;
                        yAdd = -(this.ClientSize.Height - rr.Height) / 2F;
                        break;
    
                    case ContentAlignment.BottomCenter:
                        yAdd = -(this.ClientSize.Height - rr.Height) / 2F;
                        break;
                }
    
                e.Graphics.TranslateTransform(
                    (-xAdd * (float)Math.Cos(rot)) + (-yAdd * (float)Math.Sin(rot)),
                    -(-xAdd * (float)Math.Sin(rot)) + (-yAdd * (float)Math.Cos(rot)), 
                    System.Drawing.Drawing2D.MatrixOrder.Append);
    
                if (this.Rotation != 0f)
                {
                    System.Drawing.Drawing2D.Matrix mx = e.Graphics.Transform;
                    mx.RotateAt(this.Rotation, new PointF((this.ClientSize.Width / 2F), this.ClientSize.Height / 2F), System.Drawing.Drawing2D.MatrixOrder.Append);
                    e.Graphics.Transform = mx;
                }
    
                e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
                using (SolidBrush sb = new SolidBrush(this.ForeColor))
                {
                    e.Graphics.DrawString(this.Text, this.Font, sb, new PointF((this.ClientSize.Width - sz.Width) / 2F, (this.ClientSize.Height - sz.Height) / 2F));
                }
    
                e.Graphics.EndContainer(con);
            }
    

    Regards,

      Thorsten

  • Wednesday, October 19, 2011 1:42 PM
     
      Has Code

    If I may jump in...

    As you may have seen below, I was also trying to provide a solution. I didn't post mine because of Thorsten's great efforts, so I wanted to wait how things develop. However, if you may want to have a look at my approach...:

    Sorry, it's VB code, but I think you get the point:

       Class MyLabel
          Inherits Label
    
          Private f_Rotation As Double     'in degrees
          Private f_TextSize As SizeF
          Private f_EnclosingRectangleSize As SizeF
    
          Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
    
             Using b As New SolidBrush(ForeColor)
                Dim ControlCenter As PointF
                Dim TextCenter As PointF
    
                ControlCenter.X = ClientSize.Width / 2.0F
                ControlCenter.Y = ClientSize.Height / 2.0F
    
                Select Case TextAlign
                   Case ContentAlignment.BottomCenter, ContentAlignment.MiddleCenter, ContentAlignment.TopCenter
                      TextCenter.X = ControlCenter.X
                   Case ContentAlignment.BottomLeft, ContentAlignment.MiddleLeft, ContentAlignment.TopLeft
                      TextCenter.X = f_EnclosingRectangleSize.Width / 2
                   Case ContentAlignment.BottomRight, ContentAlignment.MiddleRight, ContentAlignment.TopRight
                      TextCenter.X = ClientSize.Width - f_EnclosingRectangleSize.Width / 2
                End Select
    
                Select Case TextAlign
                   Case ContentAlignment.BottomCenter, ContentAlignment.BottomLeft, ContentAlignment.BottomRight
                      TextCenter.Y = ClientSize.Height - f_EnclosingRectangleSize.Height / 2
                   Case ContentAlignment.MiddleCenter, ContentAlignment.MiddleLeft, ContentAlignment.MiddleRight
                      TextCenter.Y = ControlCenter.Y
                   Case ContentAlignment.TopCenter, ContentAlignment.TopLeft, ContentAlignment.TopRight
                      TextCenter.Y = f_EnclosingRectangleSize.Height / 2
                End Select
    
                e.Graphics.TranslateTransform(TextCenter.X, TextCenter.Y)
                e.Graphics.RotateTransform(CSng(f_Rotation))
    
                e.Graphics.DrawString(Text, Font, Brushes.White, -f_TextSize.Width / 2, -f_TextSize.Height / 2)
             End Using
    
          End Sub
          Protected Overrides Sub OnTextChanged(ByVal e As System.EventArgs)
             MyBase.OnTextChanged(e)
             UpdateTextSize()
          End Sub
          Protected Overrides Sub OnFontChanged(ByVal e As System.EventArgs)
             MyBase.OnFontChanged(e)
             UpdateTextSize()
          End Sub
          Public Property Rotation() As Double
             Get
                Return f_Rotation
             End Get
             Set(ByVal value As Double)
    
                If f_Rotation = value Then Return
    
                f_Rotation = value
                UpdateEnclosingRectangleSize()
                Invalidate()
    
             End Set
          End Property
          Private Sub UpdateTextSize()
    
             Using g = CreateGraphics()
                f_TextSize = g.MeasureString(Text, Font)
             End Using
    
             UpdateEnclosingRectangleSize()
    
          End Sub
          Private Sub UpdateEnclosingRectangleSize()
    
             f_EnclosingRectangleSize = GetEnclosingRectangleSize(f_TextSize, f_Rotation)
    
          End Sub
          Private Shared Function GetEnclosingRectangleSize(ByVal TextSize As SizeF, ByVal Angle As Double) As SizeF
    
             Dim AngleDeg = Angle * DEG
             Dim Corner1, Corner2 As PointF
             Dim result As SizeF
    
             Corner1.X = TextSize.Width / 2
             Corner1.Y = TextSize.Height / 2
             Corner2.X = TextSize.Width / 2
             Corner2.Y = TextSize.Height / -2
    
             Corner1 = RotatePoint(Corner1, Angle)
             Corner2 = RotatePoint(Corner2, Angle)
    
             result.Width = 2 * (Math.Max(Math.Abs(Corner1.X), Math.Abs(Corner2.X)))
             result.Height = 2 * (Math.Max(Math.Abs(Corner1.Y), Math.Abs(Corner2.Y)))
    
             Return result
    
          End Function
          Private Shared Function RotatePoint(ByVal Point As PointF, ByVal Angle As Double) As PointF
    
             Dim AngleDeg = Angle * DEG
    
             RotatePoint.X = CSng(Point.X * Math.Cos(AngleDeg) - Point.Y * Math.Sin(AngleDeg))
             RotatePoint.Y = CSng(Point.X * Math.Sin(AngleDeg) + Point.Y * Math.Cos(AngleDeg))
    
          End Function
       End Class
    
    
    The question from my first message in the thread which alignment strategy you prefer still remains, but both could be implemented easily now.



    Armin
  • Wednesday, October 19, 2011 1:46 PM
     
      Has Code

    Of course, text shouldn't be White always. ;-) Correction:

                Using fBrush As New SolidBrush(ForeColor)
                   e.Graphics.DrawString(Text, Font, fBrush, -f_TextSize.Width / 2, -f_TextSize.Height / 2)
                End Using
    
    



    Armin
  • Wednesday, October 19, 2011 2:16 PM
     
     

    Hi Armin,

    always do it and "jump in". I think, the more ideas shared, the more ways of solving presented, the better the overall solution will be.

    Regards,

      Thorsten