Search code examples
winformsautosize

No collapse if Control.Hide


I want to use AutoSize of TableLayoutPanel and FlowLayoutPanel to layout my Controls. After the parent Control has been layed out I want to freeze the layout, so if I hide a control, nothing should collapse. So I want to use Control.Visible but without collapsing it.

Background information: I have a Control that supports multiple languages. I want the design to adjust to the language automatically. If I click a CheckBox then sometimes some Controls are hiding or showing, which causes the whole design to change if I use AutoSize. I don't want the Controls to collapse, I just want the Controls to show the background of the parent Control and keep the size.

Example (vb.net)

Form1.vb

Public Class Form1
    Private Sub CheckBox1_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox1.CheckedChanged
        Label1.Visible = CheckBox1.Checked
    End Sub
End Class

Form1.Designer.vb

<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class Form1
    Inherits System.Windows.Forms.Form

    'Das Formular überschreibt den Löschvorgang, um die Komponentenliste zu bereinigen.
    <System.Diagnostics.DebuggerNonUserCode()> _
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        Try
            If disposing AndAlso components IsNot Nothing Then
                components.Dispose()
            End If
        Finally
            MyBase.Dispose(disposing)
        End Try
    End Sub

    'Wird vom Windows Form-Designer benötigt.
    Private components As System.ComponentModel.IContainer

    'Hinweis: Die folgende Prozedur ist für den Windows Form-Designer erforderlich.
    'Das Bearbeiten ist mit dem Windows Form-Designer möglich.  
    'Das Bearbeiten mit dem Code-Editor ist nicht möglich.
    <System.Diagnostics.DebuggerStepThrough()> _
    Private Sub InitializeComponent()
        Me.TableLayoutPanel1 = New System.Windows.Forms.TableLayoutPanel()
        Me.Label1 = New System.Windows.Forms.Label()
        Me.CheckBox1 = New System.Windows.Forms.CheckBox()
        Me.TableLayoutPanel1.SuspendLayout()
        Me.SuspendLayout()
        '
        'TableLayoutPanel1
        '
        Me.TableLayoutPanel1.AutoSize = True
        Me.TableLayoutPanel1.ColumnCount = 1
        Me.TableLayoutPanel1.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle())
        Me.TableLayoutPanel1.Controls.Add(Me.Label1, 0, 0)
        Me.TableLayoutPanel1.Controls.Add(Me.CheckBox1, 0, 1)
        Me.TableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill
        Me.TableLayoutPanel1.Location = New System.Drawing.Point(0, 0)
        Me.TableLayoutPanel1.Name = "TableLayoutPanel1"
        Me.TableLayoutPanel1.RowCount = 2
        Me.TableLayoutPanel1.RowStyles.Add(New System.Windows.Forms.RowStyle())
        Me.TableLayoutPanel1.RowStyles.Add(New System.Windows.Forms.RowStyle())
        Me.TableLayoutPanel1.Size = New System.Drawing.Size(800, 450)
        Me.TableLayoutPanel1.TabIndex = 0
        '
        'Label1
        '
        Me.Label1.AutoSize = True
        Me.Label1.Location = New System.Drawing.Point(3, 0)
        Me.Label1.Name = "Label1"
        Me.Label1.Size = New System.Drawing.Size(39, 13)
        Me.Label1.TabIndex = 0
        Me.Label1.Text = "Label1"
        '
        'CheckBox1
        '
        Me.CheckBox1.AutoSize = True
        Me.CheckBox1.Checked = True
        Me.CheckBox1.CheckState = System.Windows.Forms.CheckState.Checked
        Me.CheckBox1.Location = New System.Drawing.Point(3, 16)
        Me.CheckBox1.Name = "CheckBox1"
        Me.CheckBox1.Size = New System.Drawing.Size(81, 17)
        Me.CheckBox1.TabIndex = 1
        Me.CheckBox1.Text = "CheckBox1"
        Me.CheckBox1.UseVisualStyleBackColor = True
        '
        'Form1
        '
        Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
        Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
        Me.AutoSize = True
        Me.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink
        Me.ClientSize = New System.Drawing.Size(800, 450)
        Me.Controls.Add(Me.TableLayoutPanel1)
        Me.Name = "Form1"
        Me.Text = "Form1"
        Me.TableLayoutPanel1.ResumeLayout(False)
        Me.TableLayoutPanel1.PerformLayout()
        Me.ResumeLayout(False)
        Me.PerformLayout()

    End Sub

    Friend WithEvents TableLayoutPanel1 As TableLayoutPanel
    Friend WithEvents Label1 As Label
    Friend WithEvents CheckBox1 As CheckBox
End Class

Result:

result checkedresult not checked

I want the controls to not move around. I want the controls to keep the size if the label is hidden. The label should not collapse but paint the background of the parent control instead.


