Reputation: 18138
I want to add complex databinding to my custom winforms control, so I can do the following:
myControl.DisplayMember = "Name";
myControl.ValueMember = "Name";
myControl.DataSource = new List<someObject>();
Does anyone know what interfaces, etc. have to be implemented to achieve this?
I have had a look into it and all I found is IBindableComponent
, but that seems to be for Simple Binding rather than Complex Binding.
Upvotes: 6
Views: 6559
Reputation: 2989
I want to add an answer to this question which may not address the OP's particular user case in the body, but seemed to at least answer the subject: Add DataSource Property to a Custom WinForms Control
When working with stock Controls, I would usually create a custom class that represents the data I want to bind, then use the Control's DataSource
property to link the instance of my custom class to the Control, completing the data bind. However, when it comes to creating custom controls, there is no DataSource
property like I was used to before. Using Visual Studio's designer, here's how I have data binding working in custom controls:
I select my custom control, and in the Properties dialog, I bind the control property to the property in my custom class. The designer creates a new BindingSource
object based on my custom class, and adds a new DataBinding
element in the DataBindings
list property that is included by inheriting the Control
class. In the constructor for the Form that contains the instance of my custom control, all I need to do is create a new instance of my custom class, then assign the DataSource
property of the BindingSource
object, to the instance of my custom class.
For example, in the .Designer
code file for my Form:
private System.Windows.Forms.BindingSource customControlBusinessObjectBindingSource;
private void InitializeComponent()
{
this.customControlBusinessObjectBindingSource = new System.Windows.Forms.BindingSource(this.components);
...
// Support PropertyChanged events
this.customControlInstance.DataBindings.Add(new System.Windows.Forms.Binding("MyProperty", this.customControlBusinessObjectBindingSource, "MyProperty", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
}
With the rest of your Form designer code, this is enough to prepare data binding. Then, in the Form's main code file, you would have:
private CustomControlBusinessObject customControlBO = new CustomControlBusinessObject();
public MyForm()
{
customControlBusinessObjectBindingSource.DataSource = customControlBO;
}
And that will complete the connection of your custom business object (class) to your custom control. As long as you have PropertyChanged event notifications implemented, your control's bound value will be updating.
Upvotes: 0
Reputation: 455
Apply one of the following attributes to your custom control, depending on which kind of data binding you need:
ComplexBindingPropertiesAttribute
LookupBindingPropertiesAttribute
(The question specifically mentions complex data binding, but the given code example looks like lookup data binding to me, so I have included both.)
For example implementations, look at the .NET Framework source code:
ComplexBindindPropertiesAttribute
implementation in DataGridView
LookupBindingPropertiesAttribute
implementation in ListControl
But those implementations look very complicated to me, so it might be easier to embed an existing control (such as a DataGridView
, ListBox
or ComboBox
) within your own custom control to take advantage of its existing data binding implementation, rather than writing your own. (You could make the embedded control invisible if necessary.) That is the approach demonstrated by Microsoft in the following guides:
In those guides, they create a data source to bind the custom control to an external database, but it looks like you're simply trying to bind your custom control to an internal collection such as a List<T>
. In that case, the adapted code below might work for you.
In a Windows Forms project in Visual Studio, add a new UserControl
.
For complex data binding, apply the ComplexBindingPropertiesAttribute
to the custom control. Add a DataGridView
control to it. Add DataSource
and DataMember
properties, and hook them into the DataGridView
's own properties.
// ComplexBindingControl.cs
// Adapted from https://learn.microsoft.com/visualstudio/data-tools/create-a-windows-forms-user-control-that-supports-complex-data-binding
using System.ComponentModel;
using System.Windows.Forms;
namespace BindingDemo
{
[ComplexBindingProperties("DataSource", "DataMember")]
public partial class ComplexBindingControl : UserControl
{
public ComplexBindingControl()
{
InitializeComponent();
}
// Use a DataGridView for its complex data binding implementation.
public object DataSource
{
get => dataGridView1.DataSource;
set => dataGridView1.DataSource = value;
}
public string DataMember
{
get => dataGridView1.DataMember;
set => dataGridView1.DataMember = value;
}
}
}
For lookup data binding, apply the LookupBindingPropertiesAttribute
to the custom control. Add a ListBox
or ComboBox
control to it. Add DataSource
, DisplayMember
, ValueMember
and LookupMember
properties, and hook them into the ListBox
's or ComboBox
's own properties.
// LookupBindingControl.cs
// Adapted from https://learn.microsoft.com/visualstudio/data-tools/create-a-windows-forms-user-control-that-supports-lookup-data-binding
using System.ComponentModel;
using System.Windows.Forms;
namespace BindingDemo
{
[LookupBindingProperties("DataSource", "DisplayMember", "ValueMember", "LookupMember")]
public partial class LookupBindingControl : UserControl
{
public LookupBindingControl()
{
InitializeComponent();
}
// Use a ListBox or ComboBox for its lookup data binding implementation.
public object DataSource
{
get => listBox1.DataSource;
set => listBox1.DataSource = value;
}
public string DisplayMember
{
get => listBox1.DisplayMember;
set => listBox1.DisplayMember = value;
}
public string ValueMember
{
get => listBox1.ValueMember;
set => listBox1.ValueMember = value;
}
public string LookupMember
{
get => listBox1.SelectedValue?.ToString();
set => listBox1.SelectedValue = value;
}
}
}
(Edit: thanks to Frank's answer for reminding me that listBox1.SelectedValue
could be null
.)
To test it, build the project in Visual Studio, then add an instance of the custom control to a Form
. Create some sample data, and bind it to the custom control using its relevant properties.
// Form1.cs
using System.Collections.Generic;
using System.Windows.Forms;
namespace BindingDemo
{
public partial class Form1 : Form
{
private readonly List<SomeObject> data;
public Form1()
{
InitializeComponent();
// Prepare some sample data.
data = new List<SomeObject>
{
new SomeObject("Alice"),
new SomeObject("Bob"),
new SomeObject("Carol"),
};
// Bind the data to your custom control...
// ...for "complex" data binding:
complexBindingControl1.DataSource = data;
// ...for "lookup" data binding:
lookupBindingControl1.DataSource = data;
lookupBindingControl1.DisplayMember = "Name";
lookupBindingControl1.ValueMember = "Name";
}
}
internal class SomeObject
{
public SomeObject(string name)
{
Name = name;
}
public string Name { get; set; }
}
}
Upvotes: 5
Reputation: 39
To run the very helpfull example of Chris Tollefson BindingDemo without problems put a try/catch Block around the LookupMember getter like this:
public string LookupMember {
get {
try {
return listBox1.SelectedValue.ToString();
}
catch { return null; }
}
set => listBox1.SelectedValue = value;
}
Upvotes: 0
Reputation: 1
Your class needs to inherit the DataBoundControl class instead of UserControl.
Upvotes: 0