c# winforms在添加和删除自定义控件到flowlayoutpanel(任务管理器中的许多User对象)时可能存在

本文关键字:对象 User 许多 存在 任务管理器 添加 winforms 删除 自定义控件 flowlayoutpanel | 更新日期: 2023-09-27 18:06:40

我有一个中型的Winforms应用程序(dotNET4.0),我正在动态地从flowlayoutpanel中添加和删除自定义控件。
根据用户的选择,这些控件的数量会有所不同。

这工作得很好,除了我注意到一些内存泄漏。我通过监控任务管理器中的"用户对象"数量来检查这一点。当我将自定义控件添加到flowlayoutpanel时,数字会上升,但在处理这些时不会再一路下降。

实际上:USER对象的数量下降了很多(让我们说从100%到10%:所以90%被正确处理,留下10%的内存)…

结束语:一个或多个对象在释放用户控件后仍保留在内存中。我猜是代表或形象,但我不知道…会不会是静电造成的?

所以我的实际问题是:我在哪里没有正确地从用户控件释放内存,我该如何解决这个问题?你们能帮帮忙吗?

提前非常非常感谢!

这是我的用户控件:
请注意,除了_Costlinereportdata对象之外,其他对象都可以被处理。
(这应该继续使用,因为它被链接并在其他部分使用)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Classes.CustomControls
{
public partial class CostLineReport : UserControl, IDisposable
{
    #region CONSTANTS
    public static int pnlWidth = 870;
    public static int pnlHeightBase = 112;
    public static int pnlHeightExtended = 415;
    public static int pnlHeightCollapsed = 30;
    public static Color ColorNeutral = Color.FromArgb(176, 196, 222);
    public static Color ColorApprove = Color.FromArgb(173, 255, 47);
    public static Color ColorDisapprove = Color.FromArgb(255, 99, 71);
    public static Image ImageApprove = Image.FromFile(@"Images'tableAdd.png");
    public static Image ImageDisapprove = Image.FromFile(@"Images'tableDelete.png");
    public static Image ImageDetail = Image.FromFile(@"Images'tableDetail.png");
    public static Image ImageCollapse = Image.FromFile(@"Images'compile-warning.png");
    public static Image ImageViewRecords = Image.FromFile(@"Images'table.png");
    public static Image ImageCalculationInclude = Image.FromFile(@"Images'add.png");
    public static Image ImageCalculationExclude = Image.FromFile(@"Images'delete.png");
    #endregion
    #region FIELDS
    private CostLineReportData _Costlinereportdata;
    private ToolTip ttpApprove;
    private ToolTip ttpDisapprove;
    private ToolTip ttpCollapse;
    private ToolTip ttpDetail;
    private ToolTip ttpViewRecords;
    private ToolTip ttpCalculation;
    #endregion
    #region CTORS
    public CostLineReport(CostLineReportData line)
    {
        InitializeComponent();
        //
        this._Costlinereportdata = line;
        //
        this.picApprove.Click += new EventHandler(Approve);
        this.picDisapprove.Click += new EventHandler(Disapprove);
        this.picDetail.Click += new EventHandler(ResizeControl);
        this.picCollapse.Click += new EventHandler(CollapseControl);
        this.picViewRecords.Click += new EventHandler(ShowRecords);
        this.picCalculation.Click += new EventHandler(SwitchCalculateState);
        //
        this.rtMainData.Text = _Costlinereportdata.Maintext;
        this.rtDetail.Text = _Costlinereportdata.Detailtext; ;
        this.lblTitle.Text = _Costlinereportdata.Title;
        //
        ttpApprove = new ToolTip();
        ttpDisapprove = new ToolTip();
        ttpCollapse = new ToolTip();
        ttpDetail = new ToolTip();
        ttpViewRecords = new ToolTip();
        ttpCalculation = new ToolTip();
        ttpApprove.SetToolTip(this.picApprove, "Approve this line");
        ttpDisapprove.SetToolTip(this.picDisapprove, "Disapprove this line");
        ttpCollapse.SetToolTip(this.picCollapse, "Collapse this line");
        ttpDetail.SetToolTip(this.picDetail, "Show detail");
        ttpViewRecords.SetToolTip(this.picViewRecords, "View associated recordset");
        ttpCalculation.SetToolTip(this.picCalculation, "Include/Exclude from calculation");
        //
        this.picApprove.Image = CostLineReport.ImageApprove;
        this.picDisapprove.Image = CostLineReport.ImageDisapprove;
        this.picDetail.Image = CostLineReport.ImageDetail;
        this.picCollapse.Image = CostLineReport.ImageCollapse;
        this.picViewRecords.Image = CostLineReport.ImageViewRecords;
        this.picCalculation.Image = CostLineReport.ImageCalculationExclude;
        //
        Recolor();
    }
    #endregion
    #region PROPERTIES
    public RichTextBox MainTextBox
    { get { return this.rtMainData; } }
    public RichTextBox DetailTextBox
    { get { return this.rtDetail; } }
    public Label TitleLabel
    { get { return this.lblTitle; } }
    public PictureBox CalculateControl
    { get { return this.picCalculation; } }
    #endregion
    #region METHODS
    private void Approve(object o, EventArgs e)
    {
        _Costlinereportdata.Approve();
        Recolor();
    }
    private void Disapprove(object o, EventArgs e)
    {
        _Costlinereportdata.Disapprove();
        Recolor();
    }
    private void ResizeControl(object o, EventArgs e)
    {
        _Costlinereportdata.SwitchSize();
        switch(_Costlinereportdata.Viewstate)
        {
            case ViewState.Base:
                this.Height = CostLineReport.pnlHeightBase;
                break;
            case ViewState.Extended:
                this.Height = CostLineReport.pnlHeightExtended;
                break;
        }
    }
    private void CollapseControl(object o, EventArgs e)
    {
        _Costlinereportdata.Collapse();
        if (_Costlinereportdata.Collapsed)
            this.Height = CostLineReport.pnlHeightCollapsed;
        else
            this.Height = CostLineReport.pnlHeightBase;
    }
    private void Recolor()
    {
        switch (_Costlinereportdata.Approvalstate)
        {
            case ApprovalState.Approved:
                foreach (Control c in pnlColorIndicator.Controls)
                {
                    if (c is PictureBox)
                        ((PictureBox)c).BackColor = CostLineReport.ColorApprove;
                }
                pnlColorIndicator.BackColor = CostLineReport.ColorApprove;
                break;
            case ApprovalState.Disapproved:
                foreach (Control c in pnlColorIndicator.Controls)
                {
                    if (c is PictureBox)
                        ((PictureBox)c).BackColor = CostLineReport.ColorDisapprove;
                }
                pnlColorIndicator.BackColor = CostLineReport.ColorDisapprove;
                break;
            case ApprovalState.Neutral:
                foreach (Control c in pnlColorIndicator.Controls)
                {
                    if (c is PictureBox)
                        ((PictureBox)c).BackColor = CostLineReport.ColorNeutral;
                }
                pnlColorIndicator.BackColor = CostLineReport.ColorNeutral;
                break;
        }
    }
    private void ShowRecords(object sender, EventArgs e)
    {
        if (this._Costlinereportdata.Costline.LocalData != null)
        {
            using (Forms.frmCostlineRecords f = new Forms.frmCostlineRecords(this._Costlinereportdata.Costline.LocalData))
            {
                f.ShowDialog();
            }
        }
        else
            MessageBox.Show("This line has no records associated to it. The detailed list cannot be shown.",
                            "Can't show form",
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Information);
    }
    private void SwitchCalculateState(object sender, EventArgs e)
    {
        if (this._Costlinereportdata.Calculationstate == CalculationState.Included)
        {
            this._Costlinereportdata.Calculationstate = CalculationState.Excluded;
            this.picCalculation.Image = CostLineReport.ImageCalculationExclude;
        }
        else
        {
            this._Costlinereportdata.Calculationstate = CalculationState.Included;
            this.picCalculation.Image = CostLineReport.ImageCalculationInclude;
        }
    }
    public void SetCalculateState(CalculationState st)
    {
        switch (st)
        {
            case CalculationState.Included:
                this._Costlinereportdata.Calculationstate = CalculationState.Excluded;
                break;
            case CalculationState.Excluded:
                this._Costlinereportdata.Calculationstate = CalculationState.Included;
                break;
        }
        this.SwitchCalculateState(this, null);
    }
    #endregion
    #region INTERFACE_IMEPLEMENTS
    void IDisposable.Dispose()
    {
        this._Costlinereportdata = null;
        this.picApprove.Image.Dispose();
        this.picCalculation.Image.Dispose();
        this.picCollapse.Image.Dispose();
        this.picDetail.Image.Dispose();
        this.picDisapprove.Image.Dispose();
        this.picViewRecords.Image.Dispose();
        this.rtDetail.Dispose();
        this.rtMainData.Dispose();
        this.lblDivider.Dispose();
        this.lblDivider2.Dispose();
        this.lblDivider3.Dispose();
        this.lblDivider4.Dispose();
        this.lblTextDivider.Dispose();
        this.lblTitle.Dispose();
        this.picApprove.Dispose();
        this.picCalculation.Dispose();
        this.picCollapse.Dispose();
        this.picDetail.Dispose();
        this.picDisapprove.Dispose();
        this.picViewRecords.Dispose();
        this.pnlColorIndicator.Dispose();
        ttpApprove.Dispose();
        ttpDisapprove.Dispose();
        ttpCollapse.Dispose();
        ttpDetail.Dispose();
        ttpViewRecords.Dispose();
        ttpCalculation.Dispose();
        base.Dispose(true);
    }
    #endregion
}
}

