For this tutorial you will need to have the Visual Basic PowerPack installed – You can check if you have this be looking in the toolbox and checking for the section named Visual Basic PowerPacks. If you do not have it, follow the instructions in this YouTube video http://youtu.be/Ox9w_LLyw38 which also provides a good idea of the normal setup and operation of the DataRepeater with a database.
I am using .NET framework 4 in Visual Studio 2010. I believe this tutorial should work with Studios 2008 and 2012 and .NET down to 2.0 as well, but outside of that you may have to resort to trial and error.
When using data bound controls we don’t always want to bind them to the usual database sourced DataTables. In this tutorial we will see how to bind a list of objects to a DataRepeater so that changes to the list will be reflected in the DataRepeater.
Following on from this we will see how this needs to be altered when changing the list from a non-UI thread in a multi-threaded application.
Setup the project and form
To begin with you should start a new WindowsFormsApplication project. When your project is opened, add a Button control and a DataRepeater control to the form. Next add three Labels to the DataRepeater’s ItemTemplate.
The form should now look something like this:
Attachment 103967
Open the code view of the form (press F7). Then, at the top of the code view we should see the empty Form1 class already there as normal.
Create the List
Inside the Form1 class add the following sub class:
This will create the class of the objects placed on the list and when you use this for your own application this should be changed to whatever type of object you need. The last line creates an instance of the class called TestItem.VB Code:
Public Class TestListItem Public Property TestKey As UInteger Public Property TestString As String Public Property TestVar As UInteger End Class Dim TestItem As New TestListItem
The next line to add will create the list itself, called TestList, that lists objects of the same type of class that we just created above.
You can see that we also created another variable, called TestVarInt that holds some random number; we will come back to this soon.VB Code:
Dim TestList As New List(Of TestListItem) Dim TestVarInt As New System.Random()
We have now created a class and a list of objects of that class, but it has no objects currently listed. So time to add some data!
Add some items to the List
Continue by adding the Form.Load event handler:
Here we start by assigning a new object to TestItem (ln 3) and then setting each of its members to some data (lns 4-6). You can see that used the random number variable to set one of the members to some random number between 1 and 10. Once populated the item is added to the list (ln 7). This is then done a second time with different data to create and add a second list item (lns 9-13). Finally we show the form (ln 16).VB Code:
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load ' Create and populate 2 items and add them to the List. TestItem = New TestListItem TestItem.TestKey = 0 TestItem.TestString = "TestItem1" TestItem.TestVar = TestVarCount.Next(1, 10) TestList.Add(TestItem) TestItem = New TestListItem TestItem.TestKey = 1 TestItem.TestString = "TestItem2" TestItem.TestVar = TestVarCount.Next(1, 10) TestList.Add(TestItem) ' Show the Form. Me.Show() End Sub
Bind the List to the DataRepeater
This has populated the list with items which are objects that have some data assigned to their members. Now we need to bind the list to the DataRepeater. Do this by inserting the following code before the form is shown.
Label1, 2 and 3 are the three labels we added to the DataRepeater ItemTemplate. The first three lines (lns 2-4) of this new code tells the labels what parts of the list items they should take notice of.VB Code:
' Bind list item properties to the DataRepeaterItem controls. Label1.DataBindings.Add("Text", TestList, "TestKey") Label2.DataBindings.Add("Text", TestList, "TestString") Label3.DataBindings.Add("Text", TestList, "TestVar") DataRepeater1.DataSource = TestList
I.E we add a binding to the label controls to the list item members.
The first argument to the Label.Databindings.Add() method sets the property of the control that is to be bound, the second argument specifies the DataSource, which in our case is our list, and the third argument specifies the data member of the DataSource that is to be bound. So in the case of Label1 we would like its .Text property to be bound to the TestKey member of the items on the TestList.
The final line (ln 5) of the new code sets the DataSource of the DataRepeater itself, our TestList.
If we run our program now we should see the DataRepeater shows the two items we added to the list, YAY!
Attachment 103969
Changing the List
Well and good, but what if we would like to add, change or remove any of the items on the list? This is where our Button comes in.
Back in the design view of our Form, double-click on the Button control. This should switch us back to the code view with a new empty Button1.Click even handler added for us. Before we add anything to the handler we need to add a few new things to the top of our Form class members at the top.
As shown below, add:
- The enum definition, named ListAction, with the three possible actions (lns 3-7).
- The ListAction type variable, named Action and initialised to ADD (ln 18).
- The unsigned integer, named TestKeyCount and initialised to 0 (ln 19).
The top of our Form1 class should now look like the below
Now head back to our empty Button1.Click event handler and add code to create (ln 3) and populate a new object (lns 6-11); nearly the same as we did in the Form1.Load event handler:VB Code:
Public Class Form1 Enum ListAction ADD REMOVE CHANGE End Enum Public Class TestListItem Public Property TestKey As UInteger Public Property TestString As String Public Property TestVar As UInteger End Class Dim TestItem As New TestListItem Dim TestList As New BindingList(Of TestListItem) Dim TestVarInt As New System.Random() Dim Action As ListAction = ListAction.ADD Dim TestKeyCount As UInteger = 0
In this case however we have set the TestKey member of the object (ln 9) to the new unsigned integer we just created at the top. You can also see that we are incrementing this count value every time the button is clicked if the Action variable is set to the ADD action (lns 6-8).VB Code:
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click ' Create new item. TestItem = New TestListItem() ' Populate New item. If Action = ListAction.ADD Then TestKeyCount += 1 End If TestItem.TestKey = TestKeyCount TestItem.TestString = "ThreadItem" TestItem.TestVar = TestVarInt.Next(1, 10) ' Call sub to do perform some action on the list. DoListAction(TestItem) End Sub
Instead of calling the List.Add method, the last line (ln 14) instead calls a new sub, named DoListAction and passes our new object to it as an argument.
This is because there are multiple operations that could be performed on our list. As you may have guessed we will be looking at adding, changing and removing items. In this case we will separate this work from the event handler into the DoListAction sub.
We will create this sub outside of the two current event handlers (Form1.Load and Button1.Click), but still inside the Form1 class; I have put it at the bottom of the class as follows:
Inside the sub we check the Action variable to see which action is required (lns 3, 9 & 15) and then use the argument passed as the listItem parameter (our newly created object) to perform this action (lns 5, 11 & 18). After the action is performed we then make sure the action flag is changed (lns 7, 13 & 20) ready for a different action the next time the Button is clicked.VB Code:
Private Sub DoListAction(ByVal listItem As Object) ' Check which action to do. If Action = ListAction.ADD Then ' Add the item to the end of the list. TestList.Add(listItem) ' Change the action flag Action = ListAction.CHANGE ElseIf Action = ListAction.CHANGE Then ' Change the last item on the list. TestList.Item(TestList.IndexOf(TestList.Last)) = listItem ' Change the action flag. Action = ListAction.REMOVE ElseIf Action = ListAction.REMOVE Then ' Remove the last item on the list. ' We dont actually need the item argument in this case. TestList.Remove(TestList.Last) ' Change the action flag Action = ListAction.ADD End If End Sub End Class
If we try running our application now, we would hope that clicking the Button would add a third item to the list, change it and then remove it. So go ahead and run it.
OH NOES! It doesn't work. So what’s going wrong? Well if we checked the TestList in the watch window while we were running our application we would see that the changes are being made to our list when we click the Button, the changes just aren't propagating to the DataRepater.
Fixing the binding
This is because the binding is only “one-way”, that is, if we had used text boxes instead of labels in the DataRepeater ItemTemplate and allowed the user to edit the content of those text boxes, the binding would ensure that those changes propagated from the DataRepeater to the list. What we want at the moment though, is for the binding to work the other way around for us.
It isn’t currently working like this for us because there is no way for the DataRepeater to “know” when the list has been changed.
What we need to do is change the type of collection we are using to one specifically suited for alerting controls bound to its data to any changes in that data, i.e. one that surfaces events that notify when the list has been changed.
Currently the collection we are using is a list:
It turns out all we need to do in this case is change our List(Of T) to a BindingList(Of T).VB Code:
Dim TestList As New List(Of TestListItem)
For this we will need to import System.Componentmodel at the top of the file, then change the list declaration like so:
Run the application and, bada-bing, bada-boom, the DataRepeater updates with respect to changes to the bound List when we press the Button.VB Code:
Dim TestList As New BindingList(Of TestListItem)
The first click of the Button adds a third item, the second click changes it (look at Label3, this is our random number at work) and a third click removes it again.
In the next post we will look at what changes we may need to make if we were using this in a multi-threaded application.
Below is the final code for this post:
VB Code:
Imports System.ComponentModel Public Class Form1 Enum ListAction ADD REMOVE CHANGE End Enum Public Class TestListItem Public Property TestKey As UInteger Public Property TestString As String Public Property TestVar As UInteger End Class Dim TestItem As New TestListItem Dim TestList As New BindingList(Of TestListItem) Dim TestVarInt As New System.Random() Dim Action As ListAction = ListAction.ADD Dim TestKeyCount As UInteger = 0 Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load ' Create and populate 2 items and add them to the List. TestItem = New TestListItem TestItem.TestKey = 0 TestItem.TestString = "TestItem1" TestItem.TestVar = TestVarInt.Next(1, 10) TestList.Add(TestItem) TestItem = New TestListItem TestItem.TestKey = 1 TestItem.TestString = "TestItem2" TestItem.TestVar = TestVarInt.Next(1, 10) TestList.Add(TestItem) ' Bind list item properties to the DataRepeaterItem controls. Label1.DataBindings.Add("Text", TestList, "TestKey") Label2.DataBindings.Add("Text", TestList, "TestString") Label3.DataBindings.Add("Text", TestList, "TestVar") DataRepeater1.DataSource = TestList ' Show the Form. Me.Show() End Sub Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click ' Create new item. TestItem = New TestListItem() ' Populate New item. If Action = ListAction.ADD Then TestKeyCount += 1 End If TestItem.TestKey = TestKeyCount TestItem.TestString = "ThreadItem" TestItem.TestVar = TestVarInt.Next(1, 10) DoListAction(TestItem) End Sub Private Sub DoListAction(ByVal listItem As Object) ' Check which action to do. If Action = ListAction.ADD Then ' Add the item to the end of the list. TestList.Add(listItem) ' Change the action flag Action = ListAction.CHANGE ElseIf Action = ListAction.CHANGE Then ' Change the last item on the list. TestList.Item(TestList.IndexOf(TestList.Last)) = listItem ' Change the action flag. Action = ListAction.REMOVE ElseIf Action = ListAction.REMOVE Then ' Remove the last item on the list. ' We dont actually need the item argument in this case. TestList.Remove(TestList.Last) ' Change the action flag Action = ListAction.ADD End If End Sub End Class
