What is DataGrid in C.


25.4 The Control Element »DataGrid«

The data binding capabilities of the WPF controls are really remarkable. Whether ListBox, ComboBox, TreeView or ListView, you can present the data in a completely individual layout as you wish. Describing all of them is beyond the scope of this book. Nevertheless, we should take a closer look at one control element: the DataGrid. This is certainly also the control with the most options.

A DataGrid displays the data of any data list in rows and columns. You can bind a DataGrid with its ItemsSource property to a data source that is described via DataContext. Here is the C # code that transfers the Products table of the Northwind database to the DataGrid:

gridProducts.DataContext = ds.Tables [0];

Now the DataGrid can be bound to the DataContext:

<DataGrid Name="gridProducts" ItemsSource="{Binding}" />

A direct transfer of the ADO.NET DataTable to the ItemsSource property fails because the DataTable cannot be converted into the IEnumerable interface. With this code, all required columns are automatically generated because the AutoGenerateColumns property of the DataGrid is set to true by default.

You can influence the creation of the columns by handling the AutoGeneratingColumn event, which is triggered when each column is created.

AutoGeneratingColumn = "gridProducts_AutoGeneratingColumn" />

Listing 25.33 Registering the AutoGeneratingColumn Event Handler

The EventArgs parameter of the event provides a lot of information in its properties in order to influence the column display. For example, the Column property describes the reference to the new column to be added, or PropertyName describes the column identifier. You can very easily prevent the creation of a specific column by setting the Cancel property of the EventArgs parameter to true or by renaming the header line in the DataGrid, as the following listing shows:

private void gridProducts_AutoGeneratingColumn (object sender,
DataGridAutoGeneratingColumnEventArgs e) {
if (e.PropertyName == "ReorderLevel" ||
e.PropertyName == "Discontinued")
e.Cancel = true;
else if (e.PropertyName == "ProductName")
e.Column.Header = "Article Name";
}

Listing 25.34 Influencing the Column Display with the AutoGeneratingColumn Event

Figure 25.7 shows the result of the measure from Listing 25.34.

Figure 25.7 Changing the columns in the »DataGrid«


25.4.1 Elementary properties of the »DataGrid«

There are numerous properties that can be used to influence the general layout of the table element at runtime. The most important properties can be found in the following table.

property description

ColumnHeaderHeight

The height of the row in which the column labels are displayed.

ColumnWidth

Specifies the default column width. This property is described by the DataGridLength type, which describes numerous options with its properties. The standard is SizeToHeader.

GridLinesVisibility

This property determines which lines should be displayed between the individual cells. The DataGridGridlines enumeration describes possible options with All, None, Vertical and Horizontal.

HeadersVisibility

This property defines the visibility of the row and column headers.

HorizontalGridLinesBrush

Specifies the brush with which the lines are drawn between the lines in the DataGrid.

RowBackGround or

AlternatingRowBackGround

Specifies the background with which each line and each alternating line should be drawn.

RowHeight

Specifies the height of each line.

VerticalGridLinesBrush

Specifies the brush with which the lines are drawn between the columns in the DataGrid.


25.4.2 Define columns

With the default setting AutoGenerateColumns = true - depending on the data type shown in the column - almost all columns are generated as TextBlock columns. This is not very flexible and does not allow any leeway. However, if you set AutoGenerateColumns to false, you have the option of choosing from a total of five column types the one that best suits your needs. The five types of columns mentioned are described in Table 25.4.

Column type description

DataGridTextColumn

This type describes DataGrid columns that are used to display text content. For this purpose, a TextBlock element is used for each cell, which is only exchanged for a TextBox if the cell is to be edited.

DataGridCheckBoxColumn

A CheckBox is displayed in each cell in the column. This type is also used automatically if the column describes a Boolean value.

DataGridHyperLinkColumn

A clickable link is displayed in the column.

DataGridComboBox

This column initially looks like a column of the DataGridTextColumn type. In the edit mode it becomes a ComboBox.

DataGridTemplateColumn

This column represents the most flexible variant. With a DataTemplate, it enables a free design option.

Let us look at an example of the use of some of the column types listed. We also use a table in the Northwind database for this purpose. This time, however, it won't be the Products table because it doesn't offer enough potential in this regard. Instead, let's work with the Orders table, which is shown in Figure 25.8 with the AutoGenerateColumns = true setting.