这是我的用户控件

的内容(就winform控件而言)
CostLineReport - System.Windows.Forms.UserControl
-lblDivider - System.Windows.Forms.Label
-lblDivider2 - System.Windows.Forms.Label
-lblDivider3 - System.Windows.Forms.Label
-lblDivider4 - System.Windows.Forms.Label
-lblTextDivider - System.Windows.Forms.Label
-lblTitle - System.Windows.Forms.Label
-lblTopDivider - System.Windows.Forms.Label
-picApprove - System.Windows.Forms.PictureBox
-picCalculation - System.Windows.Forms.PictureBox
-picCollapse - System.Windows.Forms.PictureBox
-picDetail - System.Windows.Forms.PictureBox
-picDisapprove - System.Windows.Forms.PictureBox
-picViewRecords - System.Windows.Forms.PictureBox
-pnlColorIndicator - System.Windows.Forms.Panel
-rtDetail - System.Windows.Forms.RichTextBox
-rtMaindata - System.Windows.Forms.RichTextBox

这是我如何清除我的flowlayoutpanel的所有内容:

while (pnlCenterRightControls.Controls.Count > 0)
{
    pnlCenterRightControls.Controls[0].Dispose();
}
GC.Collect();

这是我如何添加我的控件

我添加了这个块,因为它还为User控件中的一个内部控件添加了一个Delegate(事件)方法。

