【问题标题】:How to get Winforms Panel to correctly layout many items?如何让 Winforms 面板正确布局许多项目?
【发布时间】:2014-12-18 14:11:24
【问题描述】:

我有一个包含大量子控件的 WinForms 面板控件。每个孩子都停靠在左边,导致内容的水平宽度增加。包含面板设置了其 AutoScroll 属性,以便您可以访问所有内容。

当内容的总宽度变得太大时,我遇到了问题。一旦达到这个最大宽度,其他内容元素就会放置在现有内容的顶部,而不是放在右侧。但是,如果我在完成初始布局后调整面板的大小,它会通过扩展其逻辑宽度并将每个内容元素放置在正确的位置来进行自我纠正。如何在用户调整窗口大小之前正确布局

这是一个简单的例子:

Form1.vb

Public Class Form1
    Protected Overrides Sub OnLoad(e As EventArgs)
        MyBase.OnLoad(e)

        For i As Integer = 1 To 200
            Dim gb As New GroupBox
            gb.Text = "Box " & i.ToString
            gb.Width = 250
            gb.Dock = DockStyle.Left
            Panel1.Controls.Add(gb)
            gb.BringToFront()
        Next
    End Sub
End Class

Form1.Designer.vb

Partial Class Form1
    Inherits System.Windows.Forms.Form

    Private Sub InitializeComponent()
        Me.Panel1 = New System.Windows.Forms.Panel()
        Me.SuspendLayout()
        '
        'Panel1
        '
        Me.Panel1.AutoScroll = True
        Me.Panel1.Dock = System.Windows.Forms.DockStyle.Fill
        Me.Panel1.Location = New System.Drawing.Point(0, 0)
        Me.Panel1.Name = "Panel1"
        Me.Panel1.Size = New System.Drawing.Size(284, 262)
        Me.Panel1.TabIndex = 0
        '
        'Form1
        '
        Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
        Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
        Me.ClientSize = New System.Drawing.Size(284, 262)
        Me.Controls.Add(Me.Panel1)
        Me.Name = "Form1"
        Me.Text = "Form1"
        Me.ResumeLayout(False)

    End Sub
    Friend WithEvents Panel1 As System.Windows.Forms.Panel
End Class

这是窗口第一次出现时的样子,几乎滚动到最后,这样您就可以看到问题区域。请注意,框 183 到 199 缺失,因为它们被放置在彼此的顶部。这是不对的。

这是手动调整窗口大小后的样子,几乎滚动到最后。面板会根据调整大小自行修复;面板的总逻辑宽度自动扩展到足以容纳所有内容。这就是我希望它第一次出现时的样子。

我尝试手动设置每个框的位置,并尝试调用 PerformLayout() 和其他几个函数。似乎没有任何效果。到目前为止,我还没有找到获得良好布局的神奇组合。有谁知道如何解决这个问题?

编辑

这是一个屏幕截图,可能会使问题更加明显。我调整了框的宽度和框的数量以更好地显示问题。看看最后一个框如何与框 656 重叠?从 657 到 700 的每个盒子都有相同的错误位置。关闭停靠并自行设置位置无济于事。

【问题讨论】:

  • autoscroll 不是自动定位,您需要在添加面板/框时使用滚动面板的AutoScrollPosition 设置它们的位置
  • 这并不能解决问题。问题不在于面板滚动到的位置,问题在于您在向右滚动后看到的项目位于错误的位置,彼此堆叠在一起。仔细看看第一个屏幕截图有什么问题。

标签: vb.net winforms panel


【解决方案1】:

AutoScroll 不是AutoPositionMyChildren。来自MSDN

以编程方式向窗体添加控件时,使用 AutoScrollPosition 属性将控件定位在当前可视滚动区域的内部或外部。

如果您循环通过控件来打印它们的位置,您会在某个点(可能在 130 左右)看到Location.Y 固定在 32767,可能是某个默认未滚动的最大值。这也是它们开始堆叠的点,因为它们实际上具有相同的初始位置。您拥有的一些代码弥补了这一点,但它并不完全正确。滚动后,面板会修复子控件上的坐标。

