绑定到 IList,但得到 "Complex DataBinding accepts as a data source either an IList or an IListSource"

Binding to IList, but getting "Complex DataBinding accepts as a data source either an IList or an IListSource"

如果您 运行 以下代码没有调试(或 "Enable Just My Code" 处于活动状态),它似乎可以工作。但是如果你用调试器启动它并关闭 "Enable Just My Code",你将得到这个异常(然后被库代码吞没):

System.ArgumentException occurred
  HResult=-2147024809
  Message=Complex DataBinding accepts as a data source either an IList or an IListSource.
  Source=System.Windows.Forms
  StackTrace:
       at System.Windows.Forms.ListControl.set_DataSource(Object value)
  InnerException: 

这是我的代码的最小版本:

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

static class Program {
    [STAThread]
    static void Main() {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MainForm(new MainModel()));
    }
}

public class MainModel {
    public IList Options { get; } = new List<string> { "Foo", "Bar" };
}

class MainForm : Form {
    private System.ComponentModel.IContainer components;
    private ComboBox comboBox;
    private BindingSource bindingSource;
    private ErrorProvider errorProvider;

    public MainForm(MainModel mainModel) {
        InitializeComponent();
        bindingSource.DataSource = mainModel;
    }

    private void InitializeComponent() {
        components = new System.ComponentModel.Container();
        bindingSource = new BindingSource(components);
        errorProvider = new ErrorProvider(components);
        ((System.ComponentModel.ISupportInitialize) bindingSource).BeginInit();
        ((System.ComponentModel.ISupportInitialize) errorProvider).BeginInit();
        SuspendLayout();

        bindingSource.DataSource = typeof(MainModel);

        comboBox = new ComboBox();
        comboBox.DataBindings.Add(new Binding("DataSource", bindingSource, "Options", true));
        comboBox.DropDownStyle = ComboBoxStyle.DropDownList;
        Controls.Add(comboBox);

        errorProvider.ContainerControl = this;
        errorProvider.DataSource = bindingSource;

        ((System.ComponentModel.ISupportInitialize) bindingSource).EndInit();
        ((System.ComponentModel.ISupportInitialize) errorProvider).EndInit();
        ResumeLayout(false);
    }

}

问题似乎与数据绑定有关。如果我注释掉 comboBox.DataBindings.Add(new Binding("DataSource", bindingSource, "Options", true)); 行,则不会发生异常。

我在网上找到了很多关于此异常的引用,但似乎在所有这些情况下,问题都在于数据源不是 IList(如异常消息所述)。然而,在这种情况下,Options IList。所以我无法解释这个例外。

我注意到如果我删除 ErrorProvider,异常 不会 发生。但我不明白为什么会这样;我的实际程序中需要错误提供程序。

我在 Visual Studio 2015 年工作,目标是 .NET 4.6。

看起来您正在声明 DataBinding,而您的 bindingSource 仍在 BeginInit - EndInit 块中。尝试将该行移动到 之后 EndInit 行,或者改为在 OnLoad 覆盖中:

protected override void OnLoad(EventArgs e) {
  base.OnLoad(e);
  comboBox.DataBindings.Add(new Binding("DataSource", bindingSource, "Options", true));
}

DataSource 属性 添加Binding 是错误的想法。

要设置 DataSource,您应该为 DataSource 分配一些内容,而不是添加 Binding。例如,添加 BindingSelectedValue 有意义,但对 DataSource.

无效

您的代码应该是:

bindingSource.DataSource = typeof(MainModel);
bindingSource.DataMember = "Options";

comboBox = new ComboBox();
comboBox.DataSource = bindingSource;
comboBox.DropDownStyle = ComboBoxStyle.DropDownList;
Controls.Add(comboBox);

那么您将不会收到任何错误。

注意:如果出于任何原因您只是想知道如何避免示例中的错误,只需设置 this.Visible = true 或准确调用 this.Show()InitializeComponent 之后强制创建控制句柄并使数据绑定开始工作。我的代码不需要此修复程序。

为什么数据绑定到 DataSource 属性 不是一个好主意?

如果您要将 DataSource 属性 绑定到 MainModelOptins 属性,则意味着您要更新 Options 属性 使用组合框!这也意味着 MainModel class 应该实现 INotifyPropertyChanged 以通知组合框有关 Options 属性 的变化!(请记住选项 属性 是 IList) 这是完全错误的想法!

那么如何通知 ComboBox 数据源的变化?

如果要通知 ComboBox 数据源的更改,数据源列表应实现 IBindingListBindingList<T> 是实现的一个例子。所以设置 comboBox.DataSource = someDataSource; 而不是数据绑定到 DataSource.

就足够了