将列表绑定到数据源

9

我希望能够将列表绑定到ListBox数据源中,当列表被修改时,ListBox的UI会自动更新。(Winforms而非ASP)。以下是一个示例:

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_;
    }
}

如果我用BindingList<Foo> fooList = new BindingList<Foo>();替换List<Foo> fooList = new List<Foo>();,那么它就能工作。但是我不想改变foolist的原始类型。我想让像这样的东西工作:listBox1.DataSource = new BindingList<Foo>(fooList); 编辑:我刚刚在List<T> vs BindingList<T> Advantages/DisAdvantages中读到了Ilia Jerebtsov的话:“当您将BindingSource的DataSource设置为List时,它会在内部创建一个BindingList来包装您的列表”。我认为我的示例只是说明这不是真的:我的List<>似乎没有内部包装成BindingList<>。

List<>不会触发任何事件供观察者知道何时更新。无论观察者是UI组件还是充当包装器的另一个列表都无关紧要。为什么反对改用BindingList,当绑定正是您需要做的事情? - JRoughan
我不想将List更改为BindingList,因为它已经在项目中作为List使用。我将不得不替换每个方法的签名,我想避免修改已经稳定的内容。 - KVM
1
如果您将返回类型更改为IList<Foo>,那么是否仍然存在相同数量的破坏性更改? - JRoughan
2个回答

8
您的示例中缺少 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

编辑

请注意(正如评论中所指出的)- 数据绑定源与 INotifyPropertyChanged 不兼容。


这个可以运行。不幸的是,当 fooList 的一个成员的 Bar 属性在其他地方被更新时,ListBox 的内容没有被更新。这里有一个同样遇到这个问题而感到烦恼的人:http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/f54c125f-6f53-4446-9088-a193f6474059 - Larry

6

尝试

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而无需更改其原始类型。另外,当您更改Bar成员时,它将更新ListBox,例如fooList[1].Bar = "Hello"; 但是,您必须将ListBox的DisplayMember属性设置为"Bar",或者保留Foo类定义中的.ToString()覆盖。
为了避免每次都需要进行强制转换,建议您在与List定义相同的级别使用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);

谢谢你的解决方案,但我发现Jens Kloster的解决方案更加优雅 :) - KVM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接