foreach (Classes.CustomControls.CostLineReportData c in SelectedCostlineReports)
    {
        Classes.CustomControls.CostLineReport r = c.ToControl();
        r.CalculateControl.Click += delegate(object o, EventArgs e) { GetCostCalculation(); };
        this.pnlCenterRightControls.Controls.Add(r);
    }
<<p> 编辑解决方案/strong>
对于那些感兴趣的人,我已经更新了我的代码如下:

从flowlayoutpanel中移除控件
我还删除了此处事件的订阅者

while (pnlCenterRightControls.Controls.Count > 0)
{
    foreach (Control contr in pnlCenterRightControls.Controls)
    {
        Classes.CustomControls.CostLineReport clrp = contr as Classes.CustomControls.CostLineReport;
        clrp.CalculateControl.Click -= GetCostCalculation;
        clrp.Dispose();
    }
}

处理对象
(不处理静态图像)

    new public void Dispose()
    {
        this.Dispose(true);
    }
    /// <summary> 
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        this.picApprove.Click -= Approve;
        this.picDisapprove.Click -= Disapprove;
        this.picDetail.Click -= ResizeControl;
        this.picCollapse.Click -= CollapseControl;
        this.picViewRecords.Click -= ShowRecords;
        this.picCalculation.Click -= SwitchCalculateState;
        this._Costlinereportdata = null;
        this.rtDetail.Dispose();
        this.rtMainData.Dispose();
        this.lblDivider.Dispose();
        this.lblDivider2.Dispose();
        this.lblDivider3.Dispose();
        this.lblDivider4.Dispose();
        this.lblTextDivider.Dispose();
        this.lblTitle.Dispose();
        this.lblToplDivider.Dispose();
        this.picApprove.Dispose();
        this.picCalculation.Dispose();
        this.picCollapse.Dispose();
        this.picDetail.Dispose();
        this.picDisapprove.Dispose();
        this.picViewRecords.Dispose();
        this.pnlColorIndicator.Dispose();
        ttpApprove.Dispose();
        ttpDisapprove.Dispose();
        ttpCollapse.Dispose();
        ttpDetail.Dispose();
        ttpViewRecords.Dispose();
        ttpCalculation.Dispose();
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

