我可以在 Windows 窗体窗体中创建和显示多少个控件? [英] How many controls can I create and show in a Windows Forms form?

查看:36
本文介绍了我可以在 Windows 窗体窗体中创建和显示多少个控件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发 C# Windows 窗体应用程序.我知道太多的控件会导致糟糕的用户体验,我会同时保持可见的控件数量很少(例如,如 用户控件实例数量的上限(合理)限制),但某些控件可能被隐藏(即使用选项卡或图层).

我可以在表单上显示的控件的绝对最大数量是否有限制,如果有,是什么?

解决方案

实际上是有限制的,虽然不是硬编码和可配置的 - 它的默认值是 10,000 (为什么?).

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

创建窗口句柄时出错.

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

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

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

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

使用系统;使用 System.Collections.Generic;使用 System.Windows.Forms;命名空间 UserObjectsLeak{公共部分类 FrmUserObjectsLeak :表单{//这用于保持动态添加的标签的引用.静态只读列表<标签>标签 = 新列表<标签>();公共 FrmUserObjectsLeak(){初始化组件();}private void btnStart_Click(object sender, EventArgs e){for (var i = 0; i <11000; i++){var 标签 = 新标签(){Text = i.ToString(),宽度 = 50};Labels.Add(label);尝试{panel1.Controls.Add(label);}catch (System.ComponentModel.Win32Exception ex){lblException.Text = ex.ToString();返回;}lblControlsCount.Text = (i).ToString();//快速而肮脏只是为了显示进度...Application.DoEvents();如果(我%500 == 0){//从面板中删除所有标签,//只保留列表中的引用.panel1.Controls.Clear();}}}private void btnClear_Click(object sender, EventArgs e){panel1.Controls.Clear();标签.清除();lblControlsCount.Text = "";lblException.Text = "";}#region 设计器代码///<总结>///必需的设计器变量.///</总结>私有 System.ComponentModel.IContainer 组件 = null;///<总结>///清理任何正在使用的资源.///</总结>/// true 如果托管资源应该被释放;否则,为假.</param>受保护的覆盖无效处置(布尔处置){if (处理 && (components != null)){components.Dispose();}base.Dispose(处置);}#region Windows 窗体设计器生成的代码///<总结>///设计器支持所需的方法 - 不要修改///此方法的内容与代码编辑器.///</总结>私有无效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();////标签 1//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 = "点击按钮开始添加控件";////标签 2//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 = "控件计数:";////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 = "开始";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;////btn清除//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 = "清除";this.btnClear.UseVisualStyleBackColor = true;this.btnClear.Click += new System.EventHandler(this.btnClear_Click);////面板1//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;////lbl异常//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 = "用户对象泄露演示";this.ResumeLayout(false);this.PerformLayout();}#endregion私有 System.Windows.Forms.Label label1;私有 System.Windows.Forms.Label label2;私有 System.Windows.Forms.Button btnStart;私有 System.Windows.Forms.Label lblControlsCount;私有 System.Windows.Forms.Button btnClear;私有 System.Windows.Forms.FlowLayoutPanel panel1;私有 System.Windows.Forms.Label lblException;#endregion 设计器代码}}

I'm developing a C# Windows Forms application. I know that too many controls makes a terrible user experience, and I would keep the number of controls visible at the same time small (for example, as suggested in Upper (reasonable) limit to number of user control instances), but some of controls may be hidden (i.e. using tabs or layers).

Is there an absolute maximum number of controls I can show on a form, and if there is one, what is it?

解决方案

Actually, there is a limit, though not hard coded and configurable - and its default value is 10,000 (why?).

Each control is creating a user-object in the operating system, and the default maximum number of active user-objects per process in Windows is 10,000 - so once you will try to add the 10,001 control to the form you should get an exception of type System.ComponentModel.Win32Exception with the message:

Error creating window handle.

Of course, no user would want to see a form with 10,000 controls, so unless you have a leak somewhere, this should never happen. (And of course, the only reason I know about it is just because I've had this leak in the past - I've had user controls listening to events from a static class and didn't unhook them in the Dispose method, so even after they where cleared from the screen, they were still alive...)

When you get this exception, look at the Processes tab of Task manager. Click the View menu, inside click Select Columns, mark the checkbox of USER Objects (it's not visible by default; that fact probably cost me a couple of hours when attempting to understand my leak) - and then sort by that column. If you see your application at the top, with 10,000 user objects - then you know your application have reached the maximum number of controls - which means you have a leak you need to fix.

Please note that even if you remove the controls from the form, if they have some other reference they will not get disposed and you will, eventually, get this error if your application is running enough time.

If anyone is interested, here is the code I've used to re-create the error (including designer code)

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
    }
}

这篇关于我可以在 Windows 窗体窗体中创建和显示多少个控件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