TheDeveloperBlog.com

Home | Contact Us

CSharp | Java | Python | Swift | GO | WPF | Ruby | Scala | F# | JavaScript

WPF DataGrid Tips

This WPF article uses the DataGrid type to display an editable table. It uses ItemsSource and adjusts DataGrid properties.

DataGrid. A DataGrid is a table. It has rows and columns.

We use properties on DataGrid, such as ItemsSource, to populate it. We also specify how users edit its data, and how it visually renders that data.

Based on:

.NET 4.5

ItemsSource. This example uses DataGrid and its ItemsSource property with a List. First, create a WPF project and drag a DataGrid to your window. In the XAML, please add the attribute "Loaded" to the "DataGrid" element.

Then: Visual Studio will create the DataGrid_Loaded event handler. This is where we assign the ItemsSource property.

Loaded: In the DataGrid_Loaded method, we create a List of Dog objects. These objects are automatically traversed by the DataGrid.

In the Dog class, please notice how there are two public properties: Name (a string) and Size (an int). In the resulting DataGrid, each property becomes a column header. So it has "Name" and "Size" columns.

Property

Example markup: XAML

<Window x:Class="WpfApplication14.MainWindow"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	Title="MainWindow" Height="350" Width="525">
    <Grid>
	<DataGrid
	    HorizontalAlignment="Left"
	    Margin="10,10,0,0"
	    VerticalAlignment="Top"
	    Loaded="DataGrid_Loaded"/>
    </Grid>
</Window>

Example code: C#

using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;

class Dog
{
    public string Name { get; set; }
    public int Size { get; set; }

    public Dog(string name, int size)
    {
	this.Name = name;
	this.Size = size;
    }
}

namespace WpfApplication14
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
	public MainWindow()
	{
	    InitializeComponent();
	}

	private void DataGrid_Loaded(object sender, RoutedEventArgs e)
	{
	    // ... Create a List of objects.
	    var items = new List<Dog>();
	    items.Add(new Dog("Fido", 10));
	    items.Add(new Dog("Spark", 20));
	    items.Add(new Dog("Fluffy", 4));

	    // ... Assign ItemsSource of DataGrid.
	    var grid = sender as DataGrid;
	    grid.ItemsSource = items;
	}
    }
}

The ItemsSource property accepts only an IEnumerable instance. Many types implement the IEnumerable interface—arrays and Lists are among them. If the IEnumerable uses objects, each object's properties are evaluated.

IEnumerable

Caution: ItemsSource does not support a List of arrays. It is better, and more efficient, to simply use objects (such as the Dog class).

Color. A grid is sometimes hard to read—the rows are hard to follow with your eyes. The DataGrid offers the AlternatingRowBackground attribute to make rows more distinguished in a visual way.

Tip: I recommend this option for most programs that use DataGrids of nontrivial size. The color shown (Coral) is not ideal.

Example markup 2: XAML

<Window x:Class="WpfApplication14.MainWindow"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	Title="MainWindow" Height="350" Width="525">
    <Grid>
	<DataGrid
	    HorizontalAlignment="Left"
	    Margin="10,10,0,0"
	    VerticalAlignment="Top"
	    AlternatingRowBackground="Coral"
	    Loaded="DataGrid_Loaded"/>
    </Grid>
</Window>

SelectedItems. How can you access the selected cells (or objects) in a DataGrid? With the SelectedItems property, we get a List of these cells. In this example, we populate the DataGrid with a List of Dog objects.

Then: We hook up the SelectionChanged event by adding the DataGrid_SelectionChanged method.

In SelectionChanged, we loop over the items in the SelectedItems List. And we cast each object to its original type (in this program, the Dog type). Finally we join those strings together and display them on the window.

Note: I apologize for the complex example. The important part to examine is the DataGrid_SelectedChanged method.

Example markup 3: XAML

<Window x:Class="WpfApplication14.MainWindow"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	Title="MainWindow" Height="350" Width="525">
    <Grid>
	<DataGrid
	    HorizontalAlignment="Left"
	    Margin="10,10,0,0"
	    VerticalAlignment="Top"
	    SelectionChanged="DataGrid_SelectionChanged"
	    Loaded="DataGrid_Loaded"/>
    </Grid>
</Window>

Example code 3: C#

using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;

class Dog
{
    public string Name { get; set; }
    public int Size { get; set; }

    public Dog(string name, int size)
    {
	this.Name = name;
	this.Size = size;
    }
}

namespace WpfApplication14
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
	public MainWindow()
	{
	    InitializeComponent();
	}

	private void DataGrid_Loaded(object sender, RoutedEventArgs e)
	{
	    // ... Create.
	    var items = new List<Dog>();
	    items.Add(new Dog("Fido", 10));
	    items.Add(new Dog("Spark", 20));
	    items.Add(new Dog("Fluffy", 4));
	    items.Add(new Dog("Rover", 100));
	    items.Add(new Dog("Mister Mars", 30));

	    // ... Assign.
	    var grid = sender as DataGrid;
	    grid.ItemsSource = items;
	}

	private void DataGrid_SelectionChanged(object sender,
	    SelectionChangedEventArgs e)
	{
	    // ... Get SelectedItems from DataGrid.
	    var grid = sender as DataGrid;
	    var selected = grid.SelectedItems;

	    // ... Add all Names to a List.
	    List<string> names = new List<string>();
	    foreach (var item in selected)
	    {
		var dog = item as Dog;
		names.Add(dog.Name);
	    }
	    // ... Set Title to selected names.
	    this.Title = string.Join(", ", names);
	}
    }
}

