0

While working on combo box I found out a wired issue. Xaml looks like this

<ComboBox x:Name="cb" ItemsSource="{Binding MyEntity.Choices}" 
              SelectedItem="{Binding MyEntity.Choice,Mode=TwoWay}" 
              Height="25" 
              HorizontalAlignment="Stretch"
              VerticalAlignment="Top"/>

Let’s say Itemsource is binded with a List of Shaft (object) & selectedItem is one among the list.

public partial class MainWindow : Window
{
    private ShaftsData shaftData;

    public ShaftsData ShaftData
    {
        get { return shaftData; }
        set { shaftData = value; }
    }
    public MainWindow()
    {
        ShaftData = new ShaftsData();
        InitializeComponent();
        txtBox.Text = "Default";
        this.DataContext = this;
        SetComboCollectionAndSelectedShaft();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        shaftData.ComboCollection = null;
    }

    private void SetComboCollectionAndSelectedShaft()
    {
        Collection<Shaft> myCollection = new Collection<Shaft>();
        for (int i = 1; i <= 5; ++i)
        {
            Shaft sh = new Shaft();
            sh.Id = i;
            sh.Name = string.Format("{0} {1} ", txtBox.Text, i);
            myCollection.Add(sh);
        }
        shaftData.ComboCollection = myCollection;
        shaftData.SelectedShaft = shaftData.ComboCollection[0];
    }
}

 public class ShaftsData : INotifyPropertyChanged
{
    private Collection<Shaft> _comboCollection;
    private  Shaft _selectedShaft;

    public Collection<Shaft> ComboCollection
    {
        get
        {
            return _comboCollection;

        }
        set
        {
            _comboCollection = value;
            OnPropertyChanged("ComboCollection");
        }
    }

    public Shaft SelectedShaft
    {
        get { return _selectedShaft; }
        set
        {
            _selectedShaft = value;
            OnPropertyChanged("SelectedShaft");
        }
    }

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
    public event PropertyChangedEventHandler PropertyChanged;
}

And then I tried to make this list to null (Please refer to Button_Click) .Combobox is calling the .Equals of each object of the List & compare the last Selected object. While my expectation is it should not call .equals & set the SelectedItem to null.

 public class Shaft
{
    private int _id;
    private string _name;

    public int Id {
        get { return _id; }
        set {
            _id = value;
        }
    }

    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
        }
    }

    public override string ToString()
    {
        return _name;
    }

    public override bool Equals(object obj)
    {
        System.Diagnostics.Debug.WriteLine("Calling from object.Equals");
        Shaft shaft = obj as Shaft;
        if (null != shaft)
        {
            System.Diagnostics.Debug.WriteLine("Equals called for " + this.Name + ". Compared with " + shaft.Name);
        }
        else
        {
            System.Diagnostics.Debug.WriteLine("Equals called for " + this + ". Compared with " + shaft);
        }
        return base.Equals(obj);
    }

Now if I implement IEquatable on Shaft & then set the List to null it works fine. Means no call is going to .Eqauls & selectedItem is set to null.

New Implementation

public class Shaft : IEquatable<Shaft>
    {
        private int _id;
        private string _name;

        public int Id {
            get { return _id; }
            set {
                _id = value;
            }
        }

        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
            }
        }

        public override string ToString()
        {
            return _name;
        }

        public bool Equals(Shaft shaft)
        {
            System.Diagnostics.Debug.WriteLine("Calling from object.Equals");
           // Shaft shaft = obj as Shaft;
            if (null != shaft)
            {
                System.Diagnostics.Debug.WriteLine("Equals called for " + this.Name + ". Compared with " + shaft.Name);
            }
            else
            {
                System.Diagnostics.Debug.WriteLine("Equals called for " + this + ". Compared with " + shaft);
            }
            return base.Equals(shaft);
        }
    }
}

This shows that the combobox is not releasing the objects binded to Itemsource even if the list is null. Until we implements IEquatable .

Any idea why it is like that?

Deep
  • 1
  • you have to be more specific. Can't quite follow what you are talking about, especially why ComboBox is calling Equals. – Stephen Chung Mar 14 '11 at 07:47
  • @ Stephen My Question is why combobox is not releasing the objects present in ItemsSource even if I set it to null? I found a workaround the If I implement IEquatable then it is working fine, but no idea why this behavior is there. – Deep Mar 14 '11 at 08:42

1 Answers1

0

The behaviour upon Button_Click is identical for both implementations. How did you know there were no calls to Object.Equals if you didn't override it in the second case?

As for not "releasing" objects this looks to be a known issue of WPF: http://support.microsoft.com/kb/938416

As a workaround you can do one of the following:

  • call Collection.Clear method instead of setting collection reference to null
  • use ObservableCollection instead of Collection
Konstantin Oznobihin
  • 5,234
  • 24
  • 31
  • I know Object.Equals is not called (when I am using IEquatable) because I am using debug point inside Equals. Also I had tested with ObservableCollection , the behavior is same. I had also seen the article from Microsoft. It mostly talk about binding PropertyDescriptor , but in my case I am binding to property. – Deep Mar 22 '11 at 09:33
  • @Deep: In the second case you don't have Object.Equals method, only IEquatable.Equals, so it's impossible to put a breakpoint there. If you tried some other code in 2nd case could you please show it? – Konstantin Oznobihin Mar 23 '11 at 10:31
  • @Deep: PropertyDescriptor is used internally by WPF when you are binding to a regular property, that's why an article talks about it. – Konstantin Oznobihin Mar 23 '11 at 10:35
  • I Had worngly interpreted your question on object.equals. Actually when I am implementing IEquatable, I am not overriding Object.Equals. Is there is a need to override Object.Equals in case we are implementing IEquatable? – Deep Mar 27 '11 at 11:33
  • @Deep: Well, is's a good practice to provide consistent implementations of Object.Equals, IEquatable and operators '==' and '!='. Anyhow, the question is how did you know that Object.Equals was not called in the second case if you didn't implement it? Actually, the behaviour is pretty much the same in both cases. However, objects releasing is not anyhow related to equality implementation in this case. The issue is caused by reasons described in the KB article. – Konstantin Oznobihin Mar 27 '11 at 14:09