How to bind controls to an object data source in a WinForms App (.NET Core) project

Mapping classes to UI controls manually is probably the most tedious thing you can do in coding. In order to minimize this coding effort, you can bind your controls to an object data source. In this article, I’ll show how to do this in a WinForms App (.NET Core) project. First, I’ll show step-by-step how to configure binding through the UI (in both VS2019 and VS2022). Then I’ll show how to configure binding programmatically (including an example of auto-binding based on a naming convention).

1 – Add a class

Add a class with public properties that you want to bind to the form controls. To keep this simple, we’ll add a class with a single string property that we want to bind to a textbox.

public class Coder { public string Name { get; set; } }
Code language: C# (cs)

2 – Add a BindingSource

Add a BindingSource control to the form and set your class as its object data source. This allows you to bind controls to your class properties at design time. The steps for adding a BindingSource like this are different for VS2019 and VS2022, so take a look at the appropriate section below.

In VS2022

To add a BindingSource with an object data source in VS2022:

  • Add a BindingSource control to the form.
  • In the BindingSource properties, click DataSource and Add new Object Data Source
WinForms BindingSource.DataSource property. Shows a button "Add new Object Data Source"
  • Select your class from the list and click OK.
Visual Studio - Add and update object data sources window. Shows the Coder class selected.

Note: If you don’t see your public class, rebuild the project and try this step again.

  • In the BindingSource properties, click DataSource and select your class (Other Data Sources > Project Data Sources > Coder).
WinForms BindingSource.DataSource property set to the object data source that was just added (Coder class)

In VS2019

To add a BindingSource with an object data source in VS2019:

  • Add a file to the project called Coder.datasource with the following XML (with your own class type info):
<?xml version="1.0" encoding="utf-8"?> <GenericObjectDataSource DisplayName="Coder" Version="1.0" xmlns="urn:schemas-microsoft-com:xml-msdatasource"> <TypeInfo>WinFormsProject.Coder, WinFormsProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null</TypeInfo> </GenericObjectDataSource>
Code language: HTML, XML (xml)

Note: This is necessary because VS2019 doesn’t have the “Add new Object Data Source” wizard.

  • Add a BindingSource control to the form.
  • In the BindingSource properties, click DataSource and select your class (Other Data Sources > Project Data Sources > Coder).
WinForms BindingSource.DataSource property set to the object data source that was just added via the Coder.datasource file

If it won’t take your click, double-check the class type info in the Coder.datasource file and rebuild.

3 – Bind a control to a property via the BindingSource

  • Add a TextBox control to the form.
  • In the TextBox’s properties, click Data Bindings > Advanced > click the […] button.
WinForms TextBox.DataBindings property showing the [...] button to click
  • Select the properties to bind and click OK. In this example, we’re binding coderBindingSource.Name with the TextBox.Text property.
Visual Studio Advanced DataBindings Editor window. TextBox.Text is selected in the Properties section on the left, and coderBindingSource.Name is selected in the Binding section on the right.

4 – In the code, set BindingSource.DataSource to an object

Now that the binding configuration is done, you can do the binding by setting BindingSource.DataSource to an object instance in the code, like this:

public frmCoder() { InitializeComponent(); var coder = new Coder() { Name = "Bob" }; coderBindingSource.DataSource = coder; }
Code language: C# (cs)

This binds to the Coder object when the form initializes and shows the Coder.Name in the bound textbox:

WinForm with a textbox showing the bound Coder.Name value ("Bob")

You can get the initial object data from anywhere – a database, an API, a config file, or even hardcode it. Just be sure to set the BindingSource.DataSource when you have a new instance.

Configure the bindings programmatically

All of the previous steps showed how to configure the bindings at design time to minimize coding effort. If you prefer, you can add bindings programmatically instead by calling IBindableComponent.DataBindings.Add() for each control you want to bind, like this:

public frmCoder() { InitializeComponent(); var coder = new Coder() { Name = "Bob" }; txtCoder.DataBindings.Add(nameof(TextBox.Text), coder, nameof(Coder.Name)); //This means bind coder.Name -> txtCoder.Text }
Code language: C# (cs)

Note: Watch out for adding the same binding twice. This will cause a runtime exception. If you’ve added bindings through the UI already, be sure to remove them if you’re doing this programmatic approach.

When the form is launched, the textbox shows “Bob” (from Coder.Name).

Auto-binding programmatically

If you want to automatically bind controls to properties in your class, you can loop through the form controls and call DataBindings.Add() for each control. Here’s an example of automatically binding controls (just textboxes for brevity) using a simple naming convention:

var coder = new Coder() { FullName = "Bob" }; //Auto-bind by naming convention (coder.Property.Name == control.Name) var coderPropNames = coder.GetType().GetProperties().Select(t => t.Name).ToHashSet(); foreach (Control control in this.Controls) { if (!coderPropNames.Contains(control.Name)) continue; switch (control) { case TextBox txt: txt.DataBindings.Add(nameof(txt.Text), coder, txt.Name); break; //add other control types here } }
Code language: C# (cs)

Note: Using this overly simple naming convention, you’d very likely run into a naming collision at some point, which results in a rather confusing compile-time error (ex: “Cannot convert from string to Control’). The control names can’t be the same as existing form property names (i.e. can’t use “Name” because form.Name exists).

Of course, the risk here is the control names and class property names could get out of sync. If you’re going with this approach, consider trying to detect these scenarios ahead of time, so you don’t have to spend time debugging in the future.

Leave a Comment