AutoSize Label

I am using VS 2005 and writing a VB.net application for a Windows Mobile 5.0 Smartphone using .Net CF 2.0.

I need to determine the text height of multi-line wrapped text in a Label control so I can set the height of the Label to ensure that all text is visible, and then reposition other controls based on the new height of the Label.

Unfortunately, CF 2.0 Labels do not have an AutoSize property.

I have tried some examples using CreateGraphics and MeasureString such as the following...

Private Sub AutoSizeControl(ByVal ctlControl As Label, ByVal textPadding As Integer)

' Create a Graphics object for the Control

Dim g As Graphics = Me.CreateGraphics

' Get the Size needed to accommodate the formatted Text

Dim preferredSize As Size = g.MeasureString(ctlControl.Text, ctlControl.Font).ToSize()

' Pad the text and resize the control

ctlControl.ClientSize = New Size(preferredSize.Width + textPadding * 2, preferredSize.Height + textPadding * 2)

' Clean up the Graphics object

g.Dispose()

End Sub

... but with no success. The above code returns a prefferredSize as if the text is being written on a single line.

Any help/suggestions would be appreciated.



Answer this question

AutoSize Label

  • rboarman

    Couple of things are missing (one is my fault)

    1. You need to add a flag DT_WORDBREAK (and while you at it, DT_LEFT and DT_TOP for consistency)

    2. You need to select the proper font in the device context:

    Dim hFontOld as IntPtr = SelectObject(hDC, ctlLabel.Font.GetHFont())

    DrawText(...



  • tommyready

    Hi Alex,

    I've finally had time to get back to this issue and try your suggestion with the following code.

    Private Structure RECT

    Public Left As Integer

    Public Top As Integer

    Public Right As Integer

    Public Bottom As Integer

    End Structure

    Private Const DT_CALCRECT As Integer = &H400

    Private Const DT_MULTILINE As Integer = &H1

    Private Declare Function DrawText Lib "coredll.dll" (ByVal hdc As IntPtr, ByVal lpStr As String, ByVal nCount As Integer, ByRef lpRect As RECT, ByVal wFormat As Integer) As Integer

    Public Sub AutoSizeLabel(ByVal ctlLabel As Label)

    Dim uRECT As RECT

    ' Create a Graphics object from the form on which the label is located

    Dim objGraphics As Graphics = ctlLabel.TopLevelControl.CreateGraphics

    ' Get the Hdc of the Graphocs object

    Dim hDc As IntPtr = objGraphics.GetHdc

    With ctlLabel

    uRECT.Right = .Width

    uRECT.Bottom = .Height

    ' Calculate the rect of the text

    If DrawText(hDc, .Text, -1, uRECT, DT_CALCRECT Or DT_MULTILINE) <> 0 Then

    ' Apply the new height to the label

    .Height = uRECT.Bottom

    End If

    End With

    ' Clean up the Graphics object

    objGraphics.Dispose()

    End Sub

    But unfortunately it doesn't work. DrawText is still returning a RECT.Bottom that equals the height of a single line, even though the text is much longer than the width of the label.

    Am I doing something worng Any suggestions

    Tony


  • Prashant Dhingra

    Thanks Alex. The DT_WORDBREAK did the trick, and it now seems to work perfectly.

    Some things to note are...

    1. According to MSDN (http://msdn.microsoft.com/library/default.asp url=/library/en-us/wcefonts5/html/wce50lrfDrawText.asp) the DrawText API in Windows CE does not support DT_MULTILINE and "...assumes that all formatting has multiple lines unless you specify the DT_SINGLELINE".

    2. To get the handle from the Font object use the ToHfont method. The GetHFont method does not exist.

    For the record, the following is my entire Module. Hope this helps others with the same issue.

    Module CeAutoSizeLabel

    ' Externals ----------------------

    Private Structure RECT

    Public Left As Integer

    Public Top As Integer

    Public Right As Integer

    Public Bottom As Integer

    End Structure

    Private Const DT_CALCRECT As Integer = &H400

    Private Const DT_CENTER As Integer = &H1

    Private Const DT_LEFT As Integer = &H0

    Private Const DT_RIGHT As Integer = &H2

    Private Const DT_TOP As Integer = &H0

    Private Const DT_WORDBREAK As Integer = &H10

    Private Declare Function DeleteObject Lib "coredll.dll" (ByVal hObject As IntPtr) As Integer

    Private Declare Function DrawText Lib "coredll.dll" (ByVal hdc As IntPtr, ByVal lpStr As String, ByVal nCount As Integer, ByRef lpRect As RECT, ByVal wFormat As Integer) As Integer

    Private Declare Function SelectObject Lib "coredll.dll" (ByVal hdc As IntPtr, ByVal hObject As IntPtr) As IntPtr

    Public Sub AutoSizeLabelHeight(ByVal ctlLabel As Label)

    ' Auto size the height of a Label control based on the contents of the label.

    ' Note: This routine is best called from the Form's Resize event so that changes to screen size and orientation

    ' will force the Label height to be adjusted.

    Dim uRECT As RECT

    Try

    ' Create a Graphics object. We need a Graphics object so we can get a handle to a Device Context to be used

    ' later in the DrawText API. However the Label control in CF2.0 does not support the CreateGraphics method,

    ' so create the Graphics object from the form on which the Label is located.

    ' Note: This can cause an exception when this routine is called from a Form's Resize event. Probably because

    ' the form is not fully initialised and the Resize event may be called one or more times during form

    ' initialisation. Therefore, the entire routine is wrapped in a Try/Catch block.

    Dim objGraphics As Graphics = ctlLabel.TopLevelControl.CreateGraphics

    ' -------------------------------------------------------------

    ' Note: An alternative to the above method of creating a Graphics object is to create a Bitmap object and

    ' obtain the Graphics object from it as follows.

    'Dim objBitmap As New Bitmap(1, 1)

    'Dim objGraphics As Graphics = Graphics.FromImage(objBitmap)

    ' And remembering to dispose of the Bitmap object in a cleanup at the end of the routine as follows.

    'objBitmap.Dispose()

    ' This method would remove the need for the Try/Catch block, but means that Bitmap objects are repeatedly

    ' being created and destroyed.

    ' -------------------------------------------------------------

    ' Get the handle to the Device Context of the Graphics object

    Dim hDc As IntPtr = objGraphics.GetHdc

    With ctlLabel

    ' Get the handle to the Font of the Label

    Dim hFont As IntPtr = .Font.ToHfont

    ' Apply the Font to the Graphics object

    Dim hFontOld As IntPtr = SelectObject(hDc, hFont)

    ' Set the initial size of the Rect

    uRECT.Right = .Width

    uRECT.Bottom = .Height

    ' Build the base format

    Dim lFormat As Integer = DT_CALCRECT Or DT_WORDBREAK Or DT_TOP

    ' -------------------------------------------------------------

    ' Adjust the format to the Label's text alignment.

    ' Note: This probably isn't necessary as the horizontal alignment of text shouldn't affect the text

    ' height calculation. But just in case...

    Select Case .TextAlign

    Case ContentAlignment.TopLeft

    lFormat = lFormat Or DT_LEFT

    Case ContentAlignment.TopCenter

    lFormat = lFormat Or DT_CENTER

    Case ContentAlignment.TopRight

    lFormat = lFormat Or DT_RIGHT

    End Select

    ' -------------------------------------------------------------

    ' Calculate the Rect of the text

    If DrawText(hDc, .Text, -1, uRECT, lFormat) <> 0 Then ' Success

    ' Apply the new height to the label

    .Height = uRECT.Bottom

    End If

    ' Cleanup ----------------------------

    ' Set the Font of the Graphics object back to the original

    SelectObject(hDc, hFontOld)

    ' Delete the handle to the Font of the Label

    DeleteObject(hFont)

    End With

    ' Clean up the Graphics object

    objGraphics.Dispose()

    Catch

    ' Do nothing

    'Debug.WriteLine("AutoSizeLabelHeight failed.")

    End Try

    End Sub

    End Module

    Tony


  • amolchopra

    The way I do this is by P/Invoking DrawText with DT_CALCRECT|DT_MULTILINE parameter. CF2.0 gives you access to hDC (via Graphics class) and HFONT (via Font class), which is all you need

  • Sean P. Mehrlander

    Made some improvements to the above code as follows...

    1. The line ...

    uRECT.Right = .Width

    ..should read...

    uRECT.Right = .Width - 2

    ...to accommodate for the 1 pixel internal margin that the Label control has. Needed to get the height calc correct for very long text.

    2. The line...

    uRECT.Bottom = .Height

    ...is not required and can be removed.

    3. The line...

    .Height = uRECT.Bottom

    ...should read

    .Height = uRECT.Bottom + 2

    ...to add a 2 pixel margin at the bottom of the Label. Just looks better.

     

    Tony


  • AutoSize Label