C# - Using The ListView, Part 1 In WPF [Beginner]


Many of the controls in WPF have a downright dizzying array of capabilities and features, due in large part to the composibility of all the components. The ListView control is a great example of this - the possibilities are almost endless. This series of tutorials on the ListView will hopefully make the space of possible options seem not quite as daunting.

We will be starting today with a simple grid based list view, showing how to create columns and some different ways of getting data into those columns. This tutorial won't be very exciting - we will mostly just be setting up the basics for use in some of the later parts. Hopefully, the series will culminate in a sort of "DataGridView" for WPF (a control that, sadly, does not yet exist in WPF). I say hopefully cause I haven't yet written the code for those parts :

OK, so here's a screenshot of a ListView with a few columns and no data. Nothing really fancy, but the basic starting point.

Empty ListView

The code to create this is just as boring:
<Window x:Class="ListViewTest.Test0.ListViewTest" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Empty ListView Grid" Height="216" Width="435">
 <ListView>
  <ListView.View>
   <GridView>
    <GridViewColumn Width="140" Header="Column 1" />
    <GridViewColumn Width="140" Header="Column 2" />
    <GridViewColumn Width="140" Header="Column 3" />
   </GridView>
  </ListView.View>
 </ListView>
</Window>
 
The special code here is in the ListView.View section. This is what changes the list view from your standard wrapping list of items into something else - in this case a GridView. Don't confuse the GridView with the standard WPF Grid control - they are very different. Pretty much the only thing that is the same is that they both have the word Grid in the name.

So we make a GridView and then we make a couple GridViewColumns. These GridViewColumns have a lot more functionality than is used here - we will start to see that in the later examples and especially in later tutorials. There are a couple nice things that you automatically get with the GridView and GridViewColumns - first off, the columns can be resized by the user exactly like, say, the columns in Windows Explorer. Also, the user can drag+drop to reorder columns, and you don't have to worry about it at all. One thing that is missing, though, is some built in sorting capability - but it isn't that hard to add on (we will be doing that in a later tutorial).

OK, enough about that. Lets throw some data into this grid!

Listview with
dates

And heres the xaml code:
<Window x:Class="ListViewTest.Test1.ListViewTest"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    Title="Some Dates" Height="216" Width="435">  
  <ListView>
    <ListView.View>
      <GridView>
        <GridViewColumn Width="120" Header="Date" />
        <GridViewColumn Width="120" Header="Day Of Week"  
            DisplayMemberBinding="{Binding DayOfWeek}" />
        <GridViewColumn Width="120" Header="Year" 
            DisplayMemberBinding="{Binding Year}" />
      </GridView>
    </ListView.View>
    <sys:DateTime>1/2/3</sys:DateTime>
    <sys:DateTime>4/5/6</sys:DateTime>
    <sys:DateTime>7/8/9</sys:DateTime>
    <sys:DateTime>10/11/12</sys:DateTime>
  </ListView>
</Window>
 
So there are a couple changes to the xaml here. First, I imported the System namespace and registered it to "sys", because I wanted to use some DateTime objects as my ListView data. Next, you can see that two of the GridViewColumns have a DisplayMemberBinding property now. And finally, there are the 4 dates which are the data for the ListView.

The only part that probably needs explaining is the DisplayMemberBinding property. This property tells the column what data from an entry should be displayed in that column. As you can see, the first column does not have this property set, so it just does a toString on the date object for each row to determine what to put in that column. The second column is bound to the DayOfWeek property on the DateTime object - and so in that column the day of the week for each of those dates is printed. And for the third column, we have bound to the Year property, and so we get the year out. Pretty cool, eh?
Now you're probably thinking thats all well and good, but you want to display your own data objects, and you want to be able to add and remove them on the fly. No worries there - the ListView has got you covered!

Listview with games

Here we make a departure for using just XAML - there is actually (gasp!) some C# code behind. So lets take a look:
<Window x:Class="ListViewTest.Test2.ListViewTest"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    DataContext="{Binding RelativeSource={RelativeSource Self}}"
    Title="Some Game Data" Height="216" Width="435">
  <StackPanel>
    <ListView ItemsSource="{Binding GameCollection}">
      <ListView.View>
        <GridView>
          <GridViewColumn Width="140" Header="Game Name" 
              DisplayMemberBinding="{Binding GameName}"  />
          <GridViewColumn Width="140" Header="Creator"  
              DisplayMemberBinding="{Binding Creator}" />
          <GridViewColumn Width="140" Header="Publisher" 
              DisplayMemberBinding="{Binding Publisher}" />
        </GridView>
      </ListView.View>
    </ListView>
    <Button HorizontalAlignment="Right" Margin="5,5,5,5" 
        Content="Add Row" Click="AddRow_Click" />
  </StackPanel>
</Window>
 
And the code behind:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Collections.ObjectModel;

namespace ListViewTest.Test2
{
  public partial class ListViewTest : Window
  {
    ObservableCollection<GameData> _GameCollection = 
        new ObservableCollection<GameData>();

    public ListViewTest()
    {
      _GameCollection.Add(new GameData { 
          GameName = "World Of Warcraft", 
          Creator = "Blizzard", 
          Publisher = "Blizzard" });
      _GameCollection.Add(new GameData { 
          GameName = "Halo", 
          Creator = "Bungie", 
          Publisher = "Microsoft" });
      _GameCollection.Add(new GameData { 
          GameName = "Gears Of War", 
          Creator = "Epic", 
          Publisher = "Microsoft" });

      InitializeComponent();
    }

    public ObservableCollection<GameData> GameCollection
    { get { return _GameCollection; } }

    private void AddRow_Click(object sender, RoutedEventArgs e)
    {
      _GameCollection.Add(new GameData { 
          GameName = "A New Game", 
          Creator = "A New Creator", 
          Publisher = "A New Publisher" });
    }
  }

  public class GameData
  {
    public string GameName { get; set; }
    public string Creator { get; set; }
    public string Publisher { get; set; }
  }
}
 
So what we have here is a set of GameData objects that we want to display in the ListView. The GameData objects have a Game Name, a Creator, and a Publisher, and so we create three columns in the ListView and bind them each to the appropriate fields. One thing to note - and I'll probably elaborate on this more if I ever get around to a tutorial on WPF Binding - you can only bind to public properties/methods/dependency properties. You can't bind to private things, or to fields. The syntax I used in the GameData class right above is syntactic sugar for not having to write out the full property definition. When the code is compiled, those lines for GameName, etc., turn into properties with a backing private field.

So we have some GameData objects - how do we get them into the ListView? Well, we create an ObservableCollection of them. ObservableCollection is not your normal everyday generic list - it can tell other pieces of code when the contents of the collection change. You'll see why this comes in handy in a moment.

Now that we have this collection (and we have created a public field for it) what do we do with it? Well, we bind it as the ItemsSource of the ListView. This is where the fact that the collection is observable comes in handy - now the ListView gets alerted whenever anything changes inside of the collection, and can update accordingly. For example, when you press the "Add Row" button, a new GameData instance is added to the collection. The ListView is notified, and so is able to update and display the new row.

OK, well that is it for this first tutorial on the WPF ListView. You can get the source code for all three examples here if you would like it. Stay tuned for future tutorials on the ListView, covering topics like sorting, filtering, and how to configure the look of the cells in the view.
Source Files:

Comments