【问题标题】:How many controls can I create and show in a Windows Forms form?我可以在 Windows 窗体窗体中创建和显示多少个控件?
【发布时间】:2018-10-23 08:28:03
【问题描述】:

我正在开发一个 C# Windows 窗体应用程序。我知道控件过多会带来糟糕的用户体验,我会同时保持控件的可见性很小(例如,如 Upper (reasonable) limit to number of user control instances 中所建议的那样),但某些控件可能被隐藏(即使用选项卡或图层)。

我可以在表单上显示的控件的绝对最大数量是多少,如果有,它是多少?

【问题讨论】:

  • 任何你喜欢的数字,如果你想动态生成的话。
  • 嗨,这不是一个非常明确的问题......或者它只是奇怪的简单。适合表格是什么意思?简单的答案是这取决于您的表单有多大以及控件有多大?那是假设您不重叠...但是您在说什么?当然,这是一个几何问题而不是编程问题
  • 正是你的应用程序的许多用户可以处理而不会发疯。
  • 如果你遇到了需要问这个问题的情况,那就说明你做错了。
  • 如果你必须这么问,那你就做错了。无论如何,没有硬性限制。你为什么想知道这个?例如,另请参阅Upper (reasonable) limit to number of user control instances。你试过什么?

标签: c# winforms


【解决方案1】:

实际上,有一个限制,虽然不是硬编码和可配置的 - 其默认值为 10,000 (why?)。

每个控件都在操作系统中创建一个user-object,Windows 中每个进程的默认最大活动用户对象数为 10,000 - 因此,一旦您尝试将 10,001 控件添加到表单中,您应该会得到一个带有消息的System.ComponentModel.Win32Exception 类型的异常:

创建窗口句柄时出错。

当然,没有用户会希望看到包含 10,000 个控件的表单,因此除非您在某处有泄漏,否则永远不会发生这种情况。 (当然,我知道它的唯一原因仅仅是因为我过去曾发生过这种泄漏 - 我让用户控件监听来自静态类的事件并且没有在 Dispose 方法中解开它们,所以即使他们从屏幕上清除后,他们仍然活着......)

当您遇到此异常时,请查看任务管理器的“进程”选项卡。 点击 View 菜单,在里面点击 Select Columns,标记 USER Objects 的复选框(默认情况下它是不可见的;这个事实可能花费了我尝试了解我的泄漏时的几个小时)-然后按该列排序。如果您在顶部看到您的应用程序,有 10,000 个用户对象 - 那么您知道您的应用程序已达到控件的最大数量 - 这意味着您需要修复泄漏。

请注意,即使您从表单中删除控件,如果它们有其他引用,它们也不会被释放,并且如果您的应用程序运行足够的时间,您最终会收到此错误。

