views:

335

answers:

3

I'm attempting to bind a combo to a collection of objects:

    Dim t As New TradeOrderStatus()
    Dim ts As List(Of TradeOrderStatus) = t.GetStatuses

    With Me.cboTradeStatus
        .DataSource = ts
        .SelectedItem = Nothing
    End With

This works fine and I see the list of items in the combo. However when I try to set the SelectedItem of the combo to one of the items:

Me.cboTradeStatus.SelectedItem = Trade.TradeStatus

nothing happens. Trade.TradeStatus is an instance of the class TradeOrderStatus and all the necessart statuses are visible in the drop-down list. The SelectedItem remains as Nothing (or as the first item in the list if I omit the .SelectedItem = Nothing line from the binding code).

Debug.Print(CStr(Me.cboTradeStatus.Items.Contains(.TradeStatus)))

also returns false. Can anyone help?

+3  A: 

Trade.TradeStatus is an instance of the class TradeOrderStatus

You mention it is a class; is it the same instance as one of those in the data-bound list? It needs to find an equality match. Alternatively, you can override Equals (and GetHashCode() - always keep the two in sync) to achieve the same thing.


(edit)

The simplest way to fix it is to bind to SelectedValue; with the "full" example (below), something like:

cbo.DisplayMember = cbo.ValueMember = "Name";    
...
btn.Click += delegate { cbo.SelectedValue = GetCurrentStatus().Name; };

(edit)

Here's a C# example (sorry, my VB-fu is weak) of providing custom equality of the different statuses - note the "uncomment to fix":

using System.Windows.Forms;
using System.Collections.Generic;

class MyStatus {
    public MyStatus(string name) { Name = name; }
    public string Name { get; private set; }
    public override string ToString() {return Name; }
    /* uncomment to fix
    public override bool Equals(object obj) {
        MyStatus other = obj as MyStatus;
        return other != null && other.Name == this.Name;
    }
    public override int GetHashCode() {return Name.GetHashCode(); }
    */
}
static class Program {
    static void Main() {
        ComboBox cbo = new ComboBox();
        cbo.DataSource = GetStatuses();
        Button btn = new Button();
        btn.Click += delegate { cbo.SelectedItem = GetCurrentStatus(); };
        btn.Text = "Set Status";
        btn.Dock = DockStyle.Bottom;
        Application.Run(new Form { Controls = { cbo, btn } });
    }
    static List<MyStatus> GetStatuses() {
        List<MyStatus> stats = new List<MyStatus>();
        stats.Add(new MyStatus("Open"));
        stats.Add(new MyStatus("Pending"));
        stats.Add(new MyStatus("Closed"));
        return stats;
    }
    static MyStatus GetCurrentStatus() {
        return new MyStatus("Closed");
    }
}
Marc Gravell
No it's a different instance. The one I'm trying to set as SelectedItem is instantiated as a property of a parent class, the ones in the combo are instantiated purely to populate the combo.
Simon
A: 

Answering my own question here based on Marc's response. I've converted the sample given to the VB equivalent:

Public Overloads Overrides Function Equals(ByVal obj As Object) As Boolean
        ' Check for null values and compare run-time types.
        If obj Is Nothing Or Not Me.GetType() Is obj.GetType() Then
            Return False
        End If

        Dim t As TradeOrderStatus = CType(obj, TradeOrderStatus)
        Return Me.ID = t.ID

    End Function


    Public Overrides Function GetHashCode() As Integer
        Return Me.GetHashCode()
    End Function

My combo binding now works as expected.

Simon
A: 

When I use the GetHashCode() solution above in C# I get a stack overflow from the infinite loop:

public override int GetHashCode() 
{ 
    return this.GetHashCode(); 
}

Did you mean to put instead:

return this.Id;

When I made this change it worked. This was really a pain so here's the code in C# for anyone who has a null DataSource and needs to override the GetHashCode and Equals methods in the ComboBox:

using System.Windows.Forms;

namespace Ace.Docs.UI.ComboBoxes
{
    internal class DisplayInfo : ComboBox
    {
     #region Properties (2) 

        public string DisplayName { get; set; }

        public int Id { get; set; }

     #endregion Properties 

     #region Methods (2) 

     // Public Methods (2) 

        public override bool Equals(object obj)
        {
            // Check for null values and compare run-time types. 
            if (obj == null | (!object.ReferenceEquals(this.GetType(), obj.GetType())))
            {
                return false;
            }
            var t = (DisplayInfo)obj;
            return this.Id == t.Id;
        }

        public override int GetHashCode()
        {
            return this.Id;
        }

     #endregion Methods 
     }
}
Daver