In the following, step-by-step structure of an individual display, we want to limit ourselves to a few columns in the table. We will mainly be interested in the columns ShippedDate, CustomerID and ShipVia, whose column type we want to adapt to common requirements. The ShippedDate column should be of the DataGridTemplateColumn type so that the user can use a date picker element that is more intuitive and easier to use to change the date in edit mode. The columns for CustomerID and ShipVia, however, should be represented by DataGridComboBox types.

Figure 25.8 The output of the "Orders" table

Customization of the column types

If you set the AutoGenerateColumn property of the DataGrid to false, it is up to you to specify the columns to be displayed in the DataGrid. You can now explicitly specify which columns in the table should be displayed and which column type they should be. All you have to do is fill the DataGrid.Columns property with the appropriate columns. This could look like the following listing, for example.

Title = "Orders" Height = "350" Width = "760">
<Grid>
AutoGenerateColumns = "False">
<DataGrid.Columns>
Binding = "{Binding OrderID}" />
Binding = "{Binding CustomerID}" />
Binding = "{Binding ShippedDate}" />
Binding = "{Binding ShipVia}" />
Binding = "{Binding ShipAddress}" />
Binding = "{Binding ShipCity}" />
Binding = "{Binding ShipCountry}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>

Listing 25.35 Simple Custom Column Generation

The listing is kept very simple, because only the standard column type DataGridTextColumn is used for the desired columns. Each column of the DataGrid is bound to a column of the Orders table with a binding. Basically, the only thing that is remarkable is that the column heading is explicitly specified in the table control with the header.

The column type »DataGridTemplateColumn«

First, let's look at the column in the DataGrid that will be bound to the ShippedDate column of the table. The delivery date is displayed in the column. Of course, this immediately suggests supporting the user's input through a DatePicker element in the edit mode. Since such a column type cannot be specified directly (see also Table 25.4), we have to use an object of the DataGridTemplateColumn type.

A DataGridTemplateColumn has two properties that allow us to differentiate between normal view mode and edit mode:

  • CellTemplate
  • CellEditingTemplate

Within each of the two properties mentioned, a DataTemplate object can be used to describe what is to be displayed within a cell of the relevant column. In the normal view it should be a TextBlock element, in the edit mode, as mentioned above, a DatePicker element.

<DataGridTemplateColumn Header="Lieferdatum">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<DatePicker SelectedDate="{Binding ShippedDate}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ShippedDate}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

Listing 25.36 Changing the ShippedDate Column

If you replace the old column definition with the one shown in Listing 25.36, the result looks quite acceptable when we double-click a cell in this column to switch to edit mode. A calendar opens and allows you to easily select a new date. However, the American display format of the date including the time will be disturbing, at least in the German-speaking area. This has not changed from what can already be seen in Figure 25.8.

To enable a date display that is common in German-speaking countries, we have to provide a converter class and implement the Convert method of the IValueConverter interface accordingly. To do this, the value transferred to the Convert method is converted to the DateTime type and the ToShortDateString method is then called. However, we have to take into account that the content of the cell content to be converted can be empty. The second method of the interface, ConvertBack, does not play a role here and therefore does not have to be implemented.

public class DateConverter: IValueConverter {
public object Convert (object value, Type targetType,
object parameter, CultureInfo culture) {
if (value.ToString () == "") return "";
return ((DateTime) value) .ToShortDateString ();
}
public object ConvertBack (object value, Type targetType,
object parameter, CultureInfo culture) {
return DateTime.Parse (value.ToString ());
}
}

Listing 25.37 The "Converter" class

In order to be able to use the converter, we create an object of the DateConverter type in the Resources section of the Window element. We must not forget to declare the namespace in which the class is located as the XML namespace. Then all that remains is to specify the converter for the TextBlock in the DataTemplate of the DataGridTemplateColumn.CellTemplate property.

xmlns: local = "clr-namespace: DataGridSample"
Title = "Orders" Height = "350" Width = "760">
<Window.Resources>
<local:DateConverter x:Key="dateConverter" />
</Window.Resources>
<Grid>
AutoGenerateColumns = "False">
<DataGrid.Columns>
[...]
<DataGridTemplateColumn Header="Lieferdatum">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<DatePicker SelectedDate="{Binding ShippedDate}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>

</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
[...]
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>

Listing 25.38 Additions to the XAML Code

The column type »DataGridComboBoxColumn«

