Daniel Wolf
Daniel Wolf

Reputation: 13643

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

If you run the following code without debugging (or with "Enable Just My Code" active), it will seem to work. But if you start it with debugger and with "Enable Just My Code" turned off, you will get this exception (which is then swallowed by library 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: 

Here's a minimal version of my code:

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

}

The problem appears to be with the data binding. If I comment out the line comboBox.DataBindings.Add(new Binding("DataSource", bindingSource, "Options", true));, the exception does not occur.

I found many references to this exception online, but it seems that in all those cases, the problem was that the data source was not an IList (as stated by the exception message). In this case, however, Options is an IList. So I'm at a loss to explain the exception.

I noticed that the exception does not occur if I remove the ErrorProvider. But I can't figure out why that is; and I need the error provider in my actual program.

I'm working in Visual Studio 2015, targeting .NET 4.6.

Upvotes: 3

Views: 3558

Answers (2)

LarsTech
LarsTech

Reputation: 81620

It looks like you are declaring your DataBinding while your bindingSource is still inside a BeginInit - EndInit block. Try moving that line to after the EndInit line, or in the OnLoad override instead:

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

Upvotes: 3

Reza Aghaei
Reza Aghaei

Reputation: 125207

Adding a Binding for DataSource property is wrong idea.

To set the DataSource you should assign something to DataSource rather than adding a Binding. Adding a Binding makes sense for SelectedValue for example, but not for DataSource.

Your code should be:

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

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

Then you will not receive any error.

Note: If for any reason you are just curious about how to avoid the error in your example, just set this.Visible = true or call this.Show() exactly after InitializeComponent to force control handles created and make the data-binding start working. This fix is not required for my code.

Why data-binding to DataSource property is not a good idea?

If you are going to bind DataSource property to Optins property of MainModel it means you are going to update Options property using the combo box! Also it means the MainModel class should implement INotifyPropertyChanged to notify the combo boxes about changes in Options property!(Keep in mind the options property is an IList) That's totally wrong idea!

So how can I inform the ComboBox about changes in the data source?

If you want to notify the ComboBox about change in the data-source, the data-source list should implement IBindingList. BindingList<T> is an example of the implementation. So it's enough to set comboBox.DataSource = someDataSource; rather than data-binding to DataSource.

Upvotes: 1

Related Questions