如果有人感兴趣,这里是我用来重新创建错误的代码(包括设计器代码)

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace UserObjectsLeak
{
    public partial class FrmUserObjectsLeak : Form
    {
        // This is used to keep references of the labels being added dynamically.
        static readonly List<Label> Labels = new List<Label>();

        public FrmUserObjectsLeak()
        {
            InitializeComponent();
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            for (var i = 0; i < 11000; i++)
            {
                var label = new Label()
                {
                    Text = i.ToString(),
                    Width = 50
                };
                Labels.Add(label);
                try
                {
                    panel1.Controls.Add(label);
                }
                catch (System.ComponentModel.Win32Exception ex)
                {
                    lblException.Text = ex.ToString();
                    return;
                }

                lblControlsCount.Text = (i).ToString();

                // Quick and dirty just to show the progress...
                Application.DoEvents();

                if (i % 500 == 0)
                {
                    // Remove all labels from the panel,
                    // keep only the reference in the list.
                    panel1.Controls.Clear();
                }
            }
        }

        private void btnClear_Click(object sender, EventArgs e)
        {
            panel1.Controls.Clear();
            Labels.Clear();
            lblControlsCount.Text = "";
            lblException.Text = "";
        }

        #region Designer code

        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.label1 = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            this.btnStart = new System.Windows.Forms.Button();
            this.lblControlsCount = new System.Windows.Forms.Label();
            this.btnClear = new System.Windows.Forms.Button();
            this.panel1 = new System.Windows.Forms.FlowLayoutPanel();
            this.lblException = new System.Windows.Forms.Label();
            this.SuspendLayout();
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(15, 17);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(191, 13);
            this.label1.TabIndex = 0;
            this.label1.Text = "Click the button to start adding controls";
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(12, 95);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(77, 13);
            this.label2.TabIndex = 1;
            this.label2.Text = "controls count:";
            // 
            // btnStart
            // 
            this.btnStart.Location = new System.Drawing.Point(12, 49);
            this.btnStart.Name = "btnStart";
            this.btnStart.Size = new System.Drawing.Size(75, 23);
            this.btnStart.TabIndex = 2;
            this.btnStart.Text = "Start";
            this.btnStart.UseVisualStyleBackColor = true;
            this.btnStart.Click += new System.EventHandler(this.btnStart_Click);
            // 
            // lblControlsCount
            // 
            this.lblControlsCount.AutoSize = true;
            this.lblControlsCount.Location = new System.Drawing.Point(95, 95);
            this.lblControlsCount.Name = "lblControlsCount";
            this.lblControlsCount.Size = new System.Drawing.Size(0, 13);
            this.lblControlsCount.TabIndex = 3;
            // 
            // btnClear
            // 
            this.btnClear.Location = new System.Drawing.Point(98, 49);
            this.btnClear.Name = "btnClear";
            this.btnClear.Size = new System.Drawing.Size(75, 23);
            this.btnClear.TabIndex = 5;
            this.btnClear.Text = "Clear";
            this.btnClear.UseVisualStyleBackColor = true;
            this.btnClear.Click += new System.EventHandler(this.btnClear_Click);
            // 
            // panel1
            // 
            this.panel1.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
            this.panel1.Location = new System.Drawing.Point(226, 17);
            this.panel1.Name = "panel1";
            this.panel1.Size = new System.Drawing.Size(200, 148);
            this.panel1.TabIndex = 6;
            // 
            // lblException
            // 
            this.lblException.AutoSize = true;
            this.lblException.Location = new System.Drawing.Point(15, 179);
            this.lblException.Name = "lblException";
            this.lblException.Size = new System.Drawing.Size(0, 13);
            this.lblException.TabIndex = 7;
            // 
            // frmUserObjectsLeak
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(452, 308);
            this.Controls.Add(this.lblException);
            this.Controls.Add(this.panel1);
            this.Controls.Add(this.btnClear);
            this.Controls.Add(this.lblControlsCount);
            this.Controls.Add(this.btnStart);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.label1);
            this.Name = "FrmUserObjectsLeak";
            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
            this.Text = "User Objects Leak Demo";
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.Button btnStart;
        private System.Windows.Forms.Label lblControlsCount;
        private System.Windows.Forms.Button btnClear;
        private System.Windows.Forms.FlowLayoutPanel panel1;
        private System.Windows.Forms.Label lblException;

        #endregion Designer code
    }
}

【讨论】:

  • 很高兴看到积极响应。人们在这方面做得太过分了。这个问题是一个合理的问题。如果有人不熟悉动态创建组件,他们可能很难快速证明这一点。他们可能希望构建一个包含许多组件的大型表单,但在完成之前不知道它们是否会达到硬限制。
  • @Creyke 我花了大约两天的时间才找到问题所在,又花了半天时间弄清楚它发生的原因以及如何解决它。如果我能在那个时候拯救另一个程序员,那么这个答案值得我投入时间,问题也是如此。
  • 错误信息是什么? (如果不是很明显,在此处引用该消息将有助于其他用户更轻松地找到答案。)
  • @user202729 我希望我能记住它。这发生在大约 3 年前...我将尝试在我的计算机上重新创建这种情况以进行测试。
  • @user202729 感谢您的评论。我已经重新创建了错误并更新了我的答案。
【解决方案2】:

其实如果从表单构造函数动态添加没有限制,为了实验,我添加了 10 万个标签和 1 万个带颜色的面板,尽管显示需要超过 15 分钟(pc:8 ram,SixCore 3.50 GHz cpu)之后,表单显示没有性能问题(它消耗了 30mb 的 ram 内存),表单的拖动也没有受到影响。

但是,正如前面的评论(#Zohard Peled)所说,如果表单已经显示,则只能添加 10,000 个控件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-10
    • 1970-01-01
    • 2013-11-04
    • 1970-01-01
    相关资源
    最近更新 更多