c# - 我可以在 Windows 窗体窗体中创建和显示多少个控件?
问题描述
我正在开发一个 C# Windows 窗体应用程序。我知道太多的控件会导致糟糕的用户体验,我会同时保持控件的数量很小(例如,如Upper (reasonable) limit to number of user control instances中所建议的),但是一些控件可能被隐藏(即使用选项卡或图层)。
我可以在表单上显示的控件的绝对最大数量,如果有,它是什么?
解决方案
实际上,有一个限制,虽然不是硬编码和可配置的 - 它的默认值为 10,000(为什么?)。
每个控件都在操作系统中创建一个用户对象,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
}
}
推荐阅读
- c++ - 如何在c ++中使用cin获取任意数量的数字输入
- excel - 为什么此代码显示错误 -“运行时错误 13 类型不匹配”?
- postgresql - 有没有办法从 PostgreSQL 数据库中恢复已删除的记录
- windows - Windows 10 - 如何在远程桌面实例关闭时执行 ps1 文件
- pytorch - 使用 BatchNorm1d 时 running_mean 应包含 %% 元素而不是 **
- list - 从多个列表(数字)的元素制作列表
- c# - 无法覆盖 XML 配置设置:ConfigurationErrorsException: Invalid key value
- javascript - 网页抓取 Javascript 元素,最好不使用 Selenium
- enterprise-architect - 如何以编程方式获取 RTF 模板中调用的片段?
- scala - 如何使用由键值对组成的字符串创建 DataFrame?