【问题标题】:How can a scrollable area retrieve the size of what is being displayed?可滚动区域如何检索正在显示的内容的大小?
【发布时间】:2014-12-31 18:59:59
【问题描述】:

在我的应用程序中,我遇到了以下情况:

我有一个带有多个选项卡的选项卡控件的 Windows 窗体。每个选项卡都包含其他类在启动或运行时添加的任意内容。

我想设置选项卡,一旦表单太小,选项卡的面板无法显示所有内容,滚动条就会自动出现。

到目前为止,我尝试的是设置标签页的AutoScroll = true 并将AutoScrollMinSize 属性设置为面板的大小。

这没有按预期工作,因为面板的 Size 似乎总是 (200, 100) 独立于其内容。

我创建了一个小示例应用程序(下面的代码)来演示该问题。如果您调整表单的大小,您会看到只有当表单小于面板(默认大小为 (200, 100))而不是面板中的文本框(大小为 300, 150)时才会出现滚动条。如果您手动设置AutoScrollMinSize(取消注释第 34 行),它的行为与预期一样。

问题是:标签页如何获取其中显示的实际大小?

我可能会递归所有控件并尝试自己计算大小 - 但这感觉真的很糟糕。

PS:请不要建议将面板的大小设置为标签的大小,因为实际的面板比这要复杂得多。 ;-)


代码:

只需在 Visual Studio 中创建一个应用程序并使用以下代码覆盖 Program.cs:

using System;
using System.Windows.Forms;

namespace ScrollbarTest
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            var sampleForm = CreateSampleForm();

            Application.Run(sampleForm);
        }

        private static Form CreateSampleForm()
        {
            var sampleForm = new Form() { };
            var tabControl = new TabControl() { Dock = DockStyle.Fill };
            var tabPage = new TabPage("Test") { AutoScroll = true };
            sampleForm.Controls.Add(tabControl);
            tabControl.TabPages.Add(tabPage);

            var samplePanel = CreateSamplePanel();
            tabPage.Controls.Add(samplePanel);

            // this does not provide the right size
            tabPage.AutoScrollMinSize = samplePanel.Size;

            // uncomment this to make it work
            //tabPage.AutoScrollMinSize = new System.Drawing.Size(300, 150);

            return sampleForm;
        }

        private static Control CreateSamplePanel()
        {
            // As an example, create a panel with a text box with a fixed size.
            var samplePanel = new Panel() { Dock = DockStyle.Fill };
            var sampleSize = new System.Drawing.Size(300, 150);
            var textBox = new TextBox() 
            { 
                Dock = DockStyle.Fill,
                MinimumSize = sampleSize,
                MaximumSize = sampleSize,
                Size = sampleSize
            };
            samplePanel.Controls.Add(textBox);

            return samplePanel;
        }
    }
}

【问题讨论】:

  • 标签页不知道面板包含。你为什么不在面板上设置滚动条呢。
  • 在这种情况下,我会在面板上遇到同样的问题。面板通常由一个 TableLayoutPanel 和几个子面板和几个控件组成。

标签: c# winforms scroll


【解决方案1】:

samplePanel.Size 返回 (200,100)。在您的CreateSamplePanel 方法中,如果您设置samplePanel.MinimumSize = sampleSize;,那么您的代码将起作用。

面板不会根据其子控件计算其大小属性(例如SizeMinimumSizePreferredSize)。您必须继承 Panel 并提供该行为。即使TableLayoutPanelFlowLayoutPanel 也无法正确计算PreferredSize 属性,这令人惊讶。至少,您通常会覆盖GetPreferredSize(Size proposedSize) 方法,并可选择让MinimumSize 属性返回PreferredSize 属性。

值得注意的是DockStyle.FillMinimumSize 彼此不一致。 TabPage 控件本质上是DockStyle.Fill 模式,这就是为什么你必须设置AutoScrollMinSize 属性。

编辑:是否没有任何现有函数可以检索控件列表(递归)所需的总大小,例如通过他们的 X/Y 和大小?

这取决于宿主容器本身(例如TableLayoutPanel)来正确计算其PreferredSize,因为只有它知道如何执行其布局的确切细节。

您可以将AutoSize 属性设置为true,然后希望GetPreferredSize(...)/PreferredSize 计算正确的大小。对于TableLayoutPanel,我记得有一个案例计算不正确,我不得不对其进行子类化并覆盖GetPreferredSize(...) 方法。除非AutoSize 为真,否则不会调用GetPreferredSize(...)

如果您谈论的是普通的PanelUserControl,默认情况下这些使用所见即所得的LayoutEngine,并且不计算PreferredSize。您可以子类化,然后计算最大 control.X + control.Width 和相同的高度,并将其用作首选尺寸。

首先尝试将AutoSize 设置为true,看看是否适合您。如果没有,您可能必须重写 GetPreferredSize(...) 方法。这是一个粗略的例子:

   [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        var sampleForm = new Form() { AutoScroll = true };

        var panel = new MyPanel() { AutoSize = true, AutoSizeMode = AutoSizeMode.GrowAndShrink, BackColor = Color.LightYellow };

        for (int i = 0; i < 6; i++) {
            for (int j = 0; j < 3; j++) {
                Button b = new Button { Text = "Button" + panel.Controls.Count, AutoSize = true };
                b.Click += delegate {
                    MessageBox.Show("Preferred Size: " + panel.PreferredSize);
                };
                panel.Controls.Add(b, j, i);
            }
        }

        sampleForm.Controls.Add(panel);
        Application.Run(sampleForm);
    }

    private class MyPanel : TableLayoutPanel {
        public override Size MinimumSize {
            get {
                return PreferredSize;
            }
            set {

            }
        }

        public override Size GetPreferredSize(Size proposedSize) {
            Size s = new Size();
            int[] harr = new int[100];//this.RowCount];
            int[] warr = new int[100];//this.ColumnCount];
            foreach (Control c in this.Controls) {
                var cell = this.GetPositionFromControl(c);
                var ps = c.PreferredSize;
                Padding m = c.Margin;
                int w = ps.Width + m.Horizontal;
                int h = ps.Height + m.Vertical;
                if (w > warr[cell.Column])
                    warr[cell.Column] = w;
                if (h > harr[cell.Row])
                    harr[cell.Row] = h;
            }

            foreach (int w in warr)
                s.Width += w;
            foreach (int h in harr)
                s.Height += h;

            return s;
        }
    }

【讨论】:

  • 事情在我的实际应用程序中,我没有sampleSize。是否没有任何现有函数可以检索控件列表的总所需大小(递归),例如通过他们的 X/Y 和大小?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-13
  • 1970-01-01
  • 1970-01-01
  • 2020-05-27
  • 2014-03-29
  • 2016-01-24
  • 2017-08-30
相关资源
最近更新 更多