Simple Related Object List Binding

Simple Related Object List Binding

Rate This
  • Comments 4

I thought I'd post a quick how-to here based on some questions that came up in the forums like this one. The question is how do we relate two lists of data together so that when we select a "parent" object we can get automatic filtering on the related items in a Winform. For instance, one person in the forums was trying to put a list of states in a ListBox and when you selected one, a list of cities in that state would populate a CheckedListBox.

The answer lies in the BindingSource object. BindingSources do all of the heavy lifting for you when it comes to filtering and even sorting and editing. The key to using these though is we need to set up our object model correctly, in this case State and City, so that they are related properly.

In our example, we create two classes, State and City and set them up so that State has an internal list of Cities. Then you can use data binding on the controls by setting up two BindingSources that are related -- this will manage the position and the contents of the related lists automatically.

So our State and City classes need to look like this:

Public Class State

    Private m_cities As New List(Of City)

 

    Sub New(ByVal state As String)

        m_State = state

    End Sub

 

    Private m_State As String

    Public Property State() As String

        Get

            Return m_State

        End Get

        Set(ByVal value As String)

            m_State = value

        End Set

    End Property

 

    Public ReadOnly Property Cities() As List(Of City)

        Get

            Return m_cities

        End Get

    End Property

 

    Public Overrides Function ToString() As String

        Return Me.State

    End Function

 

End Class

 

 

Public Class City

    Sub New(ByVal city As String)

        m_City = city

    End Sub

 

    Private m_City As String

    Public Property City() As String

        Get

            Return m_City

        End Get

        Set(ByVal value As String)

            m_City = value

        End Set

    End Property

 

    Public Overrides Function ToString() As String

        Return Me.City

    End Function


End
Class

Then we can set up our data and two BindingSources that we relate together. We do this by setting the CitiesBindingSource.DataSource property to the StateBindingSource and then setting its DataMember to the Cities property on the State class. Once that's set up then we set the BindingSources to the DataSource property of our controls:

Private States As New List(Of State)

Private StatesBindingSource As New BindingSource

Private CitiesBindingSource As New BindingSource

 

 

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

 

    Dim ny As New State("New York")

    ny.Cities.Add(New City("New York"))

    ny.Cities.Add(New City("Rochester"))

 

    Dim vt As New State("Vermont")

    vt.Cities.Add(New City("Rutland"))

    vt.Cities.Add(New City("South Burlington"))

 

    Dim id As New State("Idaho")

    id.Cities.Add(New City("St. Albans"))

 

    States.Add(ny)

    States.Add(vt)

    States.Add(id)

 

 

    Me.StatesBindingSource.DataSource = Me.States

    Me.CitiesBindingSource.DataSource = Me.StatesBindingSource

    Me.CitiesBindingSource.DataMember = "Cities"

    Me.ListBox1.DataSource = Me.StatesBindingSource

    Me.ListBox2.DataSource = Me.CitiesBindingSource

 

End Sub

Now all we need to do is move the position in the StatesBindingSource when the SelectedIndex changes in the first ListBox. This will automatically filter the list of cities for us:

Private Sub ListBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ListBox1.SelectedIndexChanged

 

    Me.StatesBindingSource.Position = Me.ListBox1.SelectedIndex

 

End Sub

This is really easy when you use list controls that have a Datasource property like a Combobox, ListBox or a DataGridView. However, the CheckedListBox does not have one so we have to fill the items a little more manually. We still use the CitiesBindingSource though because it will still contain our filtered list of items as the StatesBindingSource position changes. So we just need to add this code:

 

Private Sub ListBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ListBox1.SelectedIndexChanged

 

    Me.StatesBindingSource.Position = Me.ListBox1.SelectedIndex

 

    'Clear out the cities in the city checkedlistbox first

    CheckedListBox1.Items.Clear() 

   

    'This is necessary only because the CheckedListbox does not have

    ' a Datasource property. Any control with a Datasource property

    ' could simply be set to Me.CitiesBindingSource

    For Each c As City In Me.CitiesBindingSource.List

        Me.CheckedListBox1.Items.Add(c)

    Next

End Sub

So now when we move through our ListBox1, we will get automatic filtering on the rest of our controls.

Leave a Comment
  • Please add 7 and 8 and type the answer here:
  • Post
  • I've been noticing a lot of questions on the forums related to Winforms data binding and the ComboBox

  • Hi Beth.

    I have typed out the same code while following this video. When I try to launch the start up form I get an error (Me.myStates.Add(NY)) telling me that a null reference was unhandled. Have I missed something? I am using VS2005/VB

    System.NullReferenceException was unhandled

     Message="Object reference not set to an instance of an object."

     Source="ObjectBindingSample"

     StackTrace:

          at ObjectBindingSample.Form1.Form1_Load(Object sender, EventArgs e) in C:\Users\Jay\Documents\Visual Studio 2005\Projects\SampleObjectClasses\ObjectBindingSample\Form1.vb:line 13

          at System.EventHandler.Invoke(Object sender, EventArgs e)

          at System.Windows.Forms.Form.OnLoad(EventArgs e)

          at System.Windows.Forms.Form.OnCreateControl()

          at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)

          at System.Windows.Forms.Control.CreateControl()

          at System.Windows.Forms.Control.WmShowWindow(Message& m)

          at System.Windows.Forms.Control.WndProc(Message& m)

          at System.Windows.Forms.ScrollableControl.WndProc(Message& m)

          at System.Windows.Forms.ContainerControl.WndProc(Message& m)

          at System.Windows.Forms.Form.WmShowWindow(Message& m)

          at System.Windows.Forms.Form.WndProc(Message& m)

          at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)

          at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)

          at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

          at System.Windows.Forms.SafeNativeMethods.ShowWindow(HandleRef hWnd, Int32 nCmdShow)

          at System.Windows.Forms.Control.SetVisibleCore(Boolean value)

          at System.Windows.Forms.Form.SetVisibleCore(Boolean value)

          at System.Windows.Forms.Control.set_Visible(Boolean value)

          at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)

          at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)

          at System.Windows.Forms.Application.Run(ApplicationContext context)

          at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.OnRun()

          at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.DoApplicationModel()

          at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.Run(String[] commandLine)

          at ObjectBindingSample.My.MyApplication.Main(String[] Args) in 17d14f5c-a337-4978-8281-53493378c1071.vb:line 81

          at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)

          at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)

          at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()

          at System.Threading.ThreadHelper.ThreadStart_Context(Object state)

          at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)

          at System.Threading.ThreadHelper.ThreadStart()

  • Hmmm cancel that, after reading it 50 times I still didnt notice I'd left out "new" in myStates = New BindingList(Of State)()

  • Beth,

    Very elegant code.

    Congratulations!

Page 1 of 1 (4 items)