I have an application I am making that creates a large number of windows controls (buttons and labels etc). They are all being made dynamically through functions. The problem I'm having is, when I remove the controls and dispose them, they are not removed from memory.
void loadALoadOfStuff()
{
while(tabControlToClear.Controls.Count > 0)
tabControlToClear.Controls[0].Dispose();
//I even put in:
GC.Collect();
GC.WaitForPendingFinalizers();
foreach(String pagename in globalList)
tabControlToClear.Controls.Add(MakeATab(pagename));
}
TabPage MakeATab(string tabText)
{
TabPage newT = new MakeATab();
newT.Text = tabText;
//Fill page with controls with methods like this
return newT;
}
Now for some reason, this just ain't giving me my memory back, so when the process runs 5 times, I end up with an out of memory violation. I am new to object and control disposal but looking through the vast net still hasn't given me any indications, so if any of you have an idea, I'd be grateful to hear it.
UPDATE: I've been watching the user objects creation and destruction (taskmanager) and noticed I create a tab page, add a click handler, add a panel, add 2 buttons both with click handlers, tooltips and backimages (I think this is where the problem is). The app says it creates 8 new items, but when I run through my dispose, I only remove 4 from memory. I've been trying to remove the event handlers, but it seems to make no difference.
RESOLVED!!! As I was adding new items to the panel, I was passing them a tooltip (stupid, but I'm learning). For anyone else who has the same problem, (thanks to comments and directions from people below. I discovered in order to make a control truly dispose (as I realise I so incorrectly put it) is:
1: IF YOU HAVE A TOOL TIP, MAKE SURE IT'S ACCESSABLE! DO NOT DO WHAT I DID! E.g:
This is WRONG!
TabPage MakeATab(string tabText)
{
TabPage newT = new MakeATab();
ToolTip myTip = new ToolTip();
newT.Text = tabText;
//Fill page with controls with methods like this
myTip.SetToolTip(newT, "Something to say");
return newT;
}
If you do this, you will lose the pointer to the tooltip, and as the tooltip is not a child of the object it's connected to (rather, the tooltip makes a strong reference to the control), then even if you destroy the control, the tooltip that you can't access keeps the object alive.
2: Before anything, call toolTip.RemoveAll(). This removes all it's ties to controls. Note, if you were using this tip for other controls, they just lost their tool tip.
3: Remove any internal controls from the base control.ControlCollection (if they use non managed memory, I guess. I'm doing it cause it's making my app work so...)
4: remove any custom event handlers.
5: finally, dispose the object. I made a quick recursing function that does it quite well.
private void RecursiveDispose(Control toDispose)
{
while (toDispose.Controls.Count > 0)
RecursiveDispose(toDispose.Controls[0]);
if (toDispose.BackgroundImage != null)
BackgroundImage = null;
if (toDispose.GetType() == typeof(Button))
toDispose.Click -= [Your Event];
else if (toDispose.GetType() == typeof(TabPage))
toDispose.DoubleClick -= [Your Event];
else if (toDispose.GetType() == typeof(Label))
toDispose.MouseMove -= [Your Event];
toDispose.Dispose();
}
This is extremely crude and there's probably a much better way of doing it, but unless someone can come up with it, this will have to do. Thanks for your help everyone. You might just have saved my entire project.
You need to also clear out the reference.
while(tabControlToClear.Controls.Count > 0)
{
var tabPage = tabControlToClear.Controls[0];
tabControlToClear.Controls.RemoveAt(0);
tabPage.Dispose();
// Clear out events.
foreach (EventHandler subscriber in tabPage.Click.GetInvocationList())
{
tabPage.Click -= subscriber;
}
}