Winforms用户控件:创建和添加到父级时的性能问题

时间:2010-09-07 15:22:54

标签: winforms performance user-controls devexpress

我已经构建了一个UserControl来显示其他UserControl的列表,这些UserControl本身绑定到各个数据对象。下面的链接显示了此控件的示例实现。

alt text

每个用户行都是自己的UserControl / XtraUserControl,布局在FlowLayoutPanel中。

我遇到的问题是性能,填充上面的列表需要 500ms (不包括任何数据加载) - 这是创建每个控件然后使用它们将它们添加到FlowLayoutPanel的组合AddRange(controls [])方法。

有谁知道我可以在这里提高性能吗?我是否必须手动绘制项目而不是使用用户控件?

提前致谢。

编辑:我在下面添加了自己的回复,显示了我现在坚持使用的解决方案。

4 个答案:

答案 0 :(得分:1)

手动绘画是否有帮助是一种猜测。即使它是对的(我怀疑),最好不要猜测。

我以前见过这种问题,很可能在绑定中有很多东西。

我解决问题的方法是this approach,但在程序员接受方面肯定是“在那里”。

答案 1 :(得分:1)

我猜你正在使用devexpress控件,因为你提到了XtraUserControl。如果是这样,为什么不使用XtraGrid?你可以添加图像列和按钮列,我认为你会获得更好的性能和更简单/更少的代码

答案 2 :(得分:1)

首先,尝试使用SuspendLayout()/ ResumeLayout()对,然后通过隐藏容器控件直到所有子用户控件都添加为止来停止绘画是有意义的。

无论如何,将大量子控件放在容器上并不是一个好主意。 通过使用高度自定义的网格或自定义绘画(最好),您可以获得相同的结果。

祝你好运!

答案 3 :(得分:1)

我有另一种解决方案的脑波,我不太确定是否合适。我真的很感激任何反馈。

两个基本原理导致了这个解决方案:

  • 首先,我希望像任何其他控件一样创建行的灵活性
  • 其次,使用此方法的列表仅打算显示简短的数据块,绝不会超过20项 - 对于任何更大的,使用ListViews。

所以无论如何,我决定要做的是缓存一定数量的Panel(我在整个代码中将自定义控件或行称为Panel),并在创建控件时构建此缓存。使用BusinessObjects填充控件时,将显示现有的缓存面板及其绑定的BusinessObject。您可以从下面的代码中看到它是如何工作的,所以不需要深入的描述。

事实是我已经成功地将数据填充时间(在10个面板的初始缓存设置大约180ms之后)从 500ms 减少到 150ms 对于上图中显示的列表。

private int cacheSize = 10;
private List<P> cachedPanels = new List<P>(10);
private void InitItems()
{
    this.contentPanel.SuspendLayout();

    // Create the cached panels from the default cache value.
    for (int i = 0; i < cacheSize; i++)
        cachedPanels.Add(new P() { Margin = new Padding(0), Visible = false });

    this.contentPanel.Controls.AddRange(cachedPanels.ToArray());

    this.contentPanel.ResumeLayout(true);
}

private void PopulateListFromCache()
{
    this.contentPanel.SuspendLayout();

    // Iterate against both BusinessObjects and Panels to ensure that nothing is missed, for
    // instance, where there are too many panels, the rest are hidden, and too many Business
    // Objects, then more Panels are created.
    for (int i = 0; i < this.businessObjects.Count || i < this.cachedPanels.Count; i++)
    {
        if (i >= this.cachedPanels.Count)
        {
            // Here, we have more BusinessObjects than Panels, thus we must create
            // and assign a new panel.
            this.cachedPanels.Add(new P() { Margin = new Padding(0) });
            this.cachedPanels[i].Item = this.businessObjects[i];
            this.contentPanel.Controls.Add(this.cachedPanels[i]);
        }
        else if (i >= this.businessObjects.Count)
        {
            // Here, we still have Panels cached but have run out of BusinessObjects,
            // let's just hide them and clear their bindings.
            this.cachedPanels[i].Item = default(T);
            this.cachedPanels[i].Visible = false;
        }
        else
        {
            // Here, we have both BusinessObjects and Panels to put them in, so just
            // update the binding and ensure the Panel is visible.
            this.cachedPanels[i].Item = this.businessObjects[i];
            this.cachedPanels[i].Visible = true;
        }
    }

    this.contentPanel.ResumeLayout(true);
}

显然,可以进行更多的优化,例如在一段时间不使用之后取消缓存Panel。另外,我不完全确定是否在缓存中保留这些控制 - 这很简单会影响内存使用量。

如果有人能想到任何其他指示,那么请成为我的客人。哦,如果你走得这么远,那么谢谢你阅读本文。