2012-05-22 103 views
0

我的表单上有两个控件:一个带有工作列表和面板的列表框,它充当显示关于工作细节(卡片)的容器。 当用户点击工人的名字时,我在面板上显示卡片。一张卡片是一个用户控件,带有一些相当简单的用户界面(2个组框,3个文本框和几个标签)和简单的逻辑(设置标签的前景色)。缓慢处理UserControl

卡片在运行时创建。以前的卡片会从面板中移除,新的卡片将被添加 - 每名工人的卡片数量为1到4个。这里变得很有趣。 一切工作正常,直到约。第五次点击工人。似乎GC启动了,旧卡(之前删除)需要约两秒钟(0.3s x先前删除的卡的数量)才能处理并显示新的卡。如果工人之间的移动工作很好,那么在这一点上会变得非常缓慢。 经过一番探索后,我找到了我使用过的控件的方法Dispose。致电base.Dispose()大约需要0.3秒。

这里是我的代码:

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(); 
} 

我试过到目前为止:

  • 藏身卡,而不是处置 - 移动员工之间 当它工作得更快,但点球支付时关闭表格
  • 隐藏卡片,并有一个单独的线程,配置它们 - 没有变化
  • 测试添加10张卡片d立即处理它们 - 慢
  • 测试添加10张卡并立即处理它们在构造函数中 - FAST!
  • 取代的DevExpress与“正常”的控制 - 没有改变
  • 手动处置旧卡而不是改变工人的时候移除它们的 - 工人之间的一举一动变慢:for (var ix = panelControl1.Controls.Count - 1; ix >= 0; --ix) { panelControl1.Controls[ix].Dispose();}
  • 剖析它 - 这就是我是如何发现问题在Dispose。我可以跟踪它归结为Control.DestroyHandle
  • 调用Controls.Clear()在我的控制Dispose方法 - 超奇怪的行为和异常
  • 取消了所有从我的用户控件 - 快一点点,但仍然缓慢
  • 隐藏panelControl1时删除和添加卡 - 没有变化
  • 打开背景GC关闭 - 没有变化
  • 将卡AddRange

由于从构造函数调用时相同的功能运行速度很快,我相信原因必须在(控制)句柄中的某处。

我只是找不到这种奇怪行为的原因。我会很感激任何想法....

更新:当研究GC和控制之间的连接。处置我发现this excellent answer

+0

处置与垃圾收集器没有任何关系。您以非常危险的方式使用Controls.Clear(),它不会处理控件。运行Taskmgr.exe,进程选项卡。查看+选择列并勾选USER对象和GDI对象。确保这些值对于您的应用程序是稳定的,并且不会毫无束缚地攀登。联系devexpress获取更多支持。 –

+0

@HansPassant感谢您的意见和建议。 USER和GDI对象的数量不会改变。 “危险的方式”是什么意思?我只是希望他们从我的容器中移除。 –

回答

0

经过[DevExpress不成功的支持]后,我做了一些测试并玩了代码,并最终找到了解决方案。

诀窍是在处置之前清除UserControl上的控件。

人们可以修改Dispose方法在UC(该溶液在某些情况下工作,但不是在所有的)或隐藏的,而不是除去它形成面板和cleraring其Controls

解决方案1的UC:

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倍,这是不够好。

+0

恐怕这不是一个解决方案...这是危险的方式,因为它有一个控制处理泄漏(你应该总是处置所有未使用的控件)。看看我的答案。我相信它可以帮助。 – DmitryG

0

问题的原因既与DevExpress也不与标准控件相关。但是,它与创建和销毁控制手柄有关。为了改进卡片的实施,尽可能避免这些操作。我建议你使用缓存你的卡:

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

下一步在优化卡中减少使用的控制手柄的总数。由于您使用的是DevExpress控件,因此最适合您的选项是XtraLayoutControl。使用XtraLayoutControl可以显着减少控制手柄的总数。在使用标准控件时,它只为您描述的布局创建4个手柄(3个编辑器在几个组框中标注标签)而不是8个手柄。 XtraLayoutControl不会为编辑器的标签,组和标签创建句柄。 也请看看XtraGrid LayoutView - 它提供了使用网格的数据绑定架构和卡的布局虚拟化没有任何额外的代码的好处..