我有一个中型 Winforms 应用程序 (dotNET4.0),我在其上从 flowlayoutpanel 动态添加和删除自定义控件。
根据用户的选择,这些控件的数量会有所不同。
这工作正常,除了我注意到一些内存泄漏。我通过监视任务管理器中的“用户对象”编号来检查这一点。当我将自定义控件添加到 flowlayoutpanel 时,该数字会上升,但在处理这些控件时不会再次下降。
实际上:用户对象的数量下降了很多(可以说从 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);
}
编辑解决方案
对于那些感兴趣的人,我已将我的代码更新为以下内容:
从 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);
}