Today, I’m walking you through the ins and outs of using the WPF DataGrid in C# applications. While it might seem a bit trickier than the Windows Forms DataGrid at first glance, I promise it’s actually quite straightforward once you get the hang of it. I’ve spent countless hours working with this control, and I’m excited to share what I’ve learned to make your development journey smoother.
Before diving in, you should have:
Also, if you need a refresher on LINQ to SQL, check out our beginner-friendly LINQ tutorial first, as we’ll be using some database concepts in our examples.
Unlike Windows Forms, the DataGrid isn’t automatically included in older WPF projects. For modern .NET projects (using .NET Core 3.0+ or .NET 5/6/7/8), the DataGrid is included in the base framework. However, for older .NET Framework projects, you might need to add a reference.
System.Windows.Controls.DataGrid.ToolkitOnce installed, you’ll need to add the namespace reference to your XAML file:
xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.DataGrid.Toolkit"Code language: JavaScript (javascript) Or alternatively, you can use the Microsoft WPF Toolkit namespace:
xmlns:my="http://schemas.microsoft.com/wpf/2008/toolkit"Code language: JavaScript (javascript) The DataGrid is included in the base frameworks, so you simply need to add:
xmlns:data="clr-namespace:System.Windows.Controls;assembly=PresentationFramework"Code language: JavaScript (javascript) Let’s start by creating a simple DataGrid. Here’s the XAML code:
<DataGrid x:Name="ConfigGrid" AutoGenerateColumns="False" Margin="10">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Id}" Header="ID" />
<DataGridTextColumn Binding="{Binding Path=Name}" Header="Name" Width="200" />
<DataGridTextColumn Binding="{Binding Path=CreatedDate}" Header="Date Created" />
</DataGrid.Columns>
</DataGrid>Code language: HTML, XML (xml) The AutoGenerateColumns="False" property is crucial! It tells the DataGrid not to automatically create columns from your data source. This gives you complete control over which properties to display and how they should appear.
WPF DataGrid offers several column types to handle different data presentations:
The most powerful of these is the DataGridTemplateColumn, which allows you to create custom UI elements within cells.
One of the most powerful features of the WPF DataGrid is the ability to embed custom controls. Here’s how to add a delete button to each row:
<DataGrid x:Name="ConfigGrid" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Id}" Header="ID" Visibility="Hidden" />
<DataGridTextColumn Binding="{Binding Path=PartNumber}" Header="Part Number" />
<DataGridTextColumn Binding="{Binding Path=CreatedDate}" Header="Date Created" />
<DataGridTextColumn Binding="{Binding Path=Comments}" Header="Comments" Width="250" />
<DataGridTemplateColumn Header="Actions">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Name="btnDelete" Click="btnDelete_Click"
Padding="5,2" Margin="2">Delete</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>Code language: HTML, XML (xml) The magic happens in the DataGridTemplateColumn. Inside the DataTemplate tag, you can add virtually any WPF control – buttons, checkboxes, images, or even custom user controls!
Now for the exciting part – populating your DataGrid with actual data. This is absolutely simple with WPF’s data binding capabilities. Here’s how to do it:
// Create or retrieve your data collection
List<Product> products = GetProductsFromDatabase();
// Assign it to the DataGrid's ItemsSource property
MyDataGrid.ItemsSource = products;Code language: PHP (php) That’s it! The DataGrid automatically binds to the properties of your objects that match the column bindings.
WPF DataGrid works wonderfully with various data sources:
List<T>, ObservableCollection<T>)For real-time updates, I recommend using ObservableCollection<T> which notifies the UI when items are added or removed:
// Create an observable collection
ObservableCollection<Customer> customers = new ObservableCollection<Customer>();
// Add items
customers.Add(new Customer { Id = 1, Name = "John Doe" });
// Bind to DataGrid
CustomersGrid.ItemsSource = customers;
// Later, when you add new items, the UI updates automatically
customers.Add(new Customer { Id = 2, Name = "Jane Smith" });Code language: JavaScript (javascript) Let’s look at handling operations on DataGrid rows. Here’s how to implement a delete function:
private void btnDelete_Click(object sender, RoutedEventArgs e)
{
try
{
// Get the clicked button
Button button = (Button)sender;
// Get the data context (your object)
var item = button.DataContext;
// Alternative method: Get from the DataGrid directly
// var item = MyDataGrid.CurrentItem;
// Cast to your type
Product product = (Product)item;
// Perform the delete operation
if (ProductManager.DeleteProduct(product.Id))
{
MessageBox.Show("Product deleted successfully!");
// Refresh the DataGrid
LoadDataGrid();
}
else
{
MessageBox.Show("Could not delete the product");
}
}
catch (Exception ex)
{
// Log the error
Console.WriteLine(ex.Message);
MessageBox.Show("An error occurred while deleting");
}
}Code language: PHP (php) A key point here is accessing the current item. You have two options:
DataContext propertyCurrentItem propertyBoth approaches work great, but I prefer the first method because it’s more reliable when you have multiple buttons in a row.
WPF offers incredible styling capabilities. Here’s a simple example to make alternate rows have different background colors:
<DataGrid AlternatingRowBackground="LightGray"
RowBackground="White"
GridLinesVisibility="Horizontal"
HeadersVisibility="Column">Code language: HTML, XML (xml) For more advanced styling, you can create complete style templates in your resources:
<Window.Resources>
<Style TargetType="{x:Type DataGrid}">
<Setter Property="BorderBrush" Value="#DDD" />
<Setter Property="HorizontalGridLinesBrush" Value="#EEE" />
</Style>
<Style TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="LightBlue" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>Code language: HTML, XML (xml) The DataGrid supports in-place editing by default. To enable or disable this:
<DataGrid CanUserAddRows="False"
CanUserDeleteRows="False"
IsReadOnly="False">Code language: HTML, XML (xml) You can define different templates for viewing versus editing:
<DataGridTemplateColumn Header="Status">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Status}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Source={StaticResource StatusOptions}}"
SelectedItem="{Binding Status}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>Code language: HTML, XML (xml) Enable built-in sorting with these properties:
<DataGrid CanUserSortColumns="True"
SortingMode="Automatic">Code language: HTML, XML (xml) For filtering and grouping, you’ll need to implement these yourself using collection views:
ICollectionView view = CollectionViewSource.GetDefaultView(MyDataGrid.ItemsSource);
view.Filter = item => ((Product)item).Price > 100; // Show only products over $100Code language: JavaScript (javascript) When dealing with large datasets, enable UI virtualization:
<DataGrid VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Standard">Code language: HTML, XML (xml) To ensure consistent row selection:
<DataGrid SelectionUnit="FullRow"
SelectionMode="Single">Code language: HTML, XML (xml) A common issue is that checkboxes lose state when scrolling. Fix this by ensuring proper binding:
<DataGridCheckBoxColumn Binding="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}" />Code language: HTML, XML (xml) Make sure your model implements INotifyPropertyChanged to maintain state:
public class ProductItem : INotifyPropertyChanged
{
private bool _isSelected;
public bool IsSelected
{
get => _isSelected;
set
{
_isSelected = value;
OnPropertyChanged("IsSelected");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
} Here’s a complete example putting everything together:
// XAML
<DataGrid x:Name="ProductsGrid"
AutoGenerateColumns="False"
CanUserAddRows="False"
SelectionMode="Single"
SelectionUnit="FullRow"
Margin="10">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Id}" Header="ID" />
<DataGridTextColumn Binding="{Binding Name}" Header="Product Name" Width="200" />
<DataGridTextColumn Binding="{Binding Price, StringFormat=C}" Header="Price" />
<DataGridCheckBoxColumn Binding="{Binding IsInStock}" Header="In Stock" />
<DataGridTemplateColumn Header="Actions">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Content="Edit" Margin="2" Click="EditButton_Click" />
<Button Content="Delete" Margin="2" Click="DeleteButton_Click" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
// C# Code
private void LoadProducts()
{
ObservableCollection<Product> products = new ObservableCollection<Product>
{
new Product { Id = 1, Name = "Laptop", Price = 1299.99, IsInStock = true },
new Product { Id = 2, Name = "Monitor", Price = 349.99, IsInStock = true },
new Product { Id = 3, Name = "Keyboard", Price = 59.99, IsInStock = false }
};
ProductsGrid.ItemsSource = products;
}
private void EditButton_Click(object sender, RoutedEventArgs e)
{
var button = (Button)sender;
var product = (Product)button.DataContext;
// Open edit dialog or navigate to edit page
MessageBox.Show($"Editing product: {product.Name}");
}
private void DeleteButton_Click(object sender, RoutedEventArgs e)
{
var button = (Button)sender;
var product = (Product)button.DataContext;
// Perform delete operation
MessageBox.Show($"Deleting product: {product.Name}");
// Remove from collection
var collection = (ObservableCollection<Product>)ProductsGrid.ItemsSource;
collection.Remove(product);
}Code language: PHP (php) The WPF DataGrid is an absolutely powerful control that gives you tremendous flexibility for displaying and manipulating tabular data. While it might seem complex at first, you’ll soon discover that its rich feature set and customization options make it far superior to the Windows Forms DataGrid.
I’ve covered the basics here, but the possibilities are practically endless. You can create master-detail views, implement drag-and-drop functionality, add row details sections, and much more.
If you have questions or run into specific issues, feel free to leave a comment below. Happy coding! 🚀
Ever wondered what happens when you run Python code? The Python runtime environment—comprising the interpreter, virtual machine, and system resources—executes your code through bytecode compilation…
Tired of repetitive tasks eating up your time? Python can help you automate the boring stuff — from organizing files to scraping websites and sending…
Learn python file handling from scratch! This comprehensive guide walks you through reading, writing, and managing files in Python with real-world examples, troubleshooting tips, and…
This website uses cookies.
View Comments
Excellent tutorial, for Data grid binding, I was looking for this kind of simple one, no where else it available. you made my job easy. Thanks allot. Keep it up.
Hello
There is problem with datagrid which is i am getting that this datagrid is not persistant. I add checkbox column to datagrid when i am selecting some checkboxes and scrolling down the datagrid some checks
automatically get removed and some get added. What is the solution for that? Please provide me solution for that.