Providing a DataGridComboBoxColumn is simpler than a DataGridTemplateColumn. We want to use this column type for the columns CustomerID and ShipVia in order to display the name of the customer or that of the supplier instead of identifiers that are of no interest to the user. In order to access the latter information, it is necessary to query the corresponding tables from the database. Again, ADO.NET classes are used for this, although the information would be more easily obtained with the EDM (Entity Data Model) of the Entity Framework.In practice, however, the Entity Framework has not yet become so widely accepted that its preference in this example would be justified. Here first the C # code to load the three necessary tables Orders, Customers and Shippers into the local memory.

public MainWindow () {
SqlConnection con = new SqlConnection (@ "...");
DataSet ds = new DataSet ();
SqlDataAdapter da = new SqlDataAdapter ("SELECT * FROM Orders", con);
da.Fill (ds, "Orders");
da = new SqlDataAdapter ("SELECT CustomerID," + "CompanyName FROM Customers", con);
da.Fill (ds, "Customers");
da = new SqlDataAdapter ("SELECT ShipperID, CompanyName" + "FROM Shippers", con);
da.Fill (ds, "Shippers");
InitializeComponent ();
gridProducts.DataContext = ds.Tables ["Orders"];
customerColumn.ItemsSource = ds.Tables ["Customers"]. DefaultView;
shippersColumn.ItemsSource = ds.Tables ["Shippers"]. DefaultView;
}

Listing 25.39 Loading the Tables with ADO.NET

The identifiers customerColumn and shippersColumn are the two DataGridComboBoxColumn objects.

It is enough if we only devote ourselves to the column with suppliers at this point. The customer column is built up in the same way. The two columns ShipperId and CompanyName are queried from the table of suppliers (Shippers). The ComboBox has the task of displaying the supplier's company name in the DataGrid instead of the ID of the supplier. For this purpose, the ItemsSource property of the object is linked to the table of suppliers in the local memory (see Listing 25.39).

We now set the DisplayMemberPath property to the column of the table that is to be displayed in the ComboBox, i.e. CompanyName. The SelectedValuePath property specifies the column of the bound table that is associated with a column in a related table. So here it is ShipperID. SelectedValueBinding is finally linked to the related table (i.e. Orders) and here again to the ShipVia column.

DisplayMemberPath = "CompanyName"
SelectedValuePath = "ShipperID"
SelectedValueBinding = "{Binding ShipVia}" />

Listing 25.40 The "DataGridComboBoxColumn" object of the "ShipVia" column

The definition of the second column for customers is very similar.

DisplayMemberPath = "CompanyName"
SelectedValuePath = "CustomerID"
SelectedValueBinding = "{Binding CustomerID}" />

Listing 25.41 The "DataGridComboBoxColumn" Object of the "CustomerID" Column

Figure 25.9 The »Delivery date« column in edit mode

The names and no longer the identifiers are now displayed in both columns. If the user changes to edit mode, all customers and suppliers are now displayed in the ComboBoxes.

Figure 25.9 shows the result of the user-defined columns. The delivery date column is in edit mode (because this is the most spectacular, i.e. pure showmanship ).


25.4.3 Show details of a line

The DataGrid also supports a special area that can be displayed below the currently selected row and is used to present details of the row to the user. You can see what this means in Figure 25-10.

Figure 25.10 Additional detailed view of the selected data line

The RowDetailsTemplate property of the DataGrid element is used to display this special area. Within the property, a data template describes the structure of the display area. To implement the RowDetail area from Figure 25.10, a border element is first described within the DataTemplate, which in turn contains a StackPanel in horizontal element alignment. TextBlock elements display the data obtained from bindings.

<DataGrid>
[...]
<DataGrid.RowDetailsTemplate>
<DataTemplate>
BorderThickness = "3" CornerRadius = "5">
<StackPanel Orientation="Horizontal">
Text = "Delivery details:" />
Text = "{Binding ShipAddress}" />
<TextBlock Foreground="Red" FontSize="14" Text=", "/>
Text = "{Binding ShipCity}" />
<TextBlock Foreground="Red" FontSize="14" Text=", "/>
Text = "{Binding ShipCountry}" />
</StackPanel>
</Border>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>

Listing 25.42 The DataGrid.RowDetailsTemplate Object

The display behavior can be influenced with the RowDetailsVisibilityMode property of the DataGrid. By default, this property is set to VisibleWhenSelected. This means that the detail area is displayed for the currently selected line in the DataGrid. You can also use the Visible setting, which means that all details for all lines are displayed immediately. The third alternative, Collapsed, means that the details are never displayed.

The complete example can be found on the book DVD under .. \ Chapter 25 \ DataGridSample.



your opinion

How did you like the Openbook? We always look forward to your feedback. Please send us your feedback as an e-mail to [email protected]