Microsoft KB Archive/820636

= BUG: The ListBox control or the ComboBox control copies list items several times in Visual Basic .NET or in Visual C# .NET =

Article ID: 820636

Article Last Modified on 6/1/2007

-

APPLIES TO


 * Microsoft Visual Basic .NET 2003 Standard Edition
 * Microsoft Visual Basic .NET 2002 Standard Edition
 * Microsoft Visual C# .NET 2003 Standard Edition
 * Microsoft Visual C# .NET 2002 Standard Edition

-





SYMPTOMS
You have a Microsoft Windows form with a ListBox control or a ComboBox control. When you bind the ListBox or the ComboBox to a data source, the control copies the items in the list several times.



CAUSE
When you create the ListBox or when you create the ComboBox, the form does not have a BindingContext property. The ListBox or the ComboBox must have a BindingContext to copy the data. Therefore, the control requests that the form send the BindingContext. The form creates the BindingContext, and then the form fires notification to the control. The ListBox or the ComboBox receives the notification, and then the control tries to update the DataManager property. Therefore, the ListBox or the ComboBox creates the first copy of the list. When the form creates the BindingContext, the ListBox or the ComboBox creates the second copy of the list.



RESOLUTION
To resolve this problem, use the following procedures:
 * Set the BindingContext property of the control before you set any data bindings.
 * Set the DisplayMember property of the control before you set the DataSource property.

To use these procedures, follow these steps:   Replace the existing code in the Form f constructor with the following code.

Microsoft Visual Basic .NET Code Dim pt As ProductTypes = New ProductTypes 'Add a ComboBox control to the form. Dim cb As ComboBox = New ComboBox Controls.Add(cb) 'Invoke the binding context for the ComboBox. Dim bc As New BindingContext cb.BindingContext = bc 'Set the DataSource property and set the DisplayMember property of the ComboBox. cb.DisplayMember = &quot;Description&quot; cb.DataSource = pt Microsoft Visual C# .NET Code ProductTypes pt = new ProductTypes; //Add a ComboBox control to the form. ComboBox cb = new ComboBox; Controls.Add(cb); //Invoke the binding context for the ComboBox. BindingContext bc=new BindingContext; cb.BindingContext =bc; //Set the DataSource property and set the DisplayMember property of the ComboBox. cb.DisplayMember = &quot;Description&quot;; cb.DataSource =pt; Note You can also use this code for a ListBox control by replacing ComboBox with ListBox.  On the Debug menu, click Start.

The CopyTo method is called only one time.



STATUS
Microsoft has confirmed that this is a bug in the Microsoft products that are listed in the &quot;Applies to&quot; section.



Steps to Reproduce the Behavior
 In Microsoft Visual Studio .NET, start a new Windows application by using Visual Basic .NET or Visual C# .NET.

By default, Form1 is created.  Replace the existing code in Form1 with the following code.

Visual Basic .NET Code Imports System.Windows.Forms

Class f   Inherits Form Shared Sub Main Application.Run(New f)   End Sub

Private Sub New Dim pt As ProductTypes = New ProductTypes 'Add a ComboBox control to the form. Dim cb As ComboBox = New ComboBox Controls.Add(cb) 'Set the DataSource property and set the DisplayMember property of the ComboBox. cb.DataSource = pt       cb.DisplayMember = &quot;Description&quot; End Sub End Class Visual C# .NET Code using System.Windows.Forms; class f : Form {   static void Main {       Application.Run(new f); }