c# winforms在添加和删除自定义控件到flowlayoutpanel(任务管理器中的许多User对象)时可能存在

如果没有可用的代码来运行分析器,很难判断,但我注意到的一件事是您不是-=您的事件。事件的任何处理程序仍将包含对对象的引用,因此将使它们不符合垃圾收集的条件。

编辑:

有两组事件需要担心,那些在你的控件内部的事件(你需要在你的dispose方法中手动删除)和那些引用你的控件或它的子的外部事件。

我所见过的任何自动执行此操作的方法都有bug或效果不佳,通常我发现最好手动执行。有关自动删除事件的更多信息,请参阅此回答。

如何从控件中删除所有事件处理程序

从它的外观来看,您应该处理所有这些对象:

ImageApprove.Dispose;
ImageDisapprove.Dispose;
ImageDetail.Dispose;
ImageCollapse.Dispose;
ImageViewRecords.Dispose;
ImageCalculationInclude.Dispose;
ImageCalculationExclude.Dispose;

c#是垃圾收集的,所以在你的代码中有这么多Dispose是没有意义的。

通常,你不需要关心设计者添加的任何东西,只需要关心你自己的代码(应该期望生成的代码是正确的)。

也因为UserControl已经实现了protected override void Dispose(bool disposing),你应该真正覆盖那个方法,而不是试图重新实现接口。

我不明白你的代码是怎么工作的…因为你的代码可能会在同一个对象上多次调用Dispose。我猜预定义的控件忽略了第二次调用,否则你会有异常,因为一个对象已经被处置了。

设计器添加的任何控件都将被自动处置。

在内部对象上调用Dispose是完全没有意义的。每个对象都应该处理自己的对象。总是这样。时期。

picApprove.Image.Dispose();    // BAD CODE!!!

显然,当您手动添加控件并手动连接事件处理程序时,通常必须手动删除两者。虽然您的语法可能是相同的,但我建议您在添加事件时使用与删除事件时相同的简短语法(除了+=而不是-=)。这样,就更容易确保它们都匹配。用途:

r.CalculateControl.Click += GetCostCalculation;

代替:

r.CalculateControl.Click += 
    delegate(object o, EventArgs e) { GetCostCalculation(); };

为什么要为设计器添加的控件手动添加事件处理程序?我看不出在设计器中初始化控件的一部分而在构造器中初始化其他部分有什么好处。如果其他人维护代码并认为事件没有正确连接,那么您只会增加回归错误的风险。

new public void Dispose()是另一个完全没有意义的

最后,我发现你的代码还有很多其他问题…它没有遵循很多好的设计实践。我建议你学习SOLID设计模式。

一些不良实践的例子:

    返回内部控件的公共属性。这通常是不必要的,因为对这些控件进行操作的代码应该驻留在这个类中。你也可以搜索TDA原则的信息。
  • UI和业务代码之间的紧密耦合。
  • Recolor函数中重复代码(DRY原则)
  • 不尊重。net约定的命名法,特别是事件处理函数名。