WPF comes with a large number of built in controls, but from the
beginning it has lacked something that many application developers find
extremely important - a DataGrid. You can use the
ListView
to create
something approximating a DataGrid (I've talked about it in a
this tutorial),
but it is a lot of work and not particularly straightforward.
Thankfully, Microsoft realizes how important a full-featured DataGrid is
- and how you probably don't want to wait for the next version of WPF to
be able to use one. This is where the WPFToolkit comes in. The WPF Toolkit is "a
collection of WPF features and components that are being made available
outside of the normal .NET Framework ship cycle" which to me translates
as "handy new controls I don't have to wait for".
The WPF Toolkit has a couple different controls, but the big one is the
DataGrid - and that is what we will be exploring today. You can grab the
toolkit from
here.
It is an MSI install package that will drop a couple DLLs off in a "WPF
Toolkit" directory in your program files directory. Once you have that
installed, the first thing you need to do to be able to use the toolkit
is add it as a reference to your project:
Ok, now that we have the dll added as a reference, it is time to start
actually using the DataGrid. The basics of using the DataGrid are
incredibly easy - for example, take a look at the following XAML:
<Window x:Class="SOTC_DataGridExample.Window1" Name="This"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dg="http://schemas.microsoft.com/wpf/2008/toolkit"
Title="DataGrid Example" Height="300" Width="300">
<dg:DataGrid ItemsSource="{Binding ElementName=This, Path=GameData}" Margin="5" />
</Window>
First off, to use the toolkit, you have to add a new namespace line in
XAML for the toolkit. In this case, I called the namespace
dg
. Once
you have that, you can drop down a XAML tag for the DataGrid
. For this
example, the DataGrid
is bound to an ItemSource
called GameData
-
let's take a look at the backing C# code to find out what that is:public partial class Window1 : Window
{
private DataTable _GameData;
public Window1()
{
_GameData = new DataTable();
_GameData.Columns.Add(new DataColumn("Game Name", typeof(string)));
_GameData.Columns.Add(new DataColumn("Creator", typeof(string)));
_GameData.Columns.Add(new DataColumn("Publisher", typeof(string)));
var row = _GameData.NewRow();
_GameData.Rows.Add(row);
row["Game Name"] = "World Of Warcraft";
row["Creator"] = "Blizzard";
row["Publisher"] = "Blizzard";
row = _GameData.NewRow();
_GameData.Rows.Add(row);
row["Game Name"] = "Halo";
row["Creator"] = "Bungie";
row["Publisher"] = "Microsoft";
row = _GameData.NewRow();
_GameData.Rows.Add(row);
row["Game Name"] = "Gears Of War";
row["Creator"] = "Epic";
row["Publisher"] = "Microsoft";
InitializeComponent();
}
public DataTable GameData
{ get { return _GameData; } }
}
GameData is a
DataTable.
If you have played around with the DataGridView in WinForms, you
probably recognize the name - it was the easy way to hook up a
DataGridView to a database. In this case, I didn't bring a whole
database into this sample app - I just populated the DataTable by hand
with a little bit of code.
One of the handy things with the WPF
DataGrid
that with something like
a DataTable
, the DataGrid
can do pretty much everything that we need
automatically. With only the code above, we get an application that
looks like this:
The
DataGrid
will, if it can, automatically generate the columns for
your data (in this case, it created the three columns "Game Name",
"Creator" and "Publisher"). In addition to that, though, you
automatically get edit, add, and remove capabilities. If we attach to
the RowChanged
and RowDeleted
events on the DataTable
, we can see
that changes to the DataGrid
are automatically getting pushed back
into the DataTable
:private void GameDataRowChanged(object sender, DataRowChangeEventArgs e)
{
Console.WriteLine("----Row Changed----");
Console.WriteLine("Action: " + e.Action);
if (e.Action != DataRowAction.Delete)
{
Console.Write("Values: ");
foreach (var val in e.Row.ItemArray)
{ Console.Write(val + ", "); }
Console.WriteLine();
}
Console.WriteLine("-------------------");
}
After adding a new row to the app, we get this output:
----Row Changed----
Action: Add
Values: My Game, A Creator, A Publisher,
-------------------
If the
DataTable
was connected up to a database, these changes would
get automatically pushed, with little to no work on our part!
Ok, time to get a bit more advanced. Let's say we wanted the
Publisher
column to be a ComboBox
, and we wanted to add a CheckBox
column for
if the game is available on the Xbox:
Because we have custom column types in there, we can't use automatic
column generation anymore. This makes the XAML much more interesting, so
let's take a look at it:
<Window x:Class="SOTC_DataGridExample.Window1" Name="This"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dg="http://schemas.microsoft.com/wpf/2008/toolkit"
Title="DataGrid Example" Height="300" Width="300">
<dg:DataGrid ItemsSource="{Binding ElementName=This, Path=GameData}"
Margin="5" AutoGenerateColumns="False">
<dg:DataGrid.Columns>
<dg:DataGridTextColumn Binding="{Binding Game Name}" Header="Game Name" />
<dg:DataGridTextColumn Binding="{Binding Creator}" Header="Creator" />
<dg:DataGridComboBoxColumn Header="Publisher" x:Name="_PublisherCombo"
SelectedItemBinding="{Binding Publisher}" />
<dg:DataGridCheckBoxColumn Binding="{Binding On Xbox}"
Header="On Xbox 360" />
</dg:DataGrid.Columns>
</dg:DataGrid>
</Window>
Looks kind of similar to how you would use the
GridView
, doesn't it?
However, unlike there, here we have a couple built in column types that
we can use - in this case, DataGridTextColumn
for editable text,
DataGridComboBoxColumn
for when we want a combo box, and
DataGridCheckBoxColumn
for when we want a checkbox. The basics are the
same for all three of them - the Header
property sets the name of the
column, and the Binding
property (or SelectedItemBinding
in the case
of the DataGridComboBoxColumn
) binds the value of that column to a
column in the backing data store (whether that is a DataTable
, some
XML, or some other type of data).
The
dg:DataGridCheckBoxColumn
pretty much works without any extra
work, but the DataGridComboBoxColumn
takes a little more care. We have
to provide it with a collection of entries for the combo box - in this
case a collection of publisher names. You can do another binding
(although beware, the DataContext will be the particular row in your
data source, and so the binding may be difficult), but in this case I
just set it to a collection of strings in code:using System;
using System.Collections.Generic;
using System.Data;
using System.Windows;
namespace SOTC_DataGridExample
{
public partial class Window1 : Window
{
private DataTable _GameData;
public Window1()
{
_GameData = new DataTable();
_GameData.Columns.Add(new DataColumn("Game Name", typeof(string)));
_GameData.Columns.Add(new DataColumn("Creator", typeof(string)));
_GameData.Columns.Add(new DataColumn("Publisher", typeof(string)));
_GameData.Columns.Add(new DataColumn("On Xbox", typeof(bool)));
var row = _GameData.NewRow();
_GameData.Rows.Add(row);
row["Game Name"] = "World Of Warcraft";
row["Creator"] = "Blizzard";
row["Publisher"] = "Blizzard";
row["On Xbox"] = false;
row = _GameData.NewRow();
_GameData.Rows.Add(row);
row["Game Name"] = "Halo 3";
row["Creator"] = "Bungie";
row["Publisher"] = "Microsoft";
row["On Xbox"] = true;
row = _GameData.NewRow();
_GameData.Rows.Add(row);
row["Game Name"] = "Gears Of War";
row["Creator"] = "Epic";
row["Publisher"] = "Microsoft";
row["On Xbox"] = true;
_GameData.RowChanged += GameDataRowChanged;
_GameData.RowDeleted += GameDataRowChanged;
InitializeComponent();
_PublisherCombo.ItemsSource = new List<string>() { "Activision", "Ubisoft",
"Microsoft", "Blizzard", "Nintendo", "Electronic Arts",
"Take-Two Interactive" };
}
private void GameDataRowChanged(object sender, DataRowChangeEventArgs e)
{
Console.WriteLine("----Row Changed----");
Console.WriteLine("Action: " + e.Action);
if (e.Action != DataRowAction.Delete)
{
Console.Write("Values: ");
foreach (var val in e.Row.ItemArray)
{ Console.Write(val + ", "); }
Console.WriteLine();
}
Console.WriteLine("-------------------");
}
public DataTable GameData
{ get { return _GameData; } }
}
}
Well, that is it for this introduction to the WPF Toolkit DataGrid. This
really only scratches the surface of what you can do with the DataGrid -
it is quite the complex control. I will leave you with one tidbit,
though - as much as possible the DataGrid follows standard WPF rules -
so all the rules for styling, control templates, and data templates
still apply. You can look forward to details on that type of
customization in a future tutorial - for now, you will have to be
satisfied with experimenting on your own. If you would like somewhere to
start, you can grab the Visual Studio solution for this example project
below - but remember, for it to run, you will need to install the
toolkit.
Source Files:
Comments
Post a Comment