Introduction
This article aims at introducing the concept of Data Binding with WPF, presenting – as example – a DataGrid
population through custom List(Of)
, and it's modified through controls like TextBox
es as DatePicker
, which will take their data source from the grid itself. It is widely known that one of the major features introduced with Windows Presentation Foundation resides in the ability to easily connect the user controls (and objects) with data sources of different complexities, leaving the task of their representation to the framework (on which you can always apply later customizations, nonetheless). As an example of a possible usage scenario, we assume here to have a grid expose data from a given customized structure and - later - to make possible its variation through simple text controls.
An Example Class
Let’s suppose we have an hypothetical class Articolo
that will allow us to define products to store, in their terms of product’s code (Codice
), a description (Descrizione
), and expiration date (DataScadenza
). We’ll declare that class – reductive, but useful for our means – as follows:
Public Class Articolo
Dim _codice As String
Dim _descrizione As String
Dim _datascadenza As Date
Public ReadOnly Property Codice As String
Get
Return _codice
End Get
End Property
Public Property Descrizione As String
Get
Return _descrizione
End Get
Set(value As String)
_descrizione = value
End Set
End Property
Public Property DataScadenza As Date
Get
Return _datascadenza
End Get
Set(value As Date)
_datascadenza = value
End Set
End Property
Public Sub New(codart As String, desart As String)
_codice = codart
_descrizione = desart
_datascadenza = New Date(Now.Year + 1, Now.Month, Now.Day)
End Sub
End Class
In class constructor, only the parameters for code and description are requested. The expiration date is set a year in the future. Properties which define the class are editable, with the only exception of Codice
(product code), which cannot be altered after its creation.
Next, we’ll populate a list of products with some example data, useful for next steps. Let’s suppose we have a WPF window named MainWindow.xaml. We’ll create an event manager for the Loaded
event, such as the XAML resembles as the following:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ERP_test" x:Class="MainWindow"
Title="MainWindow" Height="269"
Width="563" Loaded="Window_Loaded">
</Window>
The code behind the event will be:
Dim prodotti As List(Of Articolo)
Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
prodotti = New List(Of Articolo)
prodotti.Add(New Articolo("Prodotto01", "ARTICOLO TEST"))
prodotti.Add(New Articolo("Prodotto02", "PROVA"))
prodotti.Add(New Articolo("Prodotto03", "NESSUNA DESCRIZIONE"))
End Sub
Simply enough, we’ll have now a List(Of Articolo)
global to the window and named prodotti
, which contains three products at first execution. That structure represents the data source we’ll expose, through binding, in a DataGrid
. Let’s see how.
Data Binding on DataGrid
In our window, we’ll add a DataGrid
control, named DataGrid1
. Having our structure defined code-side, we must work on this side to link the control to its data source. The binding will be obtained using the control’s ItemsSource
property. Our Loaded
event will be modified as follows:
Dim prodotti As List(Of Articolo)
Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
prodotti = New List(Of Articolo)
prodotti.Add(New Articolo("Prodotto01", "ARTICOLO TEST"))
prodotti.Add(New Articolo("Prodotto02", "PROVA"))
prodotti.Add(New Articolo("Prodotto03", "NESSUNA DESCRIZIONE"))
DataGrid1.ItemsSource = prodotti
End Sub
ItemsSource
is indeed the property (of System.Collections.IEnumerable
type) through which we’ll set (or read) a data collection, the latter used for the control’s content generation (in our case, the grid). The binding on DataGrid
is up: from a first execution of our program, we’ll see an output like this:
Data Binding between Controls: Datagrid on TextBox and DatePicker
As previously described, we are not interested in having a mere list of products. We want to be able to view the product’s details scrolling that list. In realizing this kind of implementation, we’ll use some TextBox
es, executing a binding between them (as recipients) and the DataGrid
(the source), allowing recipient controls (TextBox
, or DatePicker
in case of expiration date) to modify their source in turn. Let’s modify our window as follows:
The properties which expose the control’s value in TextBox
and DatePicker
are, respectively, Text
and SelectedDate
. It is therefore necessary to set the data binding on these properties. Essentially, after individuating the property that will receive a particular data, we must indicate its source, and other information (some of which are optional) that will determine how those data are exchanged. In our case, desiring to modify the content of DataGrid
through the variation of recipient controls, we must use some kind of bidirectional approach.
As operations to be executed between controls, it is possible to intervene directly on XAML code, indicating how TextBox
es and the DatePicker
must be linked to DataGrid
. The code pertinent to the three controls will be modified as follows:
<TextBox HorizontalAlignment="Left" Height="23" Margin="445,42,0,0"
TextWrapping="Wrap" VerticalAlignment="Top" Width="181"
Text="{Binding SelectedItem.Codice,ElementName=DataGrid1 , Mode=OneWay }"/>
<TextBox HorizontalAlignment="Left" Height="23" Margin="445,70,0,0"
TextWrapping="Wrap" VerticalAlignment="Top" Width="181"
Text="{Binding SelectedItem.Descrizione,ElementName=DataGrid1 , Mode=TwoWay }"/>
<DatePicker HorizontalAlignment="Left" Margin="445,98,0,0"
VerticalAlignment="Top" Width="181"
SelectedDate="{Binding SelectedItem.DataScadenza,ElementName=DataGrid1 ,Mode=TwoWay }"/>
As mentioned, properties which expose a certain data must be set indicating the source of the data, and the mode in which data must be retrieved. We take case #1 (TextBox
which exposes product’s code, Codice
) as an example:
In this case, we’ll see the source control is set on DataGrid1
, from which we read Codice
property (belonging to Articolo
class) of the currently SelectedItem
. Binding’s mode is mono-directional, that is from source (DataGrid1
) to destination (TextBox
), and not vice versa: in fact, we defined Codice
property as read-only (see snippet 1), and any attempt to change its value will raise an exception.
Similarly, in the two remaining cases, we’ll have always DataGrid1
as source control, referenced properties will be Descrizione
(description) and DataScadenza
(expiration date), but the binding’s mode will be TwoWay
, or bidirectional: modifying the control’s content will result in updating its source property/control, or DataGrid1
(and, for extension, the list connected to it).
Running our program, and trying to position ourselves on the second record (for example) and modifying the data, we can verify the proper update of linked control’s data:
Alternative Method for DataGrid Population
We saw here a method for populating a DataGrid
, which involves a List(Of <Our_Class>)
. Speaking of WPF binding flexibility, there are other methods through which we could populate the contents of a control. For example, our class Articolo
could be referred directly into the XAML of our grid. If we desire to give our DataGrid
some records to start with, we could do as follows:
<DataGrid Name="DG1" HorizontalAlignment="Left"
Height="153" Margin="10,10,0,0"
VerticalAlignment="Top" Width="305"
xmlns:local="clr-namespace:WinTest" >
<DataGrid.Columns >
<DataGridTextColumn Header="Codice"
Binding="{Binding Codice}"/>
<DataGridTextColumn Header="Descrizione"
Binding="{Binding Descrizione}"/>
<DataGridTextColumn Header="Data di scadenza"
Binding="{Binding DataScadenza}"/>
</DataGrid.Columns>
<local:Articolo Codice="CODE_01"
Descrizione="MSDN TEST" DataScadenza="2014-12-01" />
</DataGrid>
We use the xmlns:local
keyword to specify our class as a reference (in my case, WinTest
). This way, we could use into XAML each class defined within our namespace (in my case, Articolo
). We then define three DataGridColumns
, each one binded with a specific property of our class. Then, with the tag local
, we could proceed in defining a variable number of records, setting each of its parameters. The code above will produce the following window:
From a situation like that, we could add other rows code-side, using the Item
collection exposed by DataGrid
. Supposing our DataGrid
's name is DataGrid1
, we could, for example, add a second row to it, using in the Loaded
event something like that:
DataGrid1.Items.Add( New Articolo("CODE_02", "MSDN TEST 2") )
Please note: As far as there are dynamically or statically generated rows, i.e., the Item
collection is not empty, it's not possible to use the ItemsSource
property discussed above. In order to bind a structure to our DataGrid
, we must first empty Item
collection (for example, with something like: DataGrid1.Items.Clear()
), and only after that, we'll be able to successfully set the ItemsSource
property.
History
- 2015-01-05: Added source code
- 2015-01-04: First release for CodeProject