Inherited Label with transparency and text rotation

VorTechS

Member
Joined
Oct 19, 2006
Messages
12
Programming Experience
10+
I'm having a problem with an inherited label, applying text rotation to
aligned text.
If text rotation is applied to the aligned text, the alignment goes 'nuts'.
I can find no logic to what is happening.

I've built the following code from several examples, if you
move the Drawstring before the rotation then alignment works fine (but you don't get rotation, unsurprisingly enough):

VB.NET:
Imports System.ComponentModel 
 
Public Class TransparentLabel 
Inherits Label 
 
Private _RotationAngle As Integer 
 
#Region " Windows Form Designer generated code " 
 
Public Sub New() 
MyBase.New() 
 
'This call is required by the Windows Form Designer. 
InitializeComponent() 
 
'Add any initialization after the InitializeComponent() call 
 
End Sub 
 
'UserControl1 overrides dispose to clean up the component list. 
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) 
If disposing Then 
If Not (components Is Nothing) Then 
components.Dispose() 
End If 
End If 
MyBase.Dispose(disposing) 
End Sub 
 
'Required by the Windows Form Designer 
Private components As System.ComponentModel.IContainer 
 
'NOTE: The following procedure is required by the Windows Form Designer 
'It can be modified using the Windows Form Designer. 
'Do not modify it using the code editor. 
<System.Diagnostics.DebuggerStepThrough()> Private Sub 
InitializeComponent() 
' 
'TransparentLabel 
' 
Me.BackColor = System.Drawing.Color.DeepPink 
Me.Name = "TransparentLabel" 
Me.Size = New System.Drawing.Size(256, 46) 
 
End Sub 
 
#End Region 
Public Property RotationAngle() As Integer 
Get 
RotationAngle = _RotationAngle 
End Get 
Set(ByVal value As Integer) 
_RotationAngle = value 
Me.Invalidate() 
End Set 
End Property 
 