CellEditEnding. The DataGrid supports editing of its cells by the user. When the edit is finished by the user, the CellEditEnding event is triggered. And in this C# method, we can cancel the edit. This is useful if we detect an invalid entry.

Here: We use the program as in previous examples, but we omit some repeated parts of the source file.

Tip: We can get a TextBox object (and its Text property) from the EditingElement property of the DataGridCellEditEndingEventArgs.

By setting the Cancel property on the DataGridCellEditEndingEventArgs to true, the edit applied by the user will be rejected. Often, an error message (in a dialog box) would be helpful in this situation.

Example markup 4: XAML

<Window x:Class="WpfApplication14.MainWindow"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	Title="MainWindow" Height="350" Width="525">
    <Grid>
	<DataGrid
	    HorizontalAlignment="Left"
	    Margin="10,10,0,0"
	    VerticalAlignment="Top"
	    CellEditEnding="DataGrid_CellEditEnding"
	    Loaded="DataGrid_Loaded"/>
    </Grid>
</Window>

Example method: C#

private void DataGrid_CellEditEnding(object sender,
    DataGridCellEditEndingEventArgs e)
{
    // ... Get the TextBox that was edited.
    var element = e.EditingElement as TextBox;
    var text = element.Text;

    // ... See if the text edit should be canceled.
    //     We cancel if the user typed a question mark.
    if (text == "?")
    {
	// ... Cancel the edit.
	this.Title = "Invalid";
	e.Cancel = true;
    }
    else
    {
	// ... Show the cell value in the title.
	this.Title = "You typed: " + text;
    }
}

Scroll. Many functions of DataGrid can be automated. It even supports scrolling. We use the ScrollIntoView method for this. Please specify the object used as the source for the row (part of the ItemsSource IEnumerable collection).

Here: We add 100 objects to a List, and use that with ItemsSource. We scroll to the final element in the collection (the last row).

Note: Please see the first example on this page for more information about using the ItemsSource property.

Example code 5: C#

using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;

class Order
{
    public int Size { get; set; }
}

namespace WpfApplication14
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
	public MainWindow()
	{
	    InitializeComponent();
	}

	private void DataGrid_Loaded(object sender, RoutedEventArgs e)
	{
	    // ... Create Order objects.
	    var items = new List<Order>();
	    for (int i = 0; i < 100; i++)
	    {
		items.Add(new Order { Size = i });
	    }

	    // ... Use ItemsSource.
	    var grid = sender as DataGrid;
	    grid.ItemsSource = items;

	    // ... Scroll final element into view.
	    grid.ScrollIntoView(items[items.Count - 1]);
	}
    }
}

File. A DataGrid can be used to edit text files (such as CSV files). This program reads in a text file with the DataGrid_Loaded event. It uses the StreamReader type to read in each line, calls Split, and adds Patient objects to a List.

StreamReaderSplit

Then: As the user edits the DataGrid, the List objects are changed. The List is stored in a field on the MainWindow class.

Finally, the Window_Closing event handler is executed. Here we access the List of Patient objects—this has been modified in memory. We loop over the objects and, with a StreamWriter, persist it to the disk.

StreamWriter

Example text file: data.txt

Harry,Ford,ABC,1
Sally,Martin,POE,2
Edith,Milner,QED,0
James,Lawry,XYZ,8

Example markup 6: XAML

<Window x:Class="WpfApplication14.MainWindow"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	Title="MainWindow" Height="350" Width="525"
	Closing="Window_Closing">
    <Grid>
	<DataGrid
	    HorizontalAlignment="Left"
	    Margin="10,10,0,0"
	    VerticalAlignment="Top"
	    Loaded="DataGrid_Loaded"/>
    </Grid>
</Window>

Example code 6: C#

using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Windows;
using System.Windows.Controls;

class Patient
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Code { get; set; }
    public int Insurance { get; set; }
    public Patient(string line)
    {
	string[] parts = line.Split(',');
	this.FirstName = parts[0];
	this.LastName = parts[1];
	this.Code = parts[2];
	this.Insurance = int.Parse(parts[3]);
    }
    public string GetLine()
    {
	return this.FirstName + "," + this.LastName + "," + this.Code + "," +
	    this.Insurance.ToString();
    }
}

namespace WpfApplication14
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
	// ... Store the list as a field.
	List<Patient> _list;

	public MainWindow()
	{
	    InitializeComponent();
	}

	private void DataGrid_Loaded(object sender, RoutedEventArgs e)
	{
	    // ... Get data.
	    var patients = new List<Patient>();
	    using (StreamReader reader = new StreamReader("data.txt"))
	    {
		while (true)
		{
		    string line = reader.ReadLine();
		    if (line == null)
		    {
			break;
		    }
		    patients.Add(new Patient(line));
		}
	    }
	    // ... Set field.
	    this._list = patients;

	    // ... Use ItemsSource.
	    var grid = sender as DataGrid;
	    grid.ItemsSource = patients;
	}

	private void Window_Closing(object sender, CancelEventArgs e)
	{
	    // ... Write the DataGrid at Closing.
	    using (StreamWriter writer = new StreamWriter("data.txt"))
	    {
		foreach (Patient patient in this._list)
		{
		    writer.WriteLine(patient.GetLine());
		}
	    }
	}
    }
}

Summary. DataGrid aids in the presentation, and editing, of tabular data. We adjusted its appearance, used its events, and populated its contents with ItemsSource. It is a powerful, and commonly-needed control in WPF.