English 中文(简体)
慢处理用户控制
原标题:Slow dispose of UserControl

I have two controls on my form: a listbox with a list of workers and a panel which acts as a container for showing the details (cards) about their work. When a user clicks on the worker s name, I display cards on the panel. A card is a usercontrol with some fairly simple UI (2 groupboxes, 3 textboxes and several labels) and simple logic (setting forecolor of labels).

The cards are created in runtime. Previous cards are removed from the panel and the new ones get added - the number of cards per worker is 1 to 4. It gets interesting here. Everything works fine until approx. the fifth click on the workers. It seems that GC kicks in and it takes about two seconds (0.3s x number of previously removed cards) for the old cards (previously removed) to get disposed and new ones shown. If moving between workers works great before, it gets painfully slow at that point. After some exploring I ve located the problem to lay in Dispose method of my usedcontrol. Call base.Dispose() takes about 0.3s.

以下是我的代码:

private void ShowCards(List<Work> workItems) {
  var y = 5;
  panelControl1.SuspendLayout();
  panelControl1.Controls.Clear();

  foreach (var work in workItems) {
    var card = new Components.WorkDisplayControl(work);
    card.Top = y;
    card.Left = 10;

    y += card.Height + 5;

    panelControl1.Controls.Add(card);
  }

  panelControl1.ResumeLayout(true);
  Application.DoEvents();
}

到目前为止,我曾尝试过:

  • hiding cards instead of disposing - it works faster when moving between workers, but the penalty is paid when closing the form
  • hiding the cards and have a separate thread which disposes them - no change
  • test with adding 10 cards and disposing them immediately - slow
  • test with adding 10 cards and disposing them immediately in the constructor - FAST!
  • replaced DevExpress controls with “normal” – no change
  • manually disposing old cards instead of removing them when changing worker - every move between workers gets slower: for (var ix = panelControl1.Controls.Count - 1; ix >= 0; --ix) { panelControl1.Controls[ix].Dispose();}
  • profiling it - that s how I ve found the problem in Dispose. I can trace it down to Control.DestroyHandle
  • calling Controls.Clear() in Dispose method of my control - super strange behaviour and exceptions
  • removed all of the controls from my usercontrol - a little bit faster, but still slow
  • hiding panelControl1 when removing and adding cards - no change
  • turned background GC off - no change
  • adding cards with AddRange

由于同一功能在从构建器中调用时快速运作,我确信原因一定是在(控制)把手的某处。

我只是找不到这种奇怪行为的原因 我非常感激...

更新: 在研究GC与控制之间的连接时。 丢弃我发现 < a href=> https:// stackoverflow. com/ a/ 9432317/820604> 这个极好的答复

最佳回答

在[Develop Express的不成功支持]之后, 我做了一些测试 和玩弄代码 最后找到了解决方案。

诀窍是在处置前清除 用户控制 的控制 。

人们可以修改UC上 dispose 的方法(这一解决方案在某些情况下有效,但在所有情况下都行不通),或者隐藏UC,而不是将其除去,并加速其 controls

<强力 > 解决方案1:

protected override void Dispose(bool disposing) {
  if (disposing && (components != null)) {
    components.Dispose();
  }

  Controls.Clear(); // <--- Add this line

  base.Dispose(disposing); 
  }

<强力 > 解决方案2:

在UC中添加新方法 :

public void ClearControls() {
  Controls.Clear();
}

在我的原问题中 替换此行

panelControl1.Controls.Clear();

与此:

for (var ii = panelControl1.Controls.Count - 1; ii >= 0; --ii) { 
  var wdc = panelControl1.Controls[ii] as Components.WorkDisplayControl;
  wdc.Visible = false;
  wdc.ClearControls();
}

速度(至少)是速度的20倍,足够好。

问题回答

问题的原因既不与设计器和标准控制有关,也不与设计器和标准控制有关。 但是,它与创建和销毁控制控控控控控控控器有关。 为了改进您的卡的操作, 请在可能时避免这些操作 。 我建议您使用卡片的缓存 :

void ShowCards(List<Work> workItems) {
    cardsPanel.SuspendLayout();
    CacheCards(cardsPanel.Controls);
    int y = 5;
    foreach(var work in workItems) {
        var card = GetCardFromCache(work);
        card.Top = y;
        card.Left = 10;
        y += card.Height + 5;
        cardsPanel.Controls.Add(card);
    }
    cardsPanel.ResumeLayout(true);
}
//
Stack<WorkDisplayControl> cache;
void CacheCards(Control.ControlCollection controls) {
    if(cache == null)
        cache = new Stack<WorkDisplayControl>();
    foreach(WorkDisplayControl wdc in controls)
        cache.Push(wdc);
    controls.Clear();
}
WorkDisplayControl GetCardFromCache(Work data) {
    WorkDisplayControl result = (cache.Count > 0) ?
        cache.Pop() : new WorkDisplayControl();
    result.InitData(data);
    return result;
}

The next step in optimizing cards is reducing the total number of used control handles. Since you are using DevExpress controls, the best option for you is XtraLayoutControl. The use of XtraLayoutControl allows you to significantly reduce the total number of control handles. It only creates 4 handles for the layout you ve described (3 editors with labels within a couple of group boxes) instead of 8 handles when the standard controls are used. XtraLayoutControl does not create handles for an editor s labels, groups, tabs. Please also take a look at XtraGrid LayoutView - it provides benefits of using the grid s databinding architecture and cards layout virtualization without any additional coding..





相关问题
Bring window to foreground after Mutex fails

I was wondering if someone can tell me what would be the best way to bring my application to the foreground if a mutex was not able to be created for a new instance. E.g.: Application X is running ...

How to start WinForm app minimized to tray?

I ve successfully created an app that minimizes to the tray using a NotifyIcon. When the form is manually closed it is successfully hidden from the desktop, taskbar, and alt-tab. The problem occurs ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

Handle DataTable.DataRow cell change event

I have a DataTable that has several DataColumns and DataRow. Now i would like to handle an event when cell of this DataRow is changed. How to do this in c#?

Apparent Memory Leak in DataGridView

How do you force a DataGridView to release its reference to a bound DataSet? We have a rather large dataset being displayed in a DataGridView and noticed that resources were not being freed after the ...

ALT Key Shortcuts Hidden

I am using VS2008 and creating forms. By default, the underscore of the character in a textbox when using an ampersand is not shown when I run the application. ex. "&Goto Here" is not ...

WPF-XAML window in Winforms Application

I have a Winforms application coded in VS C# 2008 and want to insert a WPF window into the window pane of Winforms application. Could you explain me how this is done.