清除容器的控件属性并确保正确处置控件的最安全方法是什么?

发布于 2024-10-28 15:54:34 字数 895 浏览 1 评论 0原文

在 WinForms 应用程序中,我有很多实例,我将控件添加到容器中以响应用户操作 (panel.Controls.Add(new CustomControl(...))),然后清除面板 (panel.Controls.Clear()) 并重新使用它。

在生产中,应用程序偶尔会引发与 GDI 错误或无法加载 ImageList 相关的异常。这种情况通常发生在资源有限的计算机上以及全天密集使用该应用程序的用户中。很明显,我有 GDI 句柄泄漏,并且我应该处置从容器中清除的控件,但是我能找到的任何解释对于应在何处以及何时处置控件都含糊其辞。

清理容器后是否应该立即处理子控件?比如:

var controls = new List<Control>(_panel.Controls.Cast<Control>());
_panel.Controls.Clear();
foreach (var c in controls) c.Dispose();

或者我应该跟踪列表中的控件并在容器的 Dispose() 方法中调用 dispose?例如:

List<Control> _controlsToDispose = new List<Control>();
void ClearControls()
{
    _controlsToDispose.AddRange(_panel.Controls.Cast<Control>());
    _panel.Controls.Clear();
}
void Dispose()
{
    ...
    foreach (var c in _controlsToDispose) c.Dispose();
}

In a WinForms application I have a number of instances where I add a control to a container in response to a user action (panel.Controls.Add(new CustomControl(...))), then later clear the panel (panel.Controls.Clear()) and reuse it.

In production, the app occasionally throws an exception relating to GDI errors or failing to load an ImageList. This usually happens on machines with limited resources and with users that use the application intensively over the day. It seems pretty obvious that I have a GDI handle leak and that I should be disposing the controls that get cleared from the container, however any explanations I can find are vague about where and when the control should be disposed.

Should I dispose the child controls immediately after clearing the container? Something like:

var controls = new List<Control>(_panel.Controls.Cast<Control>());
_panel.Controls.Clear();
foreach (var c in controls) c.Dispose();

Or should I track the controls in a list and call dispose in the container's Dispose() method? Such as:

List<Control> _controlsToDispose = new List<Control>();
void ClearControls()
{
    _controlsToDispose.AddRange(_panel.Controls.Cast<Control>());
    _panel.Controls.Clear();
}
void Dispose()
{
    ...
    foreach (var c in _controlsToDispose) c.Dispose();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

心清如水 2024-11-04 15:54:34

选项 2 引入了另一个您需要清理的列表,并且这些项目将占用更多内存。我更喜欢选项 1,并在您提到的代码周围加上 try catch。

Option 2 introduces another list which you would need to cleanup and it will take some more memory for those items. I would prefer option 1 with a try catch wrapped around the code you mentioned.

相守太难 2024-11-04 15:54:34

在(某种程度上有效地)纠正我的应用程序未处理清除控件的任何情况后,我可以得出一些要点:

  • 有时我会预先构建一个控件列表,例如存储在 TagListViewItemTreeViewItem 集合的属性。它们不应该被清除,但应该迭代整个列表并在父级的 Dispose() 中调用 ((Control)item.Tag).Dispose()方法。
  • 如果该控件不会再次使用(这种情况可能在我动态创建它时发生),则应在从容器中清除该控件时将其丢弃。
  • 动态清除和添加控件时,您需要考虑控件的生命周期,以确定是否立即处置它们、推迟到父级被处置或不担心它。
  • 我遇到过这样的情况:我删除了一个控件以显示“正在加载...”消息,然后将控件放回原处,以响应线程完成。当我删除控件时,我添加了一个调用来处置该控件,这在尝试再次添加它时导致了错误。由于线程问题,调试起来并不容易。重点是生命周期可以依赖于 UI 线程以外的线程。典型的例子是表单显示后 20 秒,所以至少控件仍然存在。管理控件可能被销毁而线程仍想引用它的情况可能是弱事件的情况。

我无法找到任何有关管理控制生命周期和处置的最佳实践或建议。我猜规则是,如果一个控件没有结束它嵌套在已处置的控件上的生命,则无论何时不再使用它,或者在父控件的 中,都必须手动处置它最迟使用 Dispose() 方法。

After (somewhat effectively) correcting any cases where my app wasn't disposing cleared controls I can come up with some points:

  • Sometimes I've pre-built a list of controls, stored for example in the Tag property of a collection of ListViewItems or TreeViewItems. They shouldn't be disposed on clear, but the entire list should be iterated and ((Control)item.Tag).Dispose() called in the parent's Dispose() method.
  • If the control isn't going to be used again, which can happen when I create it on the fly, it should be disposed when it is cleared from the container.
  • When clearing and adding controls on the fly you need to consider the lifecycle of the controls to determine whether to dispose them immediately, defer it until the parent is being disposed, or to not worry about it.
  • I had a situation where I removed a control to display a 'Loading...' message, then dropped the control back in later, in response to a thread completing. I added a call to dispose the control when I removed it, which caused errors when trying to add it again. Because of the threading issue it wasn't straightforward to debug. The point is that the lifecycle can depend on threads other than the UI thread. The case in point was a matter of 20 seconds after the form was displayed, so at least the control still existed. Managing a situation where a control can be destroyed with threads still wanting to refer to it is probably a case for weak events.

I haven't been able to find any best practices or recommendations on managing control lifecycle and disposal. I guess the rule is just that if a control doesn't end it's life nested on a control that is disposed, it has to be disposed manually, whenever it isn't going to be used again, or in the parent control's Dispose() method at the latest.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文