如何在不使程序崩溃的情况下添加1000多个动态控件

时间:2018-06-19 12:39:59

标签: c# dynamic controls

自从我向我解释了一个XY问题后,让我改写一下。

我正在对可能有1到1000个客户端或更多客户端的远程进程进行启动,我想要一个表单/面板/列表视图(无关紧要),该表单/面板/列表视图将向我显示他们在一个位置的个人进度。 >

为每个单独创建一个单独的ProgressBar的简短操作可能会导致崩溃或性能问题,那么解决此问题的最佳方法是什么?

目前,我发现,如果将进度条放置在列表视图中,则在创建数百甚至1000个进度条时不会崩溃,但是由于明显的原因,性能会很慢。

我还有其他可以使用的解决方案,但是我正在寻找什么是最好的方式来获得我想要的东西,而又不会增加系统负担。

我想到的最好的方法是让我使用DirectX创建进度条对象,然后计算该对象,然后将其作为一个图像渲染到显示器上,就像在视频游戏中处理1000个对象一样。

我很好奇,是否有一种真正的好方法可以使用内置组件来完成此操作,而无需求助于DirectX。

我把旧文本留在这里供参考。

上一个问题: 我正在尝试为从ListView中选择的每个项目的面板添加一个控件,在这种情况下,是一个Label。

如果我选择了120多个左右的Items,它将使我的程序崩溃。因此,我测试了一个添加标签的简单循环,它也使程序崩溃。

System.ComponentModel.Win32Exception:'创建窗口句柄时出错。'

显然我用完了100-200个标签之间的句柄。看来,如果我缓慢地添加它们,一次用控制键在列表中选择它们一次,我就不会有这个问题。

虽然重画很慢,但少于该数量时效果很好。

有没有一种方法可以动态添加1000个或更多控件而不会导致程序崩溃?还是我必须借助DirectX并创建自己的图形来完成此任务?

编辑(根据社区的请求):

关于我要完成的工作的背景知识。

我有一个客户端工作站的列表,当我从列表中选择一个或一组或一组时,我希望在面板上为每个工作站显示一个具有工作站名称的图块以及用于执行操作的进度条。

    private void listView1_SelectedIndexChanged(object sender, EventArgs e)
    {
        panel1.Controls.Clear();

        int cNumber = listView1.SelectedItems.Count;
        int MaxColumns = panel1.Width / 100;
        int Row = 0;

        Label[] lb = new Label[cNumber];

        for (int i = 0; i < cNumber; i++)
        {
            // Initialize one variable 

            lb[i] = new Label();
            // Set location
            if (i - (MaxColumns * Row) >= MaxColumns) Row++;
            lb[i].Location = new Point((i - (MaxColumns * Row)) * 105, Row * 110);


            lb[i].Text = i.ToString();
            // Add to the panel
            panel1.Controls.Add(lb[i]);
        }
    }

2 个答案:

答案 0 :(得分:3)

好吧,这是一个有趣的问题,前一段时间我也很困扰(我可以在单个表单中添加多少个控件?)。因此,这是检查的好理由。我创建了一个新的WinForms项目,并为此修改了表单代码:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        for (int i = 0; i < 10000; i++)
            Controls.Add(new Label { Text = $"{i}" });
    }
}

在运行时(冻结大约1分钟后),应用会抛出一个带有{strong> NativeErrorCode 1158的Win32Exception。您可以看到here,这是 ERROR_NO_MORE_USER_HANDLES 。快速搜索该错误,将我指向this link

您可以找到每个会话65536个用户对象的“理论限制”。

  

但是,每个会话可以打开的最大用户句柄数通常较少,因为它受可用内存的影响。用户句柄还具有默认的按进程限制。

如果您在注册表中查找 HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Windows \ USERProcessHandleQuota ,则会发现用户句柄的实际限制。对于我的Win7,它是10000。

--- 更新 ---

有一个GetGuiResources function可以帮助我们计算当前手柄的使用情况。像这样:

[DllImport("user32.dll")]
static extern uint GetGuiResources(IntPtr hProcess, uint uiFlags);

如果uiFlags为0( GR_GDIOBJECTS ),它将返回GDI对象计数,如果1( GR_USEROBJECTS ),它将返回用户对象计数。

>

我添加了一个按钮,单击后会向父窗体添加1000个标签,并添加一个计时器,对滴答计数GDI和用户对象计数并将结果设置为窗口标题。

因此,刚启动后,就有 36 个GDI对象和 35 个用户对象。然后在每个按钮上单击“我正好有 1000 个附加用户对象和 0 个附加GDI对象。点击几下后,我们达到了用户对象数上限(10000),但GDI对象数仍为36。

因此,我可以说,在具有默认Windows 7设置的表单上,我最多可以拥有〜9970 个控件:)

答案 1 :(得分:0)

午餐时我想到了另一个可能的解决方法。

仅加载面板可以显示的内容,假设有30个客户端,并且不进行滚动,而是使用上下滚动按钮来更改客户端名称文本和进度栏值,以与下一组30个客户端向上或向下匹配。列表。

不知道为什么我以前不这么想。可能是因为滚动浏览1000个客户端的列表将花费很长时间,但是我可以添加我认为的过滤器和搜索功能。

真正的问题在于SelectedIndexChanged函数,一旦将触发器更改为实际的按钮单击,它就可以正常工作,并且我测试了1000个进度条,它可以在大约5秒钟内添加。

我仍然承认,这只是我提出的几种方法中的一种,但是除了TaW提到的类似于分页然后滚动的内容外,没有听到任何其他更好的方法来实现我的目标就像我上面所做的一样。