0

I am trying to make an application that binds a List/Collection etc of custom objects (Places) with a listbox so that:

  • ListBox displays a selected property ("name") of every object
  • Selecting an item in ListBox changes the "current" item in List/Collection
  • Adding/Removing an item in ListBox, adds/removes an item in List/Collection
  • Adding/Removing an item in List/Collection, adds/removes an item in ListBox
  • Changing the displayed property of an item, changes the item in ListBox

My code so far is:

namespace DataBindingTest

{

public partial class Form1 : Form

{

    BindingSource bs;
    ObservableCollection<Place> places = new ObservableCollection<Place>();
    Place place1 = new Place("pl1", 2.2);
    Place place2 = new Place("pl2", 2.3);

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        bs = new BindingSource();
        places.Add(place1);
        places.Add(place2);
        bs.DataSource = places;

        listBox1.DataSource = bs;
        listBox1.DisplayMember = "Name";
        listBox1.DataBindings.Add(new Binding("Text", bs, "Name", true, DataSourceUpdateMode.OnPropertyChanged));             
    }

    public class Place : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        string name;
        double losses;

        public Place(string n, double l)
        {
            name = n;
            losses = l;
        }

        public string Name
        {
            get { return name; }
            set 
            {
                name = value;
                OnPropertyChanged("Name");
            }
        }

        public double Losses
        {
            get { return losses; }
            set { losses = value; }
        }

        protected void OnPropertyChanged(string PropertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
        }
    }

    private void button1_Click(object sender, EventArgs e)
    {

        places[0].Name = "Place 1";
        places[1].Name = "Place 2";
    }
}

When OnPropertyChanged is called for places[0] it's OK, when it's called for places[1], the handler is null! Why is that happening?

SOLUTION

It seems to be a problem (bug maybe?) with ObservableCollection! If I use BindingList instead, it works fine!! Found it here. after 3 days of searching.

EDIT

namespace DataBindingTest
{
    public partial class Form1 : Form
    {
        BindingSource bs;
        BindingList<Place> places = new BindingList<Place>();
        Place place1 = new Place("pl1", 2.2);
        Place place2 = new Place("pl2", 2.3);


        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            bs = new BindingSource();
            places.Add(place1);
            places.Add(place2);
            bs.DataSource = places;

            listBox1.DataSource = bs;
            listBox1.DisplayMember = "Name";
            listBox1.DataBindings.Add(new Binding("Text", bs, "Name", true, DataSourceUpdateMode.OnPropertyChanged));

            Place place3 = new Place("pl3", 0);
            bs.Add(place3);
            places[2].Name = "Place3";

        }

        private void button1_Click(object sender, EventArgs e)
        {
            places[0].Name = "Place 1";
            places[1].Name = "Place 2";
        }


    }


    public class Place : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        string name;
        double losses;

        public Place(string n, double l)
        {
            name = n;
            losses = l;
        }


        public string Name
        {
            get { return name; }
            set
            {
                name = value;
                OnPropertyChanged("Name");

            }
        }

        public double Losses
        {
            get { return losses; }
            set { losses = value; }
        }

        protected void OnPropertyChanged(string PropertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(PropertyName));
        }
    }

}

I just put the Class Place outside the Class Form1, because I need to use it in the whole namespase, but I dont think it changes something.

Community
  • 1
  • 1
Than Gav
  • 21
  • 1
  • 4
  • can you post the working code as answer? i just tried your code, and noticed that only selected item will be updated (if you select 2nd row in listbox, than the handler will be null for places[0]).. – har07 Nov 23 '13 at 14:11
  • @har07 Let me know if that worked for you – Than Gav Nov 23 '13 at 14:30
  • yup, changing ObservableCollection to BindingList also worked for me. So, just post that as an answer and mark it accepted than. I've been working with ObservableCollection and its work great in WPF and Windows Phone (anything that use XAML binding), but seems not suitable for windows form data binding – har07 Nov 23 '13 at 14:43

2 Answers2

2

It seems to be a problem (bug maybe?) with ObservableCollection! If I use BindingList instead, it works fine!! Found it here. after 3 days of searching.

EDIT

namespace DataBindingTest 
{

    public partial class Form1 : Form

    {

        BindingSource bs;
        BindingList<Place> places = new BindingList<Place>();
        Place place1 = new Place("pl1", 2.2);
        Place place2 = new Place("pl2", 2.3);


        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            bs = new BindingSource();
            places.Add(place1);
            places.Add(place2);
            bs.DataSource = places;

            listBox1.DataSource = bs;
            listBox1.DisplayMember = "Name";
            listBox1.DataBindings.Add(new Binding("Text", bs, "Name", true, DataSourceUpdateMode.OnPropertyChanged));

            Place place3 = new Place("pl3", 0);
            bs.Add(place3);
            places[2].Name = "Place3";

        }

        private void button1_Click(object sender, EventArgs e)
        {
            places[0].Name = "Place 1";
            places[1].Name = "Place 2";
        }


    }


    public class Place : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        string name;
        double losses;

        public Place(string n, double l)
        {
            name = n;
            losses = l;
        }


        public string Name
        {
            get { return name; }
            set
            {
                name = value;
                OnPropertyChanged("Name");

            }
        }

        public double Losses
        {
            get { return losses; }
            set { losses = value; }
        }

        protected void OnPropertyChanged(string PropertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(PropertyName));
        }
    }

}
Than Gav
  • 21
  • 1
  • 4
  • They are used in a different situation. WPF supports INotifyCollectionChanged (which is ObservableCollection base), but DataGridView only supports IBindingList changes. INotifyCollectionChanged is more efficient on changes. ObservableCollection could support both interfaces. But it doesn't. – TamusJRoyce Jul 15 '16 at 14:55
0

Not sure why, but it only seems to update the selected item. This isn't elegant, but it does seem to work as a stopgap solution:

private void button1_Click(object sender, EventArgs e)
{
   int actualIndex = listBox1.SelectedIndex;
   listBox1.SelectedIndex = 0;
   places[0].Name = "Place 1";
   listBox1.SelectedIndex = 1;
   places[1].Name = "Place 2";
   listBox1.SelectedIndex = actualIndex;
}
Hakaron
  • 16
  • 1
  • Thank you for your response. The problem is that I want the listbox to be updated automatically when a property changes. The reason I added the button was to check if that happens. You said it updates only the selectedItem of listbox and thanks for that, I'll try to automate that process. – Than Gav Nov 23 '13 at 13:47
  • ObservableCollection seems to have problems being notified about changes on its items. BindingList does the job perfectly though! – Than Gav Nov 23 '13 at 14:26
  • Ofcourse, how could I forget about BindingList. Glad to see you sorted it out though. – Hakaron Nov 23 '13 at 20:15