私はフローレイアウトパネルからカスタムコントロールを動的に追加および削除している中規模の Winforms アプリケーション (dotNET4.0) を持っています。
ユーザーの選択に基づいて、これらのコントロールの量は異なります。
これは問題なく動作していますが、いくつかのメモリリークに気付きました。Taskmanager で 'USER Objects' の数を監視してこれをチェックします。カスタム コントロールを flowlayoutpanel に追加すると数値が上がりますが、これらを破棄しても数値が下がりません。
実際: USER オブジェクトの数は大幅に減少します (100% から 10% までとしましょう: 90% が適切に破棄され、メモリに 10% が残ります)...
結論:ユーザー コントロールを破棄した後も、1 つ以上のオブジェクトがメモリ内に残っています。私の推測では、代表者または画像ですが、私にはわかりません...静的なものでしょうか?
したがって、私の実際の質問は次のとおりです。ユーザーコントロールからメモリを適切に解放していない場所と、これを解決するにはどうすればよいですか? 助けていただけませんか?
よろしくお願いします!
これは私のユーザー コントロールです。_ 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();
これが私のコントロールを追加する方法です
このブロックを追加したのは、これにより、ユーザー コントロールの内部コントロールの 1 つにデリゲート (イベント) メソッドも追加されるためです。
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);
}