Since my support request has gone unanswered since it was entered last wednesday, I thought I would try the forum and see if anyone else has seen this issue.
I am binding a business object to a ultracombo and the objects are not being released when the business object has a collection property of type ICollection<>.
If the type is changed from an ICollection<> to a Collection<>, the objects are properly disposed.
If I bind the entity to a System.Windows.Forms.ComboBox, the objects are properly disposed.
Steps to repoduce:
1) Run the following sample code in a memory profiler (We are using ANTS from Redgate).
2) After the application is running, use a memory profiler and notice that there are 5000 Employee objects still in memory even though only one of them is still bound to the control.
3) Modify the code to bind to the System.Windows.Forms.ComboBox and rerun that application. This time there is only 1 object in memory.
4) Instead of step 3 modify the code to to use Collection<> instead of ICollection<> and rerun that application. This time there is again only 1 object in memory.
So the problem appears to be with the Infragistics control. I supect this may also be a problem with the UltraGrid control. This is a high priority issue for us because our application is auto-refreshing changes and over long periods of time significant memory is used up.
FORM CODE:
using System;using System.Collections.Generic;using System.Collections.ObjectModel;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;Designer CODE:
namespace InterfaceBindingMemoryLeak{ public partial class Form1 : Form { public Form1() { InitializeComponent(); }
protected override void OnLoad(EventArgs e) { base.OnLoad(e);
for (int iLoop = 0; iLoop < 5000; iLoop++) { Employee[] plans = new Employee[1]; plans[0] = new Employee(); plans[0].Version = "2009-Q2";
ultraCombo1.SetDataBinding(plans, "", true); //comboBox1.DataSource = plans; } } }
public class Employee {
#region [ Constructors ]
public Employee() {
}
#endregion [ Constructors ]
#region [ Public Properties ]
public virtual string Version { get; set; }
//public virtual Collection<Locations> Deliverables public virtual ICollection<Locations> Deliverables { get { return null; } }
#endregion [ Public Properties ] }
public class Locations { public string address { get; set; } }}
DESIGNER CODE:
namespace InterfaceBindingMemoryLeak{ partial class Form1 { /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.IContainer components = null;
/// <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) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); }
#region Windows Form Designer generated code
/// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { Infragistics.Win.Appearance appearance4 = new Infragistics.Win.Appearance(); Infragistics.Win.Appearance appearance1 = new Infragistics.Win.Appearance(); Infragistics.Win.Appearance appearance2 = new Infragistics.Win.Appearance(); Infragistics.Win.Appearance appearance3 = new Infragistics.Win.Appearance(); Infragistics.Win.Appearance appearance12 = new Infragistics.Win.Appearance(); Infragistics.Win.Appearance appearance7 = new Infragistics.Win.Appearance(); Infragistics.Win.Appearance appearance6 = new Infragistics.Win.Appearance(); Infragistics.Win.Appearance appearance5 = new Infragistics.Win.Appearance(); Infragistics.Win.Appearance appearance9 = new Infragistics.Win.Appearance(); Infragistics.Win.Appearance appearance11 = new Infragistics.Win.Appearance(); Infragistics.Win.Appearance appearance10 = new Infragistics.Win.Appearance(); Infragistics.Win.Appearance appearance8 = new Infragistics.Win.Appearance(); this.comboBox1 = new System.Windows.Forms.ComboBox(); this.ultraCombo1 = new Infragistics.Win.UltraWinGrid.UltraCombo(); ((System.ComponentModel.ISupportInitialize)(this.ultraCombo1)).BeginInit(); this.SuspendLayout(); // // comboBox1 // this.comboBox1.FormattingEnabled = true; this.comboBox1.Location = new System.Drawing.Point(242, 12); this.comboBox1.Name = "comboBox1"; this.comboBox1.Size = new System.Drawing.Size(121, 21); this.comboBox1.TabIndex = 5; // // ultraCombo1 // this.ultraCombo1.CheckedListSettings.CheckStateMember = ""; appearance4.BackColor = System.Drawing.SystemColors.Window; appearance4.BorderColor = System.Drawing.SystemColors.InactiveCaption; this.ultraCombo1.DisplayLayout.Appearance = appearance4; this.ultraCombo1.DisplayLayout.BorderStyle = Infragistics.Win.UIElementBorderStyle.Solid; this.ultraCombo1.DisplayLayout.CaptionVisible = Infragistics.Win.DefaultableBoolean.False; appearance1.BackColor = System.Drawing.SystemColors.ActiveBorder; appearance1.BackColor2 = System.Drawing.SystemColors.ControlDark; appearance1.BackGradientStyle = Infragistics.Win.GradientStyle.Vertical; appearance1.BorderColor = System.Drawing.SystemColors.Window; this.ultraCombo1.DisplayLayout.GroupByBox.Appearance = appearance1; appearance2.ForeColor = System.Drawing.SystemColors.GrayText; this.ultraCombo1.DisplayLayout.GroupByBox.BandLabelAppearance = appearance2; this.ultraCombo1.DisplayLayout.GroupByBox.BorderStyle = Infragistics.Win.UIElementBorderStyle.Solid; appearance3.BackColor = System.Drawing.SystemColors.ControlLightLight; appearance3.BackColor2 = System.Drawing.SystemColors.Control; appearance3.BackGradientStyle = Infragistics.Win.GradientStyle.Horizontal; appearance3.ForeColor = System.Drawing.SystemColors.GrayText; this.ultraCombo1.DisplayLayout.GroupByBox.PromptAppearance = appearance3; this.ultraCombo1.DisplayLayout.MaxColScrollRegions = 1; this.ultraCombo1.DisplayLayout.MaxRowScrollRegions = 1; appearance12.BackColor = System.Drawing.SystemColors.Window; appearance12.ForeColor = System.Drawing.SystemColors.ControlText; this.ultraCombo1.DisplayLayout.Override.ActiveCellAppearance = appearance12; appearance7.BackColor = System.Drawing.SystemColors.Highlight; appearance7.ForeColor = System.Drawing.SystemColors.HighlightText; this.ultraCombo1.DisplayLayout.Override.ActiveRowAppearance = appearance7; this.ultraCombo1.DisplayLayout.Override.BorderStyleCell = Infragistics.Win.UIElementBorderStyle.Dotted; this.ultraCombo1.DisplayLayout.Override.BorderStyleRow = Infragistics.Win.UIElementBorderStyle.Dotted; appearance6.BackColor = System.Drawing.SystemColors.Window; this.ultraCombo1.DisplayLayout.Override.CardAreaAppearance = appearance6; appearance5.BorderColor = System.Drawing.Color.Silver; appearance5.TextTrimming = Infragistics.Win.TextTrimming.EllipsisCharacter; this.ultraCombo1.DisplayLayout.Override.CellAppearance = appearance5; this.ultraCombo1.DisplayLayout.Override.CellClickAction = Infragistics.Win.UltraWinGrid.CellClickAction.EditAndSelectText; this.ultraCombo1.DisplayLayout.Override.CellPadding = 0; appearance9.BackColor = System.Drawing.SystemColors.Control; appearance9.BackColor2 = System.Drawing.SystemColors.ControlDark; appearance9.BackGradientAlignment = Infragistics.Win.GradientAlignment.Element; appearance9.BackGradientStyle = Infragistics.Win.GradientStyle.Horizontal; appearance9.BorderColor = System.Drawing.SystemColors.Window; this.ultraCombo1.DisplayLayout.Override.GroupByRowAppearance = appearance9; appearance11.TextHAlignAsString = "Left"; this.ultraCombo1.DisplayLayout.Override.HeaderAppearance = appearance11; this.ultraCombo1.DisplayLayout.Override.HeaderClickAction = Infragistics.Win.UltraWinGrid.HeaderClickAction.SortMulti; this.ultraCombo1.DisplayLayout.Override.HeaderStyle = Infragistics.Win.HeaderStyle.WindowsXPCommand; appearance10.BackColor = System.Drawing.SystemColors.Window; appearance10.BorderColor = System.Drawing.Color.Silver; this.ultraCombo1.DisplayLayout.Override.RowAppearance = appearance10; this.ultraCombo1.DisplayLayout.Override.RowSelectors = Infragistics.Win.DefaultableBoolean.False; appearance8.BackColor = System.Drawing.SystemColors.ControlLight; this.ultraCombo1.DisplayLayout.Override.TemplateAddRowAppearance = appearance8; this.ultraCombo1.DisplayLayout.ScrollBounds = Infragistics.Win.UltraWinGrid.ScrollBounds.ScrollToFill; this.ultraCombo1.DisplayLayout.ScrollStyle = Infragistics.Win.UltraWinGrid.ScrollStyle.Immediate; this.ultraCombo1.DisplayLayout.ViewStyleBand = Infragistics.Win.UltraWinGrid.ViewStyleBand.OutlookGroupBy; this.ultraCombo1.Location = new System.Drawing.Point(13, 12); this.ultraCombo1.Name = "ultraCombo1"; this.ultraCombo1.Size = new System.Drawing.Size(100, 22); this.ultraCombo1.TabIndex = 6; this.ultraCombo1.Text = "ultraCombo1"; // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(459, 143); this.Controls.Add(this.ultraCombo1); this.Controls.Add(this.comboBox1); this.Name = "Form1"; this.Text = "Form1"; ((System.ComponentModel.ISupportInitialize)(this.ultraCombo1)).EndInit(); this.ResumeLayout(false); this.PerformLayout();
#endregion
private System.Windows.Forms.ComboBox comboBox1; private Infragistics.Win.UltraWinGrid.UltraCombo ultraCombo1; }}
I believe I've identified the support case you mentioned, case number CAS-34645-JSDCJ7. I've had this case assigned.
As a Standard support case, we typically provide a response time of one to three business days, which can sometimes be longer depending on our current support volume. When we can't get to them this quickly, we handle Standard cases in the order in which they were received.
At a quick glance and a discussion with some colleagues, this will clearly take further investigation to determine what's happening. This research is best performed through the support case.
Thanks for your patience.
What is the status of CAS-34645-JSDCJ7? - i am having a similar issue.
I am databinding an UtraGrid to a list of MyClass objects that have only one property of the type IList<MySubClass>. The sub list is never initialized, so it is NULL.
After closing the Form that holds the Ultragrid, the MyClass objects are still kept in memory.
If i change the property to List<MySubClass>, there is no memory leak.
We have the problem in production with v. 2011.1 of the UltraGrid.
I have attached a small sample project using v. 2013.2 which reproduces the same problem.
I am using RedGate Ants Memory Profiler, and have also attached the Instance Retention Graph as pdf.
Looking forward to hear from you.
Hi,
I'm not entirely sure exactly why using IList instead of List causes a memory leak, but I can tell you that using an IList for data binding is not a good practice. This will results in all sorts of odd behaviors and problems. In fact, even using List<T> is not the best practice, because List<T> does not provide support for important data binding notifications. The best thing to do would be to change both of your lists to use BindingList<T>.
It's also not a good idea for your sub-list to ever return null. It should always return an empty list, because if it returns null, the BindingManager will be unable to determine the data structure (columns) that the grid should use for the child band.
Hi mike
I am aware that a BindingList should be used for databinding, that is also how we do it. But if the object that is databound to the grid has additional public properties (of e.g. IList type), which are not intended for databinding, the memory leak issue occurs.
I guess that relates to the fact that the ultra grid automatically creates Bands for any public 'Collection' property.
I found two ways of solving my problem: Changing the IList to a BindingList as you suggest, or making the property not public.
Thanks for your response.
Hi Jesper,
I just wanted to give you an update on this.
We checked out the sample project you provided to Developer Support, and your sample does, in fact, leak memory. But this is nothing to do with the grid or the data source having an Interface property.
Your sample calls ShowDialog on the form. When you do this, closing the form does not dispose of that form. So your form is never disposed, and thus the grid is never disposed and that's where the leak is.
If I change your code to explicitly dispose the form, I can find no memory leaks, regardless of the IList in the data source.
//new GridForm().ShowDialog(); using (GridForm gridForm = new GridForm()) gridForm.ShowDialog();
Jesper,
This issue has been submitted to our development team for further review. The reference number for this item is 167186. A support case has also been opened for you with reference number CAS-133292-X3F5M2. You will receive more information on this issue through the support case.
Yes correct - the mere existence of an IList<> property causes a leak. I have tested it with different collection types, and it seems that it happens only with some collection types (e.g. IList<>, IDictionary<,>, Dictionary<,>) - not the List<> type.
And yes the [Browsable(false)] will also solve the problem. We have included this in our internal Performance Guide Lines - to use [Browsable(false)] on any collection not to be displayed by the UltraGrid.
But it would be great to have some developer review on this, because if you are inexperienced in working with the UltraGrid, and not doing memory profiling every now and then, you will never be aware of the problem, until an OutOfMemory occurs.
Thanks
I see. So you don't even want the child list to show up in the grid? But it's mere existence causes the leak. Hm. Okay, I'm going to ask Infragistics Developer support to write this up for developer review so we can check it out.
Another workaround you might try is to put the [Browsable(false)] attribute on the property.