首先,我建议您将 Panel1.AutoScrollMinSize 设置为 {480, 0} 以便在设计时出现 HScroll 栏;这允许您为添加控件时不会导致 VScroll 的框计算一个合适的高度。

Dim gb As GroupBox

' only 150 because problem is when (i * width) > 32k
For i As Integer = 0 To 150           

    gb = New GroupBox 
    gb.Name = i.ToString                     ' added
    gb.Text = "Box " & i.ToString
    gb.Width = 250
    ' no docking so set the height
    gb.Height = Panel1.Bounds.Height - 30       ' trying to avoid the VSCroll

    ' set location explicitly
    gb.Location = NewCtlLocation(Panel1.Controls.Count, 
                  Panel1.AutoScrollPosition.X)

    ' Dock and Anchor mess up the AutoScroll
    'gb.Dock = DockStyle.Left
    Panel1.Controls.Add(gb)

    ' allow panel to update its scroll positions
    Panel1.ScrollControlIntoView(gb)

    ' not needed; seems to offset something with Dock
    ' changing ZOrder may not always be desirable
    'gb.BringToFront()

    ' debug illumination
    Console.WriteLine("{0} {1} {2}", i.ToString, 
                          Panel1.AutoScrollPosition.X.ToString,
                          gb.Location.X.ToString)
Next
'Go back to start
Panel1.ScrollControlIntoView(Panel1.Controls("0"))

定位助手,以便您可以调整装订线或边距(更换码头):

Friend Function NewCtlLocation(ByVal n As Integer, 
              ByVal ScrollPosX As Integer) As Point
    Const TopMargin As Integer = 5
    Const LeftMargin As Integer = 5

    Return New Point((n * 250) + ScrollPosX, 0)
End Function

注意事项:

我有一个垂直滚动条,它反复添加多达 120 个用户控件,效果很好,但它不需要/使用ScrollControlIntoView,而且它们永远不会像你的那样堆叠起来。我怀疑可能是因为它们更小。在添加下一个之前至少还有一两秒钟,这可能很重要。但是,很高兴知道。

可能使用面板的ControlAdded 事件来做某事,但它可能相当于ScrollControlIntoView。最后只做一次是行不通的,所以在添加时使用它可以让一些东西在你去的时候得到更新。

通过正确的摆弄,您也许可以让Dock 工作,但我怀疑这可能是问题的一部分,例如以这种方式设置的高度和左侧不会更新面板的内部滚动图。

您的框实际上看起来比 250 窄 - 是否启用了自动调整大小?

Suspend/Resume Layout 伤害而不是帮助 - 它们阻止控件对正在填充的虚拟区域执行任何操作。它应该发生得足够快,以至于没有人会看到任何东西。结果:

在我的系统上工作TM

【讨论】:

  • "你的盒子实际上看起来比 250 窄。"屏幕截图基于带有较小框的代码版本。
【解决方案2】:

看起来像滚动信息的错误。如果在 Panel 一直向右滚动时调用 PerformLayout,它会正确地将控件放置在正确的位置。这需要 OnShown 方法中的一些代码:

Protected Overrides Sub OnLoad(e As EventArgs)
  MyBase.OnLoad(e)
  Panel1.AutoScroll = True
  Panel1.SuspendLayout()
  For i As Integer = 1 To 200
    Dim gb As New GroupBox
    gb.Text = "Box " & i.ToString
    gb.Width = 250
    gb.Dock = DockStyle.Left
    Panel1.Controls.Add(gb)
    gb.BringToFront()
  Next
  Panel1.ResumeLayout(False)
End Sub

Protected Overrides Sub OnShown(e As EventArgs)
  MyBase.OnShown(e)
  Panel1.AutoScrollPosition = New Point(Panel1.HorizontalScroll.Maximum - _
                                        Panel1.HorizontalScroll.LargeChange, 0)

  Panel1.PerformLayout()
  Panel1.AutoScrollPosition = Point.Empty
End Sub

当然,不建议在表单上使用超过 200 个容器控件。

【讨论】:

  • +1 并且很高兴知道。我永远无法弄清楚为什么 Dock 似乎没有按预期工作。
  • 这很有帮助。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-24
  • 1970-01-01
  • 1970-01-01
  • 2015-05-07
  • 1970-01-01
相关资源
最近更新 更多