Autosizing TextBox and ComboBox

rbulph

Junior Contributor
Joined
Feb 17, 2003
Messages
397
I'd like to have an autosize textbox and combobox. While these controls do have a (hidden) AutoSize property, setting this property doesn't seem to have any impact. So I have come up with the following code, which you will appreciate is not perfect. Any suggestions for improvements, especially removal of the figure 6?

Visual Basic:
Public Class Form1

    Private Sub TextBox1_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged, ComboBox1.TextChanged

        With CType(sender, Control)
            Dim lbl As New Label
            Me.Controls.Add(lbl)
            lbl.Font = .Font
            lbl.AutoSize = True
            lbl.Text = .Text
            Dim W As Integer = lbl.Width
            If TypeOf sender Is ComboBox Then W = W + SystemInformation.VerticalScrollBarWidth + 6
            .Width = W
            lbl.Dispose()
        End With

    End Sub

End Class
 
The AutoSize property is hidden because it is not implemented (hence, it does not work). There are many properties that are hidden because they aren't implemented or become irrelevant in a derived class.

Also, instead of creating a label to measure the size of a string, why not create a graphics object and use the MeasureString function to measure the string?
 
marble_eater said:
Also, instead of creating a label to measure the size of a string, why not create a graphics object and use the MeasureString function to measure the string?
Because a label is a closer approximation to a textbox than a graphics object. A control, be it a label or a textbox, consists of more than just the space that its text contains. It has borders and spare space around the edges. If I use MeasureString I get a value about 2.5 pixels less for the width than if I create a label and measure its width, and using a label removes the potential uncertainty in the actual amount of this 2.5 pixels.
 
Unfortunately, creating labels also requires much more resources than the graphics object, and it is not designed to do what you are using it for, which means that you may run into unexpected bugs and behavior might change in future versions. As far as taking borders into account, well, that's why we have the ClientRectangle property. We can compare ClientRectangle to Bounds and find the size difference, i.e. the border size.

Visual Basic:
Public Class AutosizeTextBox
    Inherits TextBox
 
    Dim _AutoSize As Boolean = True
    <System.ComponentModel.DefaultValue(True)> _
    Public Shadows Property AutoSize() As Boolean
        Get
            Return _AutoSize
        End Get
        Set(ByVal value As Boolean)
            _AutoSize = value
            If _AutoSize Then SizeControl()
        End Set
    End Property
 
    Protected Overrides Sub OnTextChanged(ByVal e As System.EventArgs)
        MyBase.OnTextChanged(e)
 
        If _AutoSize Then SizeControl()
    End Sub
 
    Sub SizeControl()
        'Use a graphics object to measure the text
        Dim g As Graphics = Me.CreateGraphics()
 
        'Measure the text
        Dim s As SizeF = g.MeasureString(Me.Text, Me.Font, 1000000)
 
        'Adjust for the border
        s.Width += Me.Width - Me.ClientRectangle.Width
        s.Height += Me.Height - Me.ClientRectangle.Height
        'Extra space for the caret
        s.Width += 3
 
        'Set the size
        Me.Size = s.ToSize
 
        'Hack: Because the box isnt resized until _after_
        'new text is typed, the text is scrolled to the left,
        'out of view. This code brings it back into view.
        Dim selection As Integer = SelectionStart
        SelectionStart = 0
        SelectionStart = selection
 
        'Dispose our IDisposables
        g.Dispose()
    End Sub
 
End Class
 
marble_eater said:
Unfortunately, creating labels also requires much more resources than the graphics object, and it is not designed to do what you are using it for, which means that you may run into unexpected bugs and behavior might change in future versions. As far as taking borders into account, well, that's why we have the ClientRectangle property. We can compare ClientRectangle to Bounds and find the size difference, i.e. the border size.

Visual Basic:
Public Class AutosizeTextBox
    Inherits TextBox
 
    Dim _AutoSize As Boolean = True
    <System.ComponentModel.DefaultValue(True)> _
    Public Shadows Property AutoSize() As Boolean
        Get
            Return _AutoSize
        End Get
        Set(ByVal value As Boolean)
            _AutoSize = value
            If _AutoSize Then SizeControl()
        End Set
    End Property
 
    Protected Overrides Sub OnTextChanged(ByVal e As System.EventArgs)
        MyBase.OnTextChanged(e)
 
        If _AutoSize Then SizeControl()
    End Sub
 
    Sub SizeControl()
        'Use a graphics object to measure the text
        Dim g As Graphics = Me.CreateGraphics()
 
        'Measure the text
        Dim s As SizeF = g.MeasureString(Me.Text, Me.Font, 1000000)
 
        'Adjust for the border
        s.Width += Me.Width - Me.ClientRectangle.Width
        s.Height += Me.Height - Me.ClientRectangle.Height
        'Extra space for the caret
        s.Width += 3
 
        'Set the size
        Me.Size = s.ToSize
 
        'Hack: Because the box isnt resized until _after_
        'new text is typed, the text is scrolled to the left,
        'out of view. This code brings it back into view.
        Dim selection As Integer = SelectionStart
        SelectionStart = 0
        SelectionStart = selection
 
        'Dispose our IDisposables
        g.Dispose()
    End Sub
 
End Class
OK, I didn't realise that ClientRectangle existed for controls. Shame it just gives the control's Bounds for combo boxes though.
 
Back
Top