Click here to Skip to main content
15,861,168 members
Articles / Programming Languages / Visual Basic
Article

Best Practice for Binding WinForms ListControls

Rate me:
Please Sign up or sign in to vote.
4.94/5 (60 votes)
26 Sep 20045 min read 258.3K   2.4K   83   32
This article demonstrates the best technique for databinding the ListBox and ComboBox controls in .NET Windows Forms

Introduction

According to the .NET Framework documentation, to bind a Windows Forms ListControl (e.g. a ListBox or ComboBox) as a lookup (that is, the control's display value maps to a different underlying value), you need to set three properties: DataSource, ValueMember, and DisplayMember. What it neither tells nor shows you, as far as I have been able to determine, is that the order in which you set these properties may make a huge difference in the performance and/or behavior of your code. In this sample, I will attempt to clarify this issue and show what I believe to be the proper way to create lookup controls in code.

Background

In Windows Forms, we consider subclasses of the abstract class ListControl to be "complex-bound" controls. In more everyday terms, that means they are useful as lookups, i.e. if their data source is an ADO.NET DataTable, they can display values from one column (perhaps a person's name) and use values from another column (perhaps a person's unique identifier) as the corresponding value of the control. The two complex-bound controls you've most likely encountered are the ComboBox and the Listbox. To complex-bind one of these controls, you need to set the DataSource (where values originate), the DisplayMember (the name of the column of data that supplies the visible list items), and the ValueMember (the name of the column of data that supplies the possible control values). You might think that the order in which you set these properties never matters, and samples from Microsoft and others would tend to back up such a belief. However, there are things going on underneath the covers that can cause trouble for your code. In particular, when the DataSource is changed, or when DisplayMember or ValueMember is changed after DataSource has been set, the binding infrastructure forces the control to rebind. Logically, this makes sense. But it poses a problem if you handle certain control events, in particular SelectedIndexChanged and SelectedValueChanged. In the worst case, the following simple sequence will raise SelectedIndexChanged three times in a row:

VB
listbox.DataSource = dataTable
listbox.ValueMember = "id"
listbox.DisplayMember = "name"

Obviously if you're doing anything non-trivial in your handlers, this could turn into a serious performance problem.

And it's even worse than that, because until DisplayMember is set, the SelectedValue of the control will not be the type you're probably expecting, but rather it will be a DataRowView. At that point, you have two choices if you're handling the aforementioned Selected-Changed events: Special-case for when SelectedValue returns a DataRowView, or handle and eat the exception when you try to do something incorrect with the DataRowView. I have actually seen instances of each of these in professional code. You don't want to have to do this, so in the following sections, I will show you how to sidestep this issue.

Why You Might Not Have This Problem

If you're using the Windows Forms designer to set up all of your lookup wiring, you don't have this problem. There are at least two reasons for this, as far as I can tell. First, in all of my testing, the designer always hooks up events -after- setting properties on form controls, so at the point the troublesome properties are set, the events aren't even there yet to be called. Second, the designer adds controls to the form at the end of InitializeComponents, and no events can be fired before that point.

Unfortunately, you frequently don't have the luxury of either of these workarounds. You may have to bind the ListControl long after all controls have been added to the form, and a workaround which temporarily removes the event handler would be a pain. There is probably a workaround by using Suspend and Resume on the binding context, but that would be too complicated a fix for what should really be a simple procedure.

The Solution

The solution is simple and can be summed up in three words: SET DATASOURCE LAST. Contrary to what you might expect, setting DisplayMember and ValueMember does not trigger any internal validation on the part of the control that might fail if there is not yet a DataSource. So you're safe in doing this. But the vast majority of samples (including those from Microsoft) set DataSource first and thus are prone to the difficulties I have mentioned.

Using the code

The included project contains code in C# and in VB.NET that demonstrates two different flavors of the problem, as well as two different solutions. The demo form contains four listboxes with four buttons that bind and unbind the listboxes by setting the lookup properties. If you're looking at the demo form, I will go over the listboxes from top to bottom by name.

  1. "HORRIBLE". This version sets the properties in the following order: DataSource, ValueMember, DisplayMember. This gives you three SelectedIndexChanged events with SelectedValue equal to a DataRowView, a DataRowView, and an Int32 respectively. Not fun to robustly handle.
  2. "MERELY BAD". This version sets the properties in the following order: DataSource, DisplayMember, ValueMember. I called this one "merely bad" because it only fires two SelectedIndexChanged events instead of three, but it's arguably not much better than "horrible" because in each of the events, SelectedValue for the listbox is still a DataRowView. You only get two events because it's apparently ok to set ValueMember without a rebind if DisplayMember is already set.
  3. "GOOD". This is the recommended order of DisplayMember, ValueMember, and DataSource. Here you only get one SelectedIndexChanged event, and it contains the type you really want, in this case an Int32.
  4. "GOOD HELPER FUNCTION". To enforce the good behavior in your code, you might want to write a helper function that takes a ListControl and values to plug into DataSource, ValueMember, and DisplayMember in the recommended order. This solution demonstrates such a function, found in LookupUtility.cs or LookupUtility.vb.

Here is the source code for the helper function in C#:

C#
public static void SetLookupBinding( ListControl toBind,
  object dataSource, string displayMember, 
  string valueMember )
{
  toBind.DisplayMember = displayMember;
  toBind.ValueMember = valueMember;

  // Only after the following line will the listbox 
  // receive events due to binding.
  toBind.DataSource = dataSource;
}

And Visual Basic.NET:

VB
Public Shared Sub SetLookupBinding(ByVal toBind As ListControl, 
 ByVal dataSource As Object, ByVal displayMember As String,
 ByVal valueMember As String)
  toBind.DisplayMember = displayMember
  toBind.ValueMember = valueMember

  '// Only after the following line will the listbox
  '// receive events due to binding.
  toBind.DataSource = dataSource
End Sub

History

  • 09/25/2004 Initial Release.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
I have over 10 years of full-lifecycle software development experience on a variety of large projects. I have a B.S. in Math from the University of Nebraska-Lincoln, a Masters in Software Engineering from Seattle University, and a Masters in Computer Science from the University of Maryland. I specialize in building and installing tools, frameworks, and processes that improve developer productivity and product quality. I am currently based in the Seattle area.

Comments and Discussions

 
QuestionWinforms forms design Pin
robertd90310-Nov-21 4:31
robertd90310-Nov-21 4:31 
AnswerRe: Winforms forms design Pin
Southmountain1-Jul-22 8:14
Southmountain1-Jul-22 8:14 
GeneralMy vote of 5 Pin
Jigar Sangoi14-Aug-13 9:27
Jigar Sangoi14-Aug-13 9:27 
Generalit is a really good one Pin
Binqing30-Oct-07 8:33
Binqing30-Oct-07 8:33 
GeneralRe: it is a really good one Pin
Rikardo Patreze15-Jun-09 13:18
Rikardo Patreze15-Jun-09 13:18 
GeneralC#: Cannot Bind to ComboBox with Master/Detail Pin
trantrum9-Jul-07 0:11
professionaltrantrum9-Jul-07 0:11 
GeneralWeird Issue with ComboBox binding Pin
Umair Ahmad4-Apr-07 2:28
Umair Ahmad4-Apr-07 2:28 
GeneralYour solution does not work Pin
ljupcoaneta3-Dec-06 16:42
ljupcoaneta3-Dec-06 16:42 
GeneralRe: Your solution does not work Pin
Scott McMaster11-Dec-06 15:31
Scott McMaster11-Dec-06 15:31 
GeneralProgramatically selecting an item by value in the list Pin
mrstu8-Nov-06 12:25
mrstu8-Nov-06 12:25 
GeneralRe: Programatically selecting an item by value in the list Pin
mrstu8-Nov-06 15:37
mrstu8-Nov-06 15:37 
Questionis there a way to deselect selected item on doubleclick ? Pin
pepedupuis21-Sep-06 23:06
pepedupuis21-Sep-06 23:06 
Generaldefault selected item problem with datasource Pin
pepedupuis19-Sep-06 1:50
pepedupuis19-Sep-06 1:50 
GeneralRe: default selected item problem with datasource Pin
pepedupuis19-Sep-06 3:09
pepedupuis19-Sep-06 3:09 
GeneralRe: default selected item problem with datasource Pin
Scott McMaster19-Sep-06 14:12
Scott McMaster19-Sep-06 14:12 
GeneralStrange with LISTbox and suggested code Pin
Greg Cronin13-Sep-06 12:35
Greg Cronin13-Sep-06 12:35 
GeneralRe: Strange with LISTbox and suggested code Pin
Scott McMaster14-Sep-06 13:49
Scott McMaster14-Sep-06 13:49 
GeneralRe: Strange with LISTbox and suggested code Pin
BrockmooreWallace18-Sep-06 9:22
BrockmooreWallace18-Sep-06 9:22 
GeneralRe: Strange with LISTbox and suggested code Pin
Greg Cronin18-Sep-06 10:45
Greg Cronin18-Sep-06 10:45 
QuestionRe: Strange with LISTbox and suggested code Pin
Beat Scholl6-Nov-06 2:09
Beat Scholl6-Nov-06 2:09 
GeneralRe: Strange with LISTbox and suggested code Pin
mrstu8-Nov-06 12:20
mrstu8-Nov-06 12:20 
Generalseems it still fires twice if you set the selectedvalue Pin
ferguslogic31-Jul-06 11:31
ferguslogic31-Jul-06 11:31 
It seems that the combobox still fires twice if you set the selectedvalue.

It appears that .net forces you to set the selectedValue property after you set the datasource and therefore the combobox fires the first time to set the selectedvalue to 0 and then again to set it to whatever you choose.

i.e
combo.valuemember = "customerid"
combo.displaymember = "customername"
combo.datasource = ds

combo.selectdvalue = myCustomer.customerid


this is really causing issues for me right now and I am trying to figure out how to work around it.>
GeneralRe: seems it still fires twice if you set the selectedvalue Pin
ferguslogic31-Jul-06 11:42
ferguslogic31-Jul-06 11:42 
GeneralProblem with displaying in ComboBox Pin
Michael P. Moore18-Apr-06 7:51
Michael P. Moore18-Apr-06 7:51 
GeneralPreventing the SelectedIndexChanged event on binding. Pin
jimmyb165129-Mar-06 3:33
jimmyb165129-Mar-06 3:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.