Reputation: 13643
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
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
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