The same bug is reproducible with .NET Framework but it seems it has no chance to be ever fixed.
But the .NET Core looks like a good place for introducing some improvements in this area.
Each instance of the System.Windows.Forms.Control type contains a private member variable of the LayoutEventArgs type - cachedLayoutEventArgs . And, the LayoutEventArgs instance typically contains a reference to some specific control.
Sometimes, the cachedLayoutEventArgs field is not cleared when the child control disposing of does not affect the layout process of the parent control due to some reasons.
Here are the minimal reproduction-steps:
Button.Click handlers:c#
void btnOpenView_Click(object sender, System.EventArgs e) {
var view = new Panel() { BackColor = Color.Red };
view.Name = "View";
view.Bounds = new Rectangle(100, 100, 100, 100);
view.Parent = this;
}
void btnCloseView_Click(object sender, System.EventArgs e) {
SuspendLayout();
var view = this.Controls.Find("View", false)[0];
if(view != null)
view.Dispose();
ResumeLayout(false);
}
Actual behavior:
The panel is still referencing via the System.Windows.Forms.Control.cachedLayoutEventArgs field at the form level. As result, it can't be GC-collected properly and still in memory.
Expected behavior:
The panel should not be referenced.
It looks like we should use the WeakReference when caching the LayoutEventArgs.
As an alternative solution, we can validate the cachedLayoutEventArgs value on child control removing and clear the problematical field.
Sometimes, the cachedLayoutEventArgs field is not cleared when the child control disposing does not affect the layout process of the parent control due to some reasons.
Could you be more clear about these scenarios? What is not triggering when the child control disposing does not affect the layout process of the parent control?
Hi, @zsd4yr
Could you be more clear about these scenarios?
From my experience, there are two cases when this issue is 100% reproducible:
Here are the steps for last case reproduction:
void btnAddViewToTab_Click(object sender, System.EventArgs e) {
var view = new Panel();
view.Name = "View";
view.Dock = DockStyle.Fill;
view.Parent = tabPage2;
}
void btnRemoveViewFromTab_Click(object sender, System.EventArgs e) {
var view = tabPage2.Controls.Find("View", false)[0];
if(view != null)
view.Dispose();
}
System.Windows.Forms.Control.cachedLayoutEventArgs field at the tabPage2 level.Also, there are some scenarios when this issue is reproducible from time to time:
I'll take a look!
@KlausLoeffelmann @DmitryGaravsky
Proposal for Fix
It looks like we should use the WeakReference when caching theLayoutEventArgs.
I think this won't work, if nobody holds a reference to the LayoutEventArgs it will be collected immediately, making the cache useless. What you want is a ConditionalWeakTable where the key is the Control and the value the LayoutEventArgs (ConditionalWeakReference does not exist unfortunately, and the framework API required to build one is internal).
@weltkante
if nobody holds a reference to the LayoutEventArgs it will be collected immediately, making the cache useless.
Yes, you're absolutely correct, but I proposed not the exact replacement of LayoutEventArgs cachedEventArgs; with the WeakReference cachedEventArgs; but "something based on weak-references" that can resolve this issue.
As an alternative solution, we can validate the cachedLayoutEventArgs value on child control removing and clear the problematical field.
@KlausLoeffelmann, we have been very busy these last few weeks! When you get a chance, could you update this thread with any progress?
Sure. We should re-prioritize issues this week, anyway, but I am guessing, this will be waiting a little time longer.