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



The information in this article applies to:

  • Microsoft Visual Basic .NET (2003)
  • Microsoft Visual Basic .NET (2002)
  • Microsoft Visual C# .NET (2003)
  • Microsoft Visual C# .NET (2002)

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:
  1. 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 = "Description"
    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 = "Description";
    cb.DataSource =pt;
    Note You can also use this code for a ListBox control by replacing ComboBox with ListBox.
  2. 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 "Applies to" section.

MORE INFORMATION

Steps to Reproduce the Behavior


  1. In Microsoft Visual Studio .NET, start a new Windows application by using Visual Basic .NET or Visual C# .NET.

    By default, Form1 is created.
  2. 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 = "Description"
        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 = "Description";
        }
    }
    Note You can also use this code for a ListBox control by replacing ComboBox with ListBox.
  3. On the Project menu, click Add Class, and then click Open.
  4. 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("In type GetEnum..." + Environment.StackTrace)
            Return (Me)
        End Function
    
        Private Function IGetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
            position = -1
            Console.WriteLine("In IEnum GetEnum..." + 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("In IList this...")
                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("In type this...")
                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("In Count()")
                Return (_Cache.Count)
            End Get
        End Property
    
        ' Declare the Reset method that IEnumerator requires:
        Public Sub Reset() Implements IEnumerator.Reset
            Console.WriteLine("Called reset")
            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("Calling copy..." + Environment.StackTrace)
        End Sub
    
        Private Sub FillCache()
            Dim ordinal As Integer = 0
    
            Dim pType As ProductType = New ProductType
            pType.Description = "eric"
            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 = "nancy"
            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("In type GetEnum..." + Environment.StackTrace);
                return (this);
            }
    
            IEnumerator IEnumerable.GetEnumerator()
            {
                position = -1;
                Console.WriteLine("In IEnum GetEnum..." + 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("In IList this...");
                    return (_Cache[_CacheOrdinal[pos]]);
                }
                set
                {
                }
            }
    		
            public ProductType this[int pos]
            {
                get 
                {
                    Console.WriteLine("In type this...");
                    return ((ProductType)_Cache[_CacheOrdinal[pos]]);
                }
                set
                {
                }
            }
    		
            // ICollection
            public   int  Count
            {
                get
                {
                    Console.WriteLine("In Count()");
                    return (_Cache.Count);
                }
            }
    	
            // Declare the Reset method that IEnumerator requires:
            public void Reset()
            {
                Console.WriteLine("Called reset");
                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("Calling copy..." + Environment.StackTrace);
            }		
    		
            private void FillCache()
            {
                int ordinal = 0;
    			
                ProductType pType = new ProductType();
                pType.Description = "eric";
                pType.ProductTypeId = System.Guid.NewGuid();
                _Cache.Add(pType.ProductTypeId, pType);
                _CacheOrdinal.Add(ordinal++, pType.ProductTypeId);
    
                ProductType pType1 = new ProductType();
                pType1.Description = "nancy";
                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;
                }
            }
    
        }
    
  5. Right-click WindowsApplication1, and then click Properties.
  6. In the Output type list, click Console Application.
  7. In the Startup object list, click f, and then click OK.
  8. On the Build menu, click Build Solution.
  9. 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.

REFERENCES

For more information, visit the following link in Microsoft Developer Network (MSDN) Web site:

Modification Type:MinorLast Reviewed:2/3/2006
Keywords:kbvs2005swept kbvs2005doesnotapply kbvs2002sp1sweep kbDataBinding kbListBox kbCtrl kbControl kbComboBox kbbug KB820636 kbAudDeveloper