Displaying Data

Just as in a Windows Forms application in the desktop Framework, the controls in the System.Windows.Forms namespace in the Compact Framework can be used to display richly file, XML, or relational data on the device. This can be done using both data binding and manual binding.

Data Binding

graphics/key point_icon.gif

As in the desktop Framework, the forms programming model of the Compact Framework supports data binding through the use of PropertyManager and CurrencyManager objects exposed through the BindingContext property of the Control class from which Form is derived. In this architecture, any class that implements the IList interface[11] can be bound to single and multivalue controls, such as the TextBox and ListBox, respectively. This includes not only the DataTable and DataView classes in ADO.NET but also arrays, collections, and even strongly typed collection classes.

[11] This is typically accomplished by a data source by implementing the IBindingList interface, which itself implements the IList interface and supports additional features such as notification through the ListChanged event.

Simple Binding

To use simple binding, a developer need only add Binding objects to the DataBindings collection exposed by a control. For example, in order to bind a Label control and several TextBox controls to columns in a DataTable, execute the following code (where dt is the DataTable):


lblName.DataBindings.Add("Text", dt, "Name")
lblPos.DataBindings.Add("Text", dt, "Position")
txtAB.DataBindings.Add("Text", dt, "AB")

In this case, each column is bound to the Text property of the respective control. Alternatively, the simple controls could use a DataGrid, ListBox, or other complex control as the data source so that, if the data source of the complex control changes, the simple controls will also be changed.

When these bindings are created, the form creates a CurrencyManager object based on the data source specified and places it in the BindingContext collection. The CurrencyManager abstracts the position of the data source and is responsible for updating the controls as the position changes. In this way, a form may contain multiple CurrencyManager objects, all independently positioning the same data source. To change the position programmatically, a developer can then simply use the Position property of the CurrencyManager:


Me.BindingContext(dt).Position += 1

On simple controls such as the TextBox, the contents of the control can be validated using the Validated and Validating methods. These methods will fire when the CausesValidation property of the control that is navigated to (not the control being validated) is set to True. The Validating event is where developers put their validation code, while the Validated event fires only if the validation succeeds (the Validating event returns False in its CancelEventArgs parameter) and can be used to reset error messages. Unfortunately, because the Compact Framework does not include the ErrorProvider control, developers will need to create their own custom error displays.

NOTE

Of course, deriving from the standard controls such as TextBox to create controls that accept only specific input, as shown in Chapter 2, is an even better approach because it alleviates the user from responding to error messages.


Complex Binding

Controls that can display multiple sets of information often expose a DataSource property and, therefore, support complex binding. In the Compact Framework, this includes the ListBox, ComboBox, and DataGrid controls.

The ListBox and ComboBox, which display only a single value at a time, also expose the DataMember and ValueMember properties to specify the member to display and the value of the displayed member, respectively. For example, to bind a table of a DataSet to a ComboBox control, a developer would do the following:


cbPlayers.DataSource = ds.Tables(0)
cbPlayers.DisplayMember = "Name"
cbPlayers.ValueMember = "Number"

In this case the Name column of the DataTable will be displayed to the user and returned through the SelectedText property, while the Number column will be tracked and returned using the SelectedValue property. In order to allow the control to display the current player for a particular row in a DataTable (for example, dt, as shown previously), the SelectedValue property of the control can be bound using the DataBindings collection:


cbPlayers.DataBindings.Add("SelectedValue", dt, "Number")

graphics/key point_icon.gif

The DataGrid control is more interesting because it can display more information for each set of data it is bound to and need only have its DataSource property set, as shown in Figure 3-4. However, unlike in the desktop Framework, the Compact Framework DataGrid control does not include a drilldown option for displaying detail rows in a child table linked to a master table through a DataRelation. In addition, the grid is not editable and would be difficult to edit in any case on a small device. As a result, a typical strategy employed by developers is to allow a user to tap a row on a DataGrid and then display the detail data on a separate area of the form or more likely on a second form.

