Resizing Label Control for VB.NET
…And now for something completely different, as they say.
I’ve tried and failed to do something similar to this before, but finally with the help of jo0ls on XVBT, the problem has been tracked down to a rounding error. The idea is to have a control (a label in this case, although the same method should work for a textbox or other type of control as well) that automatically adjusts its height to fit its contents. A simple concept, one would think. And at first glance, one might also think that the AutoSize property of a label would already do that, but no, a look at MSDN explains that AutoSize is of no use once you have text that wraps.
So here’s my solution; feel free to use it as you see fit (and leave a comment if you find it useful, problematic, etc.).
Public Class SmartSizeLabel Inherits System.Windows.Forms.Label #Region " Local declarations " Private _noReentry As Boolean = False #End Region #Region " Public properties " Public Overrides Property AutoSize() As Boolean 'Force AutoSize to false; there's not much point in this control if we aren't word-wrapping Get Return False End Get Set(ByVal value As Boolean) MyBase.AutoSize = False End Set End Property #End Region #Region " Overridden methods " Protected Overrides Sub OnResize(ByVal e As System.EventArgs) MyBase.OnResize(e) If Not _noReentry Then _SetBestHeight() End Sub Protected Overrides Sub OnStyleChanged(ByVal e As System.EventArgs) MyBase.OnStyleChanged(e) _SetBestHeight() End Sub Protected Overrides Sub OnMarginChanged(ByVal e As System.EventArgs) MyBase.OnMarginChanged(e) _SetBestHeight() End Sub Protected Overrides Sub OnTextChanged(ByVal e As System.EventArgs) MyBase.OnTextChanged(e) _SetBestHeight() End Sub Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs) 'No need to take the bottom margin into account here; it's already been done ' in _SetBestHeight(), when adjusting the size of the control itself (and ' thus its ClientRectangle). Dim rect As New RectangleF(Me.Margin.Left, Me.Margin.Top, _ Me.ClientRectangle.Width - Me.Margin.Left - Me.Margin.Right, Me.ClientRectangle.Height) e.Graphics.DrawString(Me.Text, Me.Font, New SolidBrush(Me.ForeColor), rect, _ StringFormat.GenericDefault) End Sub #End Region #Region " Local methods " Private Sub _SetBestHeight() Dim gfx As Graphics = Me.CreateGraphics() _SetBestHeight(gfx) gfx.Dispose() End Sub Private Sub _SetBestHeight(ByVal gfx As Graphics) 'This takes into account any space taken up by the control border Dim diff As Integer = Me.Height - Me.ClientSize.Height 'This will get called from OnResize, and also will generate a resize event, so ' use a flag to avoid unnecessary recursion _noReentry = True 'Adjust height for border and margins Me.Height = _BestHeight(gfx) + diff + Me.Margin.Top + Me.Margin.Bottom _noReentry = False End Sub Private Function _BestHeight(ByVal gfx As Graphics) As Integer 'Get size of the part of the ClientRectangle that the text will go in; use a really ' big height so that all the string will fit (hopefully -- if the height's larger ' than MaxValue, we've got other issues =P) in order to measure it accurately. Dim srcSize As New SizeF(Me.ClientRectangle.Width - Me.Margin.Left - Me.Margin.Right, _ Single.MaxValue) Dim bestSize As SizeF = gfx.MeasureString(Me.Text, Me.Font, srcSize, _ Drawing.StringFormat.GenericDefault) Return Convert.ToInt32(Math.Ceiling(bestSize.Height)) End Function #End Region End Class