Solution

  • The best solution that I am sort of happy with is to write my own custom Panel that implements the desired behavior and put the target control into that custom Panel. The trick is to add a normal Panel to my custom Panel that hides the target control if Visible is changed to false.

    Solution in VB.NET

    NoCollapsePanel.vb

    Public Class NoCollapsePanel
        Public Enum VisibleModeE
            Collapse
            KeepSize
        End Enum
        Public Property VisibleMode As VisibleModeE
            Get
                Return _VisibleMode
            End Get
            Set
                If Value = VisibleMode Then Return
                _VisibleMode = Value
                RefreshVisibility()
            End Set
        End Property
    
        Private Sub RefreshVisibility()
            If _Visible Then Controls.Remove(hidePanel)
            Select Case VisibleMode
                Case VisibleModeE.Collapse
                    MyBase.Visible = _Visible
                Case VisibleModeE.KeepSize
                    MyBase.Visible = True
                    If Not Visible Then
                        ' this is doing the trick
                        hidePanel.Size = Size
                        Controls.Add(hidePanel)
                        hidePanel.BringToFront()
                    End If
            End Select
        End Sub
        Public Shadows Event VisibleChanged As EventHandler
    
        Private ReadOnly hidePanel As New Panel() With {.Margin = New Padding(0)}
        Private _Visible As Boolean = True
        Private _VisibleMode As VisibleModeE = VisibleModeE.KeepSize
    
        Public Overloads Property Visible As Boolean
            Get
                Return _Visible
            End Get
            Set
                If _Visible = Value Then Return
                _Visible = Value
                RefreshVisibility()
                RaiseEvent VisibleChanged(Me, New EventArgs())
            End Set
        End Property
    
        Protected Overrides Sub OnResize(eventargs As EventArgs)
            MyBase.OnResize(eventargs)
            hidePanel.Size = Size
        End Sub
    End Class
    

    NoCollapsePanel.Designer.vb

    <Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
    Partial Class NoCollapsePanel
        Inherits System.Windows.Forms.Panel
    
        'Das Steuerelement überschreibt den Löschvorgang zum Bereinigen der Komponentenliste.
        <System.Diagnostics.DebuggerNonUserCode()> _
        Protected Overrides Sub Dispose(ByVal disposing As Boolean)
            Try
                If disposing AndAlso components IsNot Nothing Then
                    components.Dispose()
                End If
            Finally
                MyBase.Dispose(disposing)
            End Try
        End Sub
    
        'Wird vom Steuerelement-Designer benötigt.
        Private components As System.ComponentModel.IContainer
    
        ' Hinweis: Die folgende Prozedur ist für den Komponenten-Designer erforderlich.
        ' Sie kann mit dem Komponenten-Designer geändert werden.  Das Bearbeiten mit
        ' dem Code-Editor ist nicht möglich.
        <System.Diagnostics.DebuggerStepThrough()> _
        Private Sub InitializeComponent()
            components = New System.ComponentModel.Container()
        End Sub
    
    End Class
    

    Testing the Panel

    Form1.vb

    Public Class Form1
        Public Sub New()
            ' Dieser Aufruf ist für den Designer erforderlich.
            InitializeComponent()
    
            ' Fügen Sie Initialisierungen nach dem InitializeComponent()-Aufruf hinzu.
            ComboBox1.DataSource = System.Enum.GetValues(GetType(NoCollapsePanel.VisibleModeE))
        End Sub
        Private Sub CheckBox1_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox1.CheckedChanged
            NoCollapsePanel1.Visible = CheckBox1.Checked
        End Sub
    
        Private rnd As New Random
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            BackColor = Color.FromArgb(255, rnd.Next(255), rnd.Next(255), rnd.Next(255))
        End Sub
    
        Private Sub CheckBox2_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox2.CheckedChanged
            Label1.Text = If(CheckBox2.Checked, "some text", "some very very very very long text")
        End Sub
    
        Private Sub NoCollapsePanel1_VisibleChanged(sender As Object, e As EventArgs) Handles NoCollapsePanel1.VisibleChanged
            TestVisibleChanged_Label.Text = DateTime.Now
        End Sub
    
        Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged
            NoCollapsePanel1.VisibleMode = ComboBox1.SelectedValue
        End Sub
    End Class
    

    Form1.Designer.vb

    <Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
    Partial Class Form1
        Inherits System.Windows.Forms.Form
    
        'Das Formular überschreibt den Löschvorgang, um die Komponentenliste zu bereinigen.
        <System.Diagnostics.DebuggerNonUserCode()> _
        Protected Overrides Sub Dispose(ByVal disposing As Boolean)
            Try
                If disposing AndAlso components IsNot Nothing Then
                    components.Dispose()
                End If
            Finally
                MyBase.Dispose(disposing)
            End Try
        End Sub
    
        'Wird vom Windows Form-Designer benötigt.
        Private components As System.ComponentModel.IContainer
    
        'Hinweis: Die folgende Prozedur ist für den Windows Form-Designer erforderlich.
        'Das Bearbeiten ist mit dem Windows Form-Designer möglich.  
        'Das Bearbeiten mit dem Code-Editor ist nicht möglich.
        <System.Diagnostics.DebuggerStepThrough()> _
        Private Sub InitializeComponent()
            Me.TableLayoutPanel1 = New System.Windows.Forms.TableLayoutPanel()
            Me.CheckBox1 = New System.Windows.Forms.CheckBox()
            Me.Button1 = New System.Windows.Forms.Button()
            Me.CheckBox2 = New System.Windows.Forms.CheckBox()
            Me.TestVisibleChanged_Label = New System.Windows.Forms.Label()
            Me.FlowLayoutPanel2 = New System.Windows.Forms.FlowLayoutPanel()
            Me.ComboBox1 = New System.Windows.Forms.ComboBox()
            Me.NoCollapsePanel1 = New WindowsApp4.NoCollapsePanel()
            Me.FlowLayoutPanel1 = New System.Windows.Forms.FlowLayoutPanel()
            Me.Label1 = New System.Windows.Forms.Label()
            Me.TableLayoutPanel1.SuspendLayout()
            Me.FlowLayoutPanel2.SuspendLayout()
            Me.NoCollapsePanel1.SuspendLayout()
            Me.FlowLayoutPanel1.SuspendLayout()
            Me.SuspendLayout()
            '
            'TableLayoutPanel1
            '
            Me.TableLayoutPanel1.AutoSize = True
            Me.TableLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink
            Me.TableLayoutPanel1.ColumnCount = 1
            Me.TableLayoutPanel1.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle())
            Me.TableLayoutPanel1.Controls.Add(Me.NoCollapsePanel1, 0, 0)
            Me.TableLayoutPanel1.Controls.Add(Me.CheckBox1, 0, 1)
            Me.TableLayoutPanel1.Controls.Add(Me.Button1, 0, 2)
            Me.TableLayoutPanel1.Controls.Add(Me.CheckBox2, 0, 3)
            Me.TableLayoutPanel1.Controls.Add(Me.TestVisibleChanged_Label, 0, 4)
            Me.TableLayoutPanel1.Controls.Add(Me.ComboBox1, 0, 5)
            Me.TableLayoutPanel1.Location = New System.Drawing.Point(3, 3)
            Me.TableLayoutPanel1.Name = "TableLayoutPanel1"
            Me.TableLayoutPanel1.RowCount = 6
            Me.TableLayoutPanel1.RowStyles.Add(New System.Windows.Forms.RowStyle())
            Me.TableLayoutPanel1.RowStyles.Add(New System.Windows.Forms.RowStyle())
            Me.TableLayoutPanel1.RowStyles.Add(New System.Windows.Forms.RowStyle())
            Me.TableLayoutPanel1.RowStyles.Add(New System.Windows.Forms.RowStyle())
            Me.TableLayoutPanel1.RowStyles.Add(New System.Windows.Forms.RowStyle())
            Me.TableLayoutPanel1.RowStyles.Add(New System.Windows.Forms.RowStyle())
            Me.TableLayoutPanel1.Size = New System.Drawing.Size(127, 134)
            Me.TableLayoutPanel1.TabIndex = 1
            '
            'CheckBox1
            '
            Me.CheckBox1.AutoSize = True
            Me.CheckBox1.Checked = True
            Me.CheckBox1.CheckState = System.Windows.Forms.CheckState.Checked
            Me.CheckBox1.Location = New System.Drawing.Point(3, 22)
            Me.CheckBox1.Name = "CheckBox1"
            Me.CheckBox1.Size = New System.Drawing.Size(83, 17)
            Me.CheckBox1.TabIndex = 1
            Me.CheckBox1.Text = "Show Panel"
            Me.CheckBox1.UseVisualStyleBackColor = True
            '
            'Button1
            '
            Me.Button1.AutoSize = True
            Me.Button1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink
            Me.Button1.Location = New System.Drawing.Point(3, 45)
            Me.Button1.Name = "Button1"
            Me.Button1.Size = New System.Drawing.Size(106, 23)
            Me.Button1.TabIndex = 2
            Me.Button1.Text = "Change BackColor"
            Me.Button1.UseVisualStyleBackColor = True
            '
            'CheckBox2
            '
            Me.CheckBox2.AutoSize = True
            Me.CheckBox2.Location = New System.Drawing.Point(3, 74)
            Me.CheckBox2.Name = "CheckBox2"
            Me.CheckBox2.Size = New System.Drawing.Size(92, 17)
            Me.CheckBox2.TabIndex = 3
            Me.CheckBox2.Text = "Change Label"
            Me.CheckBox2.UseVisualStyleBackColor = True
            '
            'TestVisibleChanged_Label
            '
            Me.TestVisibleChanged_Label.AutoSize = True
            Me.TestVisibleChanged_Label.Location = New System.Drawing.Point(3, 94)
            Me.TestVisibleChanged_Label.Name = "TestVisibleChanged_Label"
            Me.TestVisibleChanged_Label.Size = New System.Drawing.Size(88, 13)
            Me.TestVisibleChanged_Label.TabIndex = 4
            Me.TestVisibleChanged_Label.Text = "Visibility changed"
            '
            'FlowLayoutPanel2
            '
            Me.FlowLayoutPanel2.AutoSize = True
            Me.FlowLayoutPanel2.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink
            Me.FlowLayoutPanel2.Controls.Add(Me.TableLayoutPanel1)
            Me.FlowLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill
            Me.FlowLayoutPanel2.Location = New System.Drawing.Point(0, 0)
            Me.FlowLayoutPanel2.Name = "FlowLayoutPanel2"
            Me.FlowLayoutPanel2.Size = New System.Drawing.Size(800, 450)
            Me.FlowLayoutPanel2.TabIndex = 2
            '
            'ComboBox1
            '
            Me.ComboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList
            Me.ComboBox1.FormattingEnabled = True
            Me.ComboBox1.Location = New System.Drawing.Point(3, 110)
            Me.ComboBox1.Name = "ComboBox1"
            Me.ComboBox1.Size = New System.Drawing.Size(121, 21)
            Me.ComboBox1.TabIndex = 5
            '
            'NoCollapsePanel1
            '
            Me.NoCollapsePanel1.AutoSize = True
            Me.NoCollapsePanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink
            Me.NoCollapsePanel1.Controls.Add(Me.FlowLayoutPanel1)
            Me.NoCollapsePanel1.Location = New System.Drawing.Point(3, 3)
            Me.NoCollapsePanel1.Name = "NoCollapsePanel1"
            Me.NoCollapsePanel1.Size = New System.Drawing.Size(45, 13)
            Me.NoCollapsePanel1.TabIndex = 0
            Me.NoCollapsePanel1.VisibleMode = WindowsApp4.NoCollapsePanel.VisibleModeE.KeepSize
            '
            'FlowLayoutPanel1
            '
            Me.FlowLayoutPanel1.AutoSize = True
            Me.FlowLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink
            Me.FlowLayoutPanel1.Controls.Add(Me.Label1)
            Me.FlowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill
            Me.FlowLayoutPanel1.Location = New System.Drawing.Point(0, 0)
            Me.FlowLayoutPanel1.Name = "FlowLayoutPanel1"
            Me.FlowLayoutPanel1.Size = New System.Drawing.Size(45, 13)
            Me.FlowLayoutPanel1.TabIndex = 1
            '
            'Label1
            '
            Me.Label1.AutoSize = True
            Me.Label1.Location = New System.Drawing.Point(3, 0)
            Me.Label1.Name = "Label1"
            Me.Label1.Size = New System.Drawing.Size(39, 13)
            Me.Label1.TabIndex = 0
            Me.Label1.Text = "Label1"
            '
            'Form1
            '
            Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
            Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
            Me.AutoSize = True
            Me.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink
            Me.ClientSize = New System.Drawing.Size(800, 450)
            Me.Controls.Add(Me.FlowLayoutPanel2)
            Me.Name = "Form1"
            Me.Text = "Form1"
            Me.TableLayoutPanel1.ResumeLayout(False)
            Me.TableLayoutPanel1.PerformLayout()
            Me.FlowLayoutPanel2.ResumeLayout(False)
            Me.FlowLayoutPanel2.PerformLayout()
            Me.NoCollapsePanel1.ResumeLayout(False)
            Me.NoCollapsePanel1.PerformLayout()
            Me.FlowLayoutPanel1.ResumeLayout(False)
            Me.FlowLayoutPanel1.PerformLayout()
            Me.ResumeLayout(False)
            Me.PerformLayout()
    
        End Sub
    
        Friend WithEvents NoCollapsePanel1 As NoCollapsePanel
        Friend WithEvents Label1 As Label
        Friend WithEvents FlowLayoutPanel1 As FlowLayoutPanel
        Friend WithEvents TableLayoutPanel1 As TableLayoutPanel
        Friend WithEvents CheckBox1 As CheckBox
        Friend WithEvents FlowLayoutPanel2 As FlowLayoutPanel
        Friend WithEvents Button1 As Button
        Friend WithEvents CheckBox2 As CheckBox
        Friend WithEvents TestVisibleChanged_Label As Label
        Friend WithEvents ComboBox1 As ComboBox
    End Class
    

    enter image description here