f {       ProductTypes pt = new ProductTypes; ComboBox cb = new ComboBox; //Add a ComboBox control to the form. Controls.Add(cb); //Set the DataSource property and set the DisplayMember property of the ComboBox. cb.DataSource = pt; cb.DisplayMember = &quot;Description&quot;; } } Note You can also use this code for a ListBox control by replacing ComboBox with ListBox. </li> On the Project menu, click Add Class, and then click Open.</li>  Replace the existing code in Class1 with the following code.

Visual Basic .NET Code Imports System Imports System.ComponentModel Imports System.Collections Imports System.Data Imports System.Data.SqlClient

Public Class ProductTypes Implements IList Implements IEnumerator Private position As Integer = -1 'List of items Private _Cache As Hashtable 'List of ordinal pointers to the items Private _CacheOrdinal As Hashtable

Public Sub New _Cache = New Hashtable _CacheOrdinal = New Hashtable FillCache End Sub

Public Function GetEnumerator As ProductTypes position = -1 Console.WriteLine(&quot;In type GetEnum...&quot; + Environment.StackTrace) Return (Me) End Function

Private Function IGetEnumerator As IEnumerator Implements IEnumerable.GetEnumerator position = -1 Console.WriteLine(&quot;In IEnum GetEnum...&quot; + Environment.StackTrace) Return CType((Me), IEnumerator) End Function

' IList Public ReadOnly Property IsFixedSize As Boolean Implements IList.IsFixedSize Get Return (False) End Get End Property

' IList Public ReadOnly Property IsReadOnly As Boolean Implements IList.IsReadOnly Get Return (False) End Get End Property Public Property IItem(ByVal index As Integer) As Object Implements IList.Item Get Console.WriteLine(&quot;In IList this...&quot;) Return (CType(_Cache(_CacheOrdinal(index)), ProductType)) End Get Set(ByVal Value As Object)

End Set End Property

Default Property Item(ByVal pos As Integer) As ProductType Get Console.WriteLine(&quot;In type this...&quot;) Return (CType(_Cache(_CacheOrdinal(pos)), ProductType)) End Get Set(ByVal Value As ProductType)

End Set End Property

' ICollection Public ReadOnly Property Count As Integer Implements ICollection.Count Get Console.WriteLine(&quot;In Count&quot;) Return (_Cache.Count) End Get End Property

' Declare the Reset method that IEnumerator requires: Public Sub Reset Implements IEnumerator.Reset Console.WriteLine(&quot;Called reset&quot;) position = -1 End Sub

' Declare the MoveNext method that IEnumerator requires: Public Function MoveNext As Boolean Implements IEnumerator.MoveNext If position < _Cache.Count - 1 Then position = position + 1 Return True Else Return False End If   End Function

Public Function Add(ByVal o As Object) As Integer Implements IList.Add Return 1 End Function

Public Sub Clear Implements IList.Clear End Sub

Public Sub RemoveAt(ByVal i As Integer) Implements IList.RemoveAt End Sub

Public Sub Remove(ByVal o As Object) Implements IList.Remove End Sub

Public Function IndexOf(ByVal o As Object) As Integer Implements IList.IndexOf Return 0 End Function

Public Function Contains(ByVal o As Object) As Boolean Implements IList.Contains Return False End Function

Public ReadOnly Property IsSynchronized As Boolean Implements ICollection.IsSynchronized Get Return (False) End Get End Property

Public ReadOnly Property SyncRoot As Object Implements ICollection.SyncRoot Get Return (Nothing) End Get End Property

Public Sub Insert(ByVal i As Integer, ByVal o As Object) Implements IList.Insert End Sub

Public ReadOnly Property Current As ProductType Get Return CType(_Cache(_CacheOrdinal(position)), ProductType) End Get End Property

' Declare the Current property that IEnumerator requires: Public ReadOnly Property ICurrent As Object Implements IEnumerator.Current Get Return _Cache(_CacheOrdinal(position)) End Get

End Property

Default Property Item(ByVal productTypeId As String) As ProductType Get Return CType((_Cache(productTypeId)), ProductType) End Get Set(ByVal Value As ProductType)

End Set End Property

Public Sub CopyTo(ByVal arr As System.Array, ByVal i As Integer) Implements ICollection.CopyTo _Cache.CopyTo(arr, i)       Console.WriteLine(&quot;Calling copy...&quot; + Environment.StackTrace) End Sub

Private Sub FillCache Dim ordinal As Integer = 0

Dim pType As ProductType = New ProductType pType.Description = &quot;eric&quot; pType.ProductTypeId = System.Guid.NewGuid _Cache.Add(pType.ProductTypeId, pType) _CacheOrdinal.Add(ordinal, pType.ProductTypeId) ordinal = ordinal + 1 Dim pType1 As ProductType = New ProductType pType1.Description = &quot;nancy&quot; pType1.ProductTypeId = System.Guid.NewGuid _Cache.Add(pType1.ProductTypeId, pType1) _CacheOrdinal.Add(ordinal, pType1.ProductTypeId) ordinal = ordinal + 1 End Sub

End Class

Public Class ProductType Private _ProductTypeId As Guid Private _Description As String

Public Sub New _ProductTypeId = System.Guid.NewGuid _Description = Nothing End Sub

Public Property ProductTypeId As Guid Get Return (_ProductTypeId) End Get Set(ByVal Value As Guid) _ProductTypeId = Value End Set End Property

Public Property Description As String Get Return (_Description) End Get Set(ByVal Value As String) _Description = Value End Set End Property End Class Visual C# .NET Code using System; using System.ComponentModel; using System.Collections; using System.Data; using System.Data.SqlClient;

public class ProductTypes : IList, IEnumerator {       private int position = -1; //List of items private Hashtable _Cache; //List of ordinal pointers to the items private Hashtable _CacheOrdinal;

public ProductTypes {           _Cache = new Hashtable; _CacheOrdinal = new Hashtable; FillCache; }       public   ProductTypes GetEnumerator {           position = -1; Console.WriteLine(&quot;In type GetEnum...&quot; + Environment.StackTrace); return (this); }

IEnumerator IEnumerable.GetEnumerator {           position = -1; Console.WriteLine(&quot;In IEnum GetEnum...&quot; + Environment.StackTrace); return (IEnumerator) (this); }

// IList public bool IsFixedSize {           get {               return (false); }       }

// IList public bool IsReadOnly {           get {               return (false); }       }        // IList object IList.this[int pos] {           get {               Console.WriteLine(&quot;In IList this...&quot;); return (_Cache[_CacheOrdinal[pos]]); }           set {           }        }        public ProductType this[int pos] {           get {               Console.WriteLine(&quot;In type this...&quot;); return ((ProductType)_Cache[_CacheOrdinal[pos]]); }           set {           }        }        // ICollection public  int  Count {           get {               Console.WriteLine(&quot;In Count&quot;); return (_Cache.Count); }       }        // Declare the Reset method that IEnumerator requires: public void Reset {           Console.WriteLine(&quot;Called reset&quot;); position = -1; }

// Declare the MoveNext method that IEnumerator requires: {           if (position < _Cache.Count - 1) {               position++; return true; }           else {               return false; }       }

public int Add(object o)       { return 1; }

public void Clear {}

public void RemoveAt(int i)       {} public void Remove(object o)       {}

public int IndexOf(object o)       { return 0; }

public bool Contains(object o)       { return false; }

public bool IsSynchronized {           get {               return (false); }       }

public object SyncRoot {           get {               return (null); }       }

public void Insert(int i, object o)       {}

public ProductType Current {           get {               return (ProductType)_Cache[_CacheOrdinal[position]]; }       }

// Declare the Current property that IEnumerator requires: object IEnumerator.Current {           get {               return _Cache[_CacheOrdinal[position]]; }       }        public ProductType this[string productTypeId] {           get {               return (ProductType)(_Cache[productTypeId]); }       }

public void CopyTo(System.Array arr, int i)       { _Cache.CopyTo(arr, i); Console.WriteLine(&quot;Calling copy...&quot; + Environment.StackTrace); }              private void FillCache {           int ordinal = 0; ProductType pType = new ProductType; pType.Description = &quot;eric&quot;; pType.ProductTypeId = System.Guid.NewGuid; _Cache.Add(pType.ProductTypeId, pType); _CacheOrdinal.Add(ordinal++, pType.ProductTypeId);

ProductType pType1 = new ProductType; pType1.Description = &quot;nancy&quot;; pType1.ProductTypeId = System.Guid.NewGuid; _Cache.Add(pType1.ProductTypeId, pType1); _CacheOrdinal.Add(ordinal++, pType1.ProductTypeId); }   }

public class ProductType {       private Guid                    _ProductTypeId; private string                 _Description;

public ProductType {           _ProductTypeId = System.Guid.NewGuid; _Description = null; }

public Guid ProductTypeId {           get {               return (_ProductTypeId); }           set {               _ProductTypeId = value; }       }

public string Description {           get {               return (_Description); }           set {               _Description = value; }       }

} </li> Right-click WindowsApplication1, and then click Properties.</li> In the Output type list, click Console Application.</li> In the Startup object list, click f, and then click OK.</li> On the Build menu, click Build Solution.</li> On the Debug menu, click Start.

Observe the output in the console. The CopyTo method and the GetEnumerator method are called many times, even though you may expect the GetEnumerator method to be called two times and the CopyTo method to be called only one time.</li></ol>

<div class="references_section">