Private Sub TransparentLabel_Paint(ByVal sender As Object, ByVal e As 
System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint 
Try 
Dim sFormat As StringFormat = Nothing 
 
sFormat = New StringFormat 
 
If Me.TextAlign <= ContentAlignment.TopRight Then 
sFormat.LineAlignment = StringAlignment.Near 
ElseIf Me.TextAlign >= ContentAlignment.BottomRight Then 
sFormat.LineAlignment = StringAlignment.Far 
Else 
sFormat.LineAlignment = StringAlignment.Center 
End If 
 
If Me.TextAlign = ContentAlignment.BottomLeft Or Me.TextAlign = 
ContentAlignment.MiddleLeft Or Me.TextAlign = ContentAlignment.TopLeft Then 
sFormat.Alignment = StringAlignment.Near 
ElseIf Me.TextAlign = ContentAlignment.BottomRight Or 
Me.TextAlign = ContentAlignment.MiddleRight Or Me.TextAlign = 
ContentAlignment.TopRight Then 
sFormat.Alignment = StringAlignment.Far 
Else 
sFormat.Alignment = StringAlignment.Center 
End If 
 
'variables to capture the size of the text area 
Dim width As Double = e.Graphics.MeasureString(Text, 
Me.Font).Width 
Dim height As Double = e.Graphics.MeasureString(Text, 
Me.Font).Height 
'convert the rotation angle into radians for trig functions 
Dim angleRadian As Double = ((_rotationAngle Mod 360) / 180) * 
Math.PI 
'capture the forground color as a brush 
Dim myBrush As Brush = New SolidBrush(Me.ForeColor) 
 
'If Me.BackColor = Color.Transparent Then 
If Me.AutoSize Then 
Dim Siz As Drawing.SizeF = e.Graphics.MeasureString(Me.Text, 
Font) 
Me.Width = Siz.Width + 1 
Me.Height = Siz.Height + 1 
End If 
 
 
Dim B As New Bitmap(Me.Width, Me.Height) 
Dim G As Graphics = Graphics.FromImage(B) 
Dim TextArea As New Rectangle(0, 0, Me.Width, Me.Height) 
 
If Me.BackColor = Color.Transparent Then 
G.FillRectangle(New SolidBrush(Color.DeepPink), TextArea) 
Else 
G.FillRectangle(New SolidBrush(Me.BackColor), TextArea) 
End If 
 
G.TranslateTransform(CInt((ClientRectangle.Width + (height * 
Math.Sin(angleRadian)) - (width * Math.Cos(angleRadian))) / 2), 
CInt((ClientRectangle.Height - (height * Math.Cos(angleRadian)) - (width * 
Math.Sin(angleRadian))) / 2)) 
G.RotateTransform(CInt(_RotationAngle)) 
G.DrawString(Me.Text, Font, myBrush, TextArea, sFormat) 
G.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias 
G.ResetTransform() 
 
Dim Pth As New Drawing2D.GraphicsPath() 
Dim X, Y As Short 
For X = 0 To Me.Width - 1 
For Y = 0 To Me.Height - 1 
If B.GetPixel(X, Y).ToArgb.ToString = 
Color.DeepPink.ToArgb.ToString Then 
Pth.AddRectangle(New Rectangle(X, Y, 1, 1)) 
End If 
Next 
Next 
 
Dim Rgn As Region 
 
If Me.BackColor = Color.Transparent Then 
G.FillRectangle(New SolidBrush(Me.ForeColor), New 
Rectangle(0, 0, Me.Width, Me.Height)) 
End If 
 
e.Graphics.DrawImage(B, 0, 0) 
Rgn = New Region(New Rectangle(0, 0, Me.Width, Me.Height)) 
If Me.BackColor = Color.Transparent Then 
Rgn.Exclude(Pth) 
End If 
Me.Region = Rgn 
 
B.Dispose() 
G.Dispose() 
Catch ex As Exception 
End Try 
End Sub 
 
Private Sub TransparentLabel_Load(ByVal sender As Object, ByVal e As 
System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint 
SetStyle(ControlStyles.AllPaintingInWmPaint, True) 
SetStyle(ControlStyles.DoubleBuffer, True) 
SetStyle(ControlStyles.UserPaint, True) 
End Sub 
End Class

Can anyone advise me what I'm doing wrong to throw the alignment out?

Stuart
 
You could try this with a matrix instead....

VB.NET:
Dim myMatrix As Drawing2D.Matrix
 
  myMatrix = g.Transform()
 
        myMatrix.RotateAt(45, New PointF(TextArea.Width / 2, TextArea.Height / 2), _
Drawing.Drawing2D.MatrixOrder.Append)
      g.Transform = myMatrix

Haven't tested this by the way.
 
Thanks, I've given it a try.

Assuming the '45' represents the rotation angle,the text will rotate correctly when aligned to the middlecenter.
Aligned anywhere else, the text does not appear.

What I am looking to happen, is for the text to be aligned, and then rotated at it's center.

Any more ideas?
 
There are a couple of things in your post that i don't quite understand. Why are you drawing the string onto a bitmap and then drawing the bitmap onto the screen? Why not just use the graphics object passed in the paint event to draw the string from the start?

But back to the problem, without trying this it is hard to say. I can't think of any reason why the stringformat would interfere with the rotating of the text. How about drawing the string onto the bitmap and then using the RotateFlip method of the bitmap class to rotate.
 
Firstly, I'm not a GDI expert in any sense of the word....

I am creating a transparent, rotatable, alignable label control using a number of different examples I picked up off the web that use GDI (the only way to get transparency to work). Until I started with the text rotation everything appeared to be working fine.

If there is a better way to implement this, I'd love to know! :)

As it is, I've just been playing around further with the code, and I think it might be the string alignment that might be at fault, as I'm not getting the expected results without rotation.

I'll let you know shortly...
 
Right, sorted out non-rotated text align.

VB.NET:
Imports System.ComponentModel
Public Class TransparentLabel
Inherits Label
Private _RotationAngle As Integer
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
'Add any initialization after the InitializeComponent() call
End Sub
'UserControl1 overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer. 
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
'
'TransparentLabel
'
Me.BackColor = System.Drawing.Color.DeepPink
Me.Name = "TransparentLabel"
Me.Size = New System.Drawing.Size(256, 46)
End Sub
#End Region
Public Property RotationAngle() As Integer
Get
RotationAngle = _RotationAngle
End Get
Set(ByVal value As Integer)
_RotationAngle = value
End Set
End Property
Private Sub TransparentLabel_Paint(ByVal sender As Object, 
ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
Try
Dim sFormat As StringFormat = Nothing
sFormat = New StringFormat
'HORIZONTAL ALIGNMENT
If Me.TextAlign = ContentAlignment.BottomLeft Or Me.TextAlign = 
ContentAlignment.MiddleLeft Or Me.TextAlign = ContentAlignment.TopLeft Then
'Top, Middle or Bottom Left
sFormat.Alignment = StringAlignment.Near
ElseIf Me.TextAlign = ContentAlignment.BottomRight Or Me.TextAlign = 
ContentAlignment.MiddleRight Or Me.TextAlign = ContentAlignment.TopRight Then
'Top, Middle or Bottom Right
sFormat.Alignment = StringAlignment.Far
Else
'Anywhere in the middle
sFormat.Alignment = StringAlignment.Center
End If
'VERTICAL ALIGNMENT
If Me.TextAlign = ContentAlignment.BottomLeft Or Me.TextAlign = 
ContentAlignment.BottomCenter Or Me.TextAlign = ContentAlignment.BottomRight Then
sFormat.LineAlignment = StringAlignment.Far
ElseIf Me.TextAlign = ContentAlignment.TopLeft Or Me.TextAlign = 
 
ContentAlignment.TopCenter Or Me.TextAlign = ContentAlignment.TopRight Then
sFormat.LineAlignment = StringAlignment.Near
Else
sFormat.LineAlignment = StringAlignment.Center
End If
'variables to capture the size of the text area
Dim width As Double = e.Graphics.MeasureString(Text, Me.Font).Width
Dim height As Double = e.Graphics.MeasureString(Text, Me.Font).Height
'convert the rotation angle into radians for trig functions
Dim angleRadian As Double = ((_rotationAngle Mod 360) / 180) * Math.PI
'capture the forground color as a brush
Dim myBrush As Brush = New SolidBrush(Me.ForeColor)
'If Me.BackColor = Color.Transparent Then
If Me.AutoSize Then
Dim Siz As Drawing.SizeF = e.Graphics.MeasureString(Me.Text, Font)
Me.Width = Siz.Width + 1
Me.Height = Siz.Height + 1
End If
 
Dim B As New Bitmap(Me.Width, Me.Height)
Dim G As Graphics = Graphics.FromImage(B)
Dim TextArea As New Rectangle(0, 0, Me.Width, Me.Height)
If Me.BackColor = Color.Transparent Then
G.FillRectangle(New SolidBrush(Color.DeepPink), TextArea)
Else
G.FillRectangle(New SolidBrush(Me.BackColor), TextArea)
End If
 
Dim myMatrix As Drawing2D.Matrix
myMatrix = G.Transform()
myMatrix.RotateAt(_RotationAngle, New PointF(TextArea.Width / 2, 
TextArea.Height / 2), Drawing.Drawing2D.MatrixOrder.Append)
G.Transform = myMatrix
G.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
G.DrawString(Me.Text, Font, myBrush, TextArea, sFormat)
'G.TranslateTransform(CInt((ClientRectangle.Width + (height * 
Math.Sin(angleRadian)) - (width * Math.Cos(angleRadian))) / 2), 
CInt((ClientRectangle.Height - (height * Math.Cos(angleRadian)) - 
(width * Math.Sin(angleRadian))) / 2))
'G.RotateTransform(CInt(_RotationAngle))
'G.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
'G.DrawString(Me.Text, Font, myBrush, TextArea, sFormat)
'G.ResetTransform()
Dim Pth As New Drawing2D.GraphicsPath()
Dim X, Y As Short
For X = 0 To Me.Width - 1
For Y = 0 To Me.Height - 1
If B.GetPixel(X, Y).ToArgb.ToString = Color.DeepPink.ToArgb.ToString Then
Pth.AddRectangle(New Rectangle(X, Y, 1, 1))
End If
Next
Next
Dim Rgn As Region
If Me.BackColor = Color.Transparent Then
G.FillRectangle(New SolidBrush(Me.ForeColor), New Rectangle(0, 0, Me.Width, Me.Height))
End If
e.Graphics.DrawImage(B, 0, 0)
Rgn = New Region(New Rectangle(0, 0, Me.Width, Me.Height))
If Me.BackColor = Color.Transparent Then
Rgn.Exclude(Pth)
End If
Me.Region = Rgn
B.Dispose()
G.Dispose()
Catch ex As Exception
End Try
End Sub
Private Sub TransparentLabel_Load(ByVal sender As Object, 
ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
SetStyle(ControlStyles.AllPaintingInWmPaint, True)
SetStyle(ControlStyles.DoubleBuffer, True)
SetStyle(ControlStyles.UserPaint, True)
End Sub
End Class

I think bitmaps are used to perform exclusion to achieve transparency,
showing the underlying object...? This is where my ignorance shows unfortunately.
This code is also inheriting from the label control.

I think I'm trying to do this in the wrong way, by assuming the string alignment
would occur in the painted control and then the rotation applied whereas
I suspect the string alignment is occuring after the rotation. Perhaps
one day I'll understand...! :)
 
Okay I'm beginning to understand how this rotation/alignment is working with each other.

With the amended code using the Matrix, the text is being aligned before rotation and the rectangle is then rotated. This behaviour can be observed by ensuring that the control size is square. If the control size is square and alignment is TopLeft with an angle of 90 degrees, the text is written at point 0,0 within the rectangle and then rotated the 90 degrees appearing in the top right corner of the created control.

Rather than using creating the rectange at 0,0, I guess I need to work out the alignment and create it at a calculated location based on the size of the text and then rotate that rectangle, rather than one the size of the control.

Makes sense?
 
Yea, i think i'n with you on this. Implementing in that manner should solve your problem. I'm still not so sure about the whole way you are doing this though. What i really need is to be able to see what effect you are trying to achieve. A screen shot perhaps. If i've got the picture in my head then i'll be able to better advise.
 
Okay, here are a couple of examples of how I am trying to make the relation between alignment and rotation when a rotation angle is given:

TopLeft.JPG
TopRight.JPG

BottomLeft.JPG
MiddleLeft.JPG


And so on and so forth....
 
If thats the case then whats with the whole region manipulation thing. It's a label right, so what are you trying to do with the region of the label?
 
You mean making the bitmap you have created transparent that you have drawn your text on? Right lets start again here.

In the constructor for your inherited label...

VB.NET:
MyBase.SetStyle(ControlStyles.AllPaintingInWMPaint,True)
Mybase.SetStyle(ControlStyles.OptimizedDoubleBuffer,True)
Mybase.SetStyle(ControlStyles.UserPaint,True)
Mybase.SetStyle(ControlStyles.SupportsTransparentBackcolor,True)


Open up the paint event handler for ths control....

Then usingn the matrix i provided justdraw the string directly onto the label. No regions or bitmaps needed.

VB.NET:
' Get the size if the string
Dim TxtSize As Size = Size.Ceiling(e.MeasureString(text,font))
TxtSize.Width +=2
TxtSize.Height+=2
 
 
e.Graphics.DrawString(text, blah, blah, blah)
 
You mean making the bitmap you have created transparent that you have drawn your text on? Right lets start again here.

In the constructor for your inherited label...

VB.NET:
MyBase.SetStyle(ControlStyles.AllPaintingInWMPaint,True)
Mybase.SetStyle(ControlStyles.OptimizedDoubleBuffer,True)
Mybase.SetStyle(ControlStyles.UserPaint,True)
Mybase.SetStyle(ControlStyles.SupportsTransparentBackcolor,True)


Open up the paint event handler for ths control....

Then usingn the matrix i provided justdraw the string directly onto the label. No regions or bitmaps needed.

VB.NET:
' Get the size if the string
Dim TxtSize As Size = Size.Ceiling(e.MeasureString(text,font))
TxtSize.Width +=2
TxtSize.Height+=2
 
 
e.Graphics.DrawString(text, blah, blah, blah)

Okay, there's no rotation on the text, although transparency appears to be okay!
 
Back
Top