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.
Here’s my code:
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();
}
What I've tried so far:
- 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 toControl.DestroyHandle
- calling
Controls.Clear()
inDispose
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
Since the same functionality works fast when called from the constructor, I belive the reason must be somewhere in the (control) handles.
I just can't find the reason for this strange behaviour. I would appreciate any ideas....
UPDATE: While researching connection between GC and Control.Dispose I found this excellent answer