Figure 3-4. Displaying a DataGrid. This screen shot shows a DataGrid bound to a DataTable displaying box score information.

graphics/03fig04.jpg

The latter technique can be easily accomplished by creating a second form with the appropriate controls to edit the current row. These controls can then be bound to the same data source (available globally within the application) as the DataGrid discussed previously. The key, however, is to set the BindingContext property of the form with the detail controls to the BindingContext of the form containing the DataGrid. In this way the detail form will always show the current row in the DataGrid. This code can then be placed in the Click event of the DataGrid, as follows:


Private Sub myGrid_Click(ByVal sender As System.Object, _
  ByVal e As System.EventArgs) Handles myGrid.Click

    Dim f As New frmDetail()
    f.BindingContext = Me.BindingContext
    f.ShowDialog()

End Sub

In this way, the developer needn't write any code to synchronize the CurrencyManager objects on the respective forms or write any manual binding code.

TIP

On the detail form, a developer will want to use the InputPanel control to allow the user to use the SIP to enter information into the controls.


Strongly Typed Collections

Complex data binding can be used not only with ADO.NET (DataTable, DataView, or DataViewManager), but with any object that supports the IList interface. This includes arrays and other collection classes such as the ArrayList. For example, the ArrayList exposed by the VisitingPlayers field in the Scoresheet class shown previously can be bound directly to a DataGrid, as shown in this snippet:


myGrid.DataSource = s.VisitingPlayers

In this case the public properties of the PlayerLine objects stored in the ArrayList are bound to the grid. Public fields will not be bound. The only other caveat is that all of the objects in the ArrayList must be of the same type.[12] In order to ensure that this is the case, a developer can create a strongly typed collection class that accepts only objects of a certain type. The advantages to this approach are that developers can work with objects and collections, which is more straightforward than using ADO.NET, and they can take advantage of IntelliSense in VS .NET. For more information on how this is done in .NET, see the article on www.Builder.com in the "Related Reading" section of this chapter.

[12] Actually, they all must be of the type of the first object in the collection or of a type derived from that type. If not, the row will display, but it will contain an "X."

Manual Binding

Of course, some controls do not support complex binding. This is the case, for example, with the ListView and TreeView controls that developers often use to display hierarchical data. In order to use these controls with data, a developer must manually bind them. For example, the code in Listing 3-10 displays box score data contained in an ArrayList in a ListView control passed into the method.

Listing 3-10 Manual Binding. This method shows how a developer can manually bind an ArrayList to a ListView control.
Private Sub _showBoxScore(ByVal team As ArrayList, ByVal lv As ListView)

    Dim p As PlayerLine
    Dim o As Object

    For Each o In team
        p = DirectCast(o, PlayerLine)
        Dim lvItem As New ListViewItem(p.Name)
         With lvItem
            .SubItems.Add(p.AB)
             .SubItems.Add(p.R)
            .SubItems.Add(p.H)
            .SubItems.Add(p.RBI)
            .SubItems.Add(p.D)
            .SubItems.Add(p.T)
            .SubItems.Add(p.HR)
            .SubItems.Add(p.K)
            .SubItems.Add(p.BB)
            .SubItems.Add(p.LOB)
            .SubItems.Add(p.Pitches)
        End With
        lv.Items.Add(lvItem)
    Next

End Sub

Note that because the ArrayList contains items of type Object; each object in the collection must be cast to PlayerLine using the DirectCast statement. Subitems are added to each ListViewItem to display the properties of the player. A screen shot showing the completed ListView is shown in Figure 3-5. Note that each team's box score is displayed on a separate tab using the TabControl, a very appropriate strategy for smart devices with little screen real estate.

Figure 3-5. Displaying a ListView. This screen shot shows two ListView controls hosted in a tab control used to display box score information.

graphics/03fig05.jpg

NOTE

From a performance perspective we've found that when using an ArrayList, manual binding is faster than data binding. Data binding is further slowed when using the DisplayMember property. Of course, your mileage may vary.