2013-02-28 4 views
7

Я хочу, чтобы иметь возможность привязывать список к источнику данных listbox и когда список изменен, пользовательский интерфейс списка автоматически обновляется. (Winforms не ASP). Вот пример:Bind List to DataSource

private List<Foo> fooList = new List<Foo>(); 

    private void Form1_Load(object sender, EventArgs e) 
    { 
     //Add first Foo in fooList 
     Foo foo1 = new Foo("bar1"); 
     fooList.Add(foo1); 

     //Bind fooList to the listBox 
     listBox1.DataSource = fooList; 
     //I can see bar1 in the listbox as expected 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     //Add anthoter Foo in fooList 
     Foo foo2 = new Foo("bar2"); 
     fooList.Add(foo2); 
     //I expect the listBox UI to be updated thanks to INotifyPropertyChanged, but it's not 
    } 

class Foo : INotifyPropertyChanged 
{ 
    private string bar_ ; 
    public string Bar 
    { 
     get { return bar_; } 
     set 
     { 
      bar_ = value; 
      NotifyPropertyChanged("Bar"); 
     } 
    } 

    public Foo(string bar) 
    { 
     this.Bar = bar; 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    private void NotifyPropertyChanged(string info) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(info)); 
     } 
    } 

    public override string ToString() 
    { 
     return bar_; 
    } 
} 

Если я заменяю List<Foo> fooList = new List<Foo>(); на BindingList<Foo> fooList = new BindingList<Foo>(); то он работает. Но я не хочу менять оригинальный тип глупого. Я хотел бы что-то вроде этого, чтобы работать: listBox1.DataSource = new BindingList<Foo>(fooList);

EDIT: Кроме того, я только что прочитал здесь List<T> vs BindingList<T> Advantages/DisAdvantages с Ильей Жеребцов: «Когда вы установите DataSource BindingSource к Перечню <>, он внутренне создает BindingList, чтобы обернуть список». Я думаю, что мой пример просто показывает, что это не так: мой список <>, кажется, не внутренне завернут в BindingList <>.

+0

Список <> не создает никаких событий для наблюдателей, которые знают, когда их нужно обновлять. Не имеет значения, является ли наблюдатель компонентом пользовательского интерфейса или другим списком, действующим в качестве обертки. Почему возражение о переходе на BindingList при привязке - это то, что вам нужно сделать? – JRoughan

+0

Я не хочу изменять список в BindingList, потому что он уже используется в качестве списка везде в проекте. Мне придется заменить все подписи методов, я хочу избежать изменения того, что уже стабильно. – Michael

+0

Что делать, если вы изменили тип возврата на IList ? У вас все еще есть такое же количество нарушений? – JRoughan

ответ

7

У вас нет BindingSource в вашем примере.

вам нужно изменить его, как это использовать BindingSource

var bs = new BindingSource(); 
    Foo foo1 = new Foo("bar1"); 
    fooList.Add(foo1); 

    bs.DataSource = fooList; //<-- point of interrest 

    //Bind fooList to the listBox 
    listBox1.DataSource = bs; //<-- notes it takes the entire bindingSource 

Редактировать

Имейте в виду, что (как было указано в комментариях) - BindingSource не работает с INotifyPropertyChanged

+0

Это работает. К сожалению, содержимое ListBox не обновляется, когда свойство Bar элемента fooList обновляется в другом месте. Есть кто-то, кто раздражен тем же вопросом здесь: http://social.msdn.microsoft.com/Форумы/en-US/netfxbcl/thread/f54c125f-6f53-4446-9088-a193f6474059 – Larry

+0

@Laurent хороший момент. Я этого не знал. Отредактированный ответ –

+0

Awesome, отлично работает. – Michael

5

Попробуйте

listBox1.DataSource = new BindingList<Foo>(fooList); 

затем

private void button1_Click(object sender, EventArgs e) 
{ 
    Foo foo2 = new Foo("bar2"); 
    (listBox1.DataSource as BindingList<Foo>).Add(foo2); 
} 

Это обновит fooList без необходимости изменять свой первоначальный вид. Кроме того, он будет обновлять ListBox при изменении элемента Bar, как fooList[1].Bar = "Hello";

Тем не менее, вы должны установить DisplayMember свойство ListBox в «Бар», или держать .ToString() переопределение как в определении класса Foo.

Для того, чтобы избежать придется бросить каждый раз, когда я предлагаю вам использовать переменную BindingList на том же уровне, как ваше определение списка:

private List<Foo> fooList; 
private BindingList<Foo> fooListUI; 

fooListUI = new BindingList<Foo>(fooList); 
listBox1.DataSource = fooListUI; 

и кнопки:

Foo foo2 = new Foo("bar2"); 
fooListUI.Add(foo2); 
+0

Спасибо за ваше решение, но я нашел решение Jens Kloster немного более элегантным :) – Michael

+1

Я уважаю это! :-) – Larry