WPF Tutorial - Using The ListView, Part 3 - In Place Edit

Skill

WPF Tutorial - Using The ListView, Part 3 - In Place Edit

Posted in:

And here we are, back with another installment of our ongoing series of ListView tutorials. And hey, I think that I got this one out in a reasonable amount of time - it has only been a month since Part 2 was published (as opposed to the 8 months between Part 2 and Part 1). Today we are going to take a look at how to do in-place editing using a ListView, which, because the ListView is not a full blown datagrid, takes a fair amount of work.

Now, I do realize that there is an actual WPF DataGrid control in the works (and in fact, they just released v1 about a month ago). The plan is that it will be integrated into the next release of the .NET framework, but for now you can always grab the source here if you want to play around with it. I've poked at it a little bit, and you never know, maybe I'll write up a tutorial on it at some point. But for now, we are focusing on the ListView, which is still a very useful control all on its own.

Of course, we need to start off with a screenshot of what this in place editing will look like:

List View In Place Edit Screenshot

Essentially, the selected row will turn into a row of editable controls (in this case, two textboxes and a combobox). The user can edit them to their heart's content, their changes being pushed into the backend data store. As the selected row changes, the controls flip between standard textblocks and the edit controls.

We will be starting off with the code from the previous tutorial, so you might want to review that before you continue.

So first off, we have to make it so that the ListView and the backing properties are much more tightly bound. This way, changes to backing properties will be reflected in the ListView, and changes to the ListView will be reflected in the backing properties. The old backing class was really simple, and looked like this:

public class GameData
{
  public string GameName { get; set; }
  public string Creator { get; set; }
  public string Publisher { get; set; }
}

Essentially, we need to make all of these properties dependency properties:

public class GameData : DependencyObject
{
  public static readonly DependencyProperty GameNameProperty =
    DependencyProperty.Register("GameName", typeof(string),
    typeof(GameData), new UIPropertyMetadata(null));

  public string GameName
  {
    get { return (string)GetValue(GameNameProperty); }
    set { SetValue(GameNameProperty, value); }
  }

  public static readonly DependencyProperty CreatorProperty =
    DependencyProperty.Register("Creator", typeof(string),
    typeof(GameData), new UIPropertyMetadata(null));

  public string Creator
  {
    get { return (string)GetValue(CreatorProperty); }
    set { SetValue(CreatorProperty, value); }
  }

  public static readonly DependencyProperty PublisherProperty =
      DependencyProperty.Register("Publisher", typeof(string),
      typeof(GameData), new UIPropertyMetadata(null));

  public string Publisher
  {
    get { return (string)GetValue(PublisherProperty); }
    set { SetValue(PublisherProperty, value); }
  }
}

Yeah, its a lot more code (the main thing I dislike about DependencyProperties - although that is mitigated with the extremely handy propdb built in Visual Studio code snippet). These are pretty standard DependencyProperty declarations, but if you need a refresher on how they work, I suggest you check out our DependencyProperty tutorial.

Ok, now that all those properties are DependencyProperties, they will work for two-way binding. And so now its time to work on the XAML. To start off with, we are going to take a look at the XAML behind one of the columns in the ListView:

<GridViewColumn Width="140">
  <GridViewColumnHeader Click="SortClick"
     Tag="GameName"
     Content="Game Name" />
  <GridViewColumn.CellTemplate>
    <DataTemplate>
      <Grid>
        <TextBlock Text="{Binding Path=GameName}"
           Style="{StaticResource GridBlockStyle}"/>
        <TextBox Text="{Binding Path=GameName}"
           Style="{StaticResource GridEditStyle}" />
      </Grid>
    </DataTemplate>
  </GridViewColumn.CellTemplate>
</GridViewColumn>

The GridViewColumnHeader is the same as it was in the previous tutorial, but we have gotten rid of the attribute DisplayMemberBinding on the column itself. In its place, we are setting up a DataTemplate for the cell. The DataTemplate consists of two main parts - the TextBlock and the TextBox. Both are bound to the the same backing field - the field for this column. They also both have a style set, which is what we are going to look at next:

First, the style for the TextBlock:

<Style TargetType="{x:Type TextBlock}"
      x:Key="GridBlockStyle">
  <Setter Property="VerticalAlignment" Value="Center" />
  <Setter Property="Visibility"
     Value="{Binding Path=IsSelected,
         RelativeSource={RelativeSource FindAncestor,
             AncestorType={x:Type ListViewItem}},
         Converter={StaticResource boolToVis},
             ConverterParameter=False}" />

</Style>

The VerticalAlignment isn't very important, it just makes the content look nice. The real important part here is what Visibility is being set to. What we want to happen is that the TextBlock is visible when the row is not selected, but invisible when the row is selected. The annoying part is finding out if the row a particular TextBlock is in is selected.

We do this by using a special type of Binding, called RelativeSource. Essentially, we are telling the binding to walk up the Visual Tree from the TextBlock to find the first parent that is a ListViewItem. Off of that ListViewItem, we want the IsSelected property. We then take that value and pass it to a converter - and that converter gives us a Visibility enum back. Let's take a look at that converter:

public class BoolToVisibilityConverter : IValueConverter
{
  public object Convert(object value, Type targetType,
      object parameter, System.Globalization.CultureInfo culture)
  {
    bool param = bool.Parse(parameter as string);
    bool val = (bool)value;

    return val == param ?
      Visibility.Visible : Visibility.Hidden;
  }

  public object ConvertBack(object value, Type targetType,
      object parameter, System.Globalization.CultureInfo culture)
  {
    throw new NotImplementedException();
  }
}

This converter expects a boolean as the value, and also expects a boolean (in the form of a string) as the parameter. If the value and the parameter match, the converter returns Visibility.Visible, otherwise it returns Visibility.Hidden. This is why the ConverterParameter for the visibility binding on the TextBlock is set to false - we want the TextBlock to be visible when IsSelected is false.

Ok, now for that TextBox style:

<Style TargetType="{x:Type FrameworkElement}"
     x:Key="GridEditStyle">
  <Setter Property="VerticalAlignment" Value="Center" />
  <Setter Property="Visibility"
     Value="{Binding Path=IsSelected,
         RelativeSource={RelativeSource FindAncestor,
             AncestorType={x:Type ListViewItem}},
         Converter={StaticResource boolToVis},
             ConverterParameter=True}" />

</Style>

This style is not specifically set for a TextBox, because we will be using it for other things (as you will see in a minute). Other than that it is virtually identical to the style for the TextBox. The key difference here is that the ConverterParameter on the visibility binding is set to true - in this case, we want the TextBox to only be visible when the row is selected.

Ok, let's get crazy and throw the rest of the XAML for the ListView in here:

<ListView x:Name="gameListView" ItemsSource=
   "{Binding ElementName=This, Path=GameCollection}">
  <ListView.View>
    <GridView>
     
      <GridViewColumn Width="140">
        <GridViewColumnHeader Click="SortClick"
           Tag="GameName"
           Content="Game Name" />
        <GridViewColumn.CellTemplate>
          <DataTemplate>
            <Grid>
              <TextBlock Text="{Binding Path=GameName}"
                 Style="{StaticResource GridBlockStyle}"/>
              <TextBox Text="{Binding Path=GameName}"
                 Style="{StaticResource GridEditStyle}" />
            </Grid>
          </DataTemplate>
        </GridViewColumn.CellTemplate>
      </GridViewColumn>
     
      <GridViewColumn Width="140">
        <GridViewColumnHeader Click="SortClick"
           Tag="Creator"
           Content="Creator" />
        <GridViewColumn.CellTemplate>
          <DataTemplate>
            <Grid>
              <TextBlock Text="{Binding Path=Creator}"
                 Style="{StaticResource GridBlockStyle}"/>
              <TextBox Text="{Binding Path=Creator}"
                 Style="{StaticResource GridEditStyle}" />
            </Grid>
          </DataTemplate>
        </GridViewColumn.CellTemplate>
      </GridViewColumn>
     
      <GridViewColumn Width="140">
        <GridViewColumnHeader Click="SortClick"
           Tag="Publisher"
           Content="Publisher" />
        <GridViewColumn.CellTemplate>
          <DataTemplate>
            <Grid HorizontalAlignment="Stretch">
              <TextBlock Text="{Binding Path=Publisher}"
                 Style="{StaticResource GridBlockStyle}"/>
              <ComboBox SelectedItem="{Binding Path=Publisher}"
                 ItemsSource="{Binding ElementName=This,
                     Path=AvailablePublishers}"

                 Style="{StaticResource GridEditStyle}" />
            </Grid>
          </DataTemplate>
        </GridViewColumn.CellTemplate>
      </GridViewColumn>
     
    </GridView>
  </ListView.View>
</ListView>

Well, we have already walked through the first column step by step, and the second column is identical (except for the fact that the backing property being bound to is Creator and not GameName). The third column is where things get a little bit interesting - the introduction of a ComboBox instead of a TextBox. The code is still very similar too the first two columns - we get to reuse the same edit control style that we wrote above. The important differences are that we bind the backing property to the ComboBox's SelectedValue, and that we need to give the ComboBox a set of items to actually show as choices (the ItemsSource - which we are just setting to a collection of strings being created in the C# code behind).

Well, that is about it. Below is all the C# and XAML code for this example:

<Window x:Class="ListViewTest4.ListViewTest" Name="This"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:local="clr-namespace:ListViewTest4"
   Title="Some Game Data" Height="216" Width="435">
  <Window.Resources>
    <local:BoolToVisibilityConverter x:Key="boolToVis" />
   
    <Style TargetType="{x:Type TextBlock}"
       x:Key="GridBlockStyle">
      <Setter Property="VerticalAlignment" Value="Center" />
      <Setter Property="Visibility"
         Value="{Binding Path=IsSelected,
             RelativeSource={RelativeSource FindAncestor,
                 AncestorType={x:Type ListViewItem}},
             Converter={StaticResource boolToVis},
                 ConverterParameter=False}" />

    </Style>

    <Style TargetType="{x:Type FrameworkElement}"
       x:Key="GridEditStyle">
      <Setter Property="VerticalAlignment" Value="Center" />
      <Setter Property="Visibility"
         Value="{Binding Path=IsSelected,
             RelativeSource={RelativeSource FindAncestor,
                 AncestorType={x:Type ListViewItem}},
             Converter={StaticResource boolToVis},
                 ConverterParameter=True}" />

    </Style>

  </Window.Resources>
  <StackPanel>
    <ListView x:Name="gameListView" ItemsSource=
       "{Binding ElementName=This, Path=GameCollection}">
      <ListView.View>
        <GridView>
         
          <GridViewColumn Width="140">
            <GridViewColumnHeader Click="SortClick"
               Tag="GameName"
               Content="Game Name" />
            <GridViewColumn.CellTemplate>
              <DataTemplate>
                <Grid>
                  <TextBlock Text="{Binding Path=GameName}"
                     Style="{StaticResource GridBlockStyle}"/>
                  <TextBox Text="{Binding Path=GameName}"
                     Style="{StaticResource GridEditStyle}" />
                </Grid>
              </DataTemplate>
            </GridViewColumn.CellTemplate>
          </GridViewColumn>
         
          <GridViewColumn Width="140">
            <GridViewColumnHeader Click="SortClick"
               Tag="Creator"
               Content="Creator" />
            <GridViewColumn.CellTemplate>
              <DataTemplate>
                <Grid>
                  <TextBlock Text="{Binding Path=Creator}"
                     Style="{StaticResource GridBlockStyle}"/>
                  <TextBox Text="{Binding Path=Creator}"
                     Style="{StaticResource GridEditStyle}" />
                </Grid>
              </DataTemplate>
            </GridViewColumn.CellTemplate>
          </GridViewColumn>
         
          <GridViewColumn Width="140">
            <GridViewColumnHeader Click="SortClick"
               Tag="Publisher"
               Content="Publisher" />
            <GridViewColumn.CellTemplate>
              <DataTemplate>
                <Grid HorizontalAlignment="Stretch">
                  <TextBlock Text="{Binding Path=Publisher}"
                     Style="{StaticResource GridBlockStyle}"/>
                  <ComboBox
                     SelectedItem="{Binding Path=Publisher}"
                     ItemsSource="{Binding ElementName=This,
                         Path=AvailablePublishers}"

                     Style="{StaticResource GridEditStyle}" />
                </Grid>
              </DataTemplate>
            </GridViewColumn.CellTemplate>
          </GridViewColumn>
         
        </GridView>
      </ListView.View>
    </ListView>
    <Button HorizontalAlignment="Right" Margin="5"
           Content="Add Row" Click="AddRowClick" />
  </StackPanel>
</Window>

using System;
using System.Windows;
using System.Windows.Controls;
using System.Collections.ObjectModel;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Windows.Media;
using System.Windows.Documents;
using System.Windows.Data;

namespace ListViewTest4
{
  public partial class ListViewTest : Window
  {
    private ObservableCollection<GameData> _GameCollection =
      new ObservableCollection<GameData>();
    private ObservableCollection<string> _AvailablePublishers =
      new ObservableCollection<string>();

    private GridViewColumnHeader _CurSortCol = null;
    private SortAdorner _CurAdorner = null;

    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" });

      _AvailablePublishers.Add("Microsoft");
      _AvailablePublishers.Add("Blizzard");
      _AvailablePublishers.Add("Nintendo");
      _AvailablePublishers.Add("Electronic Arts");
      _AvailablePublishers.Add("Activision");
      _AvailablePublishers.Add("Ubisoft");
      _AvailablePublishers.Add("Take-Two Interactive");

      InitializeComponent();
    }

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

    public ObservableCollection<string> AvailablePublishers
    { get { return _AvailablePublishers; } }

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

    private void SortClick(object sender, RoutedEventArgs e)
    {
      GridViewColumnHeader column =
          sender as GridViewColumnHeader;
      String field = column.Tag as String;

      if (_CurSortCol != null)
      {
        AdornerLayer.GetAdornerLayer(_CurSortCol).Remove(_CurAdorner);
        gameListView.Items.SortDescriptions.Clear();
      }

      ListSortDirection newDir = ListSortDirection.Ascending;
      if (_CurSortCol == column && _CurAdorner.Direction == newDir)
        newDir = ListSortDirection.Descending;

      _CurSortCol = column;
      _CurAdorner = new SortAdorner(_CurSortCol, newDir);
      AdornerLayer.GetAdornerLayer(_CurSortCol).Add(_CurAdorner);
      gameListView.Items.SortDescriptions.Add(
          new SortDescription(field, newDir));
    }
  }

  public class GameData : DependencyObject
  {
    public static readonly DependencyProperty GameNameProperty =
      DependencyProperty.Register("GameName", typeof(string),
      typeof(GameData), new UIPropertyMetadata(null));

    public string GameName
    {
      get { return (string)GetValue(GameNameProperty); }
      set { SetValue(GameNameProperty, value); }
    }

    public static readonly DependencyProperty CreatorProperty =
      DependencyProperty.Register("Creator", typeof(string),
      typeof(GameData), new UIPropertyMetadata(null));

    public string Creator
    {
      get { return (string)GetValue(CreatorProperty); }
      set { SetValue(CreatorProperty, value); }
    }

    public static readonly DependencyProperty PublisherProperty =
        DependencyProperty.Register("Publisher", typeof(string),
        typeof(GameData), new UIPropertyMetadata(null));

    public string Publisher
    {
      get { return (string)GetValue(PublisherProperty); }
      set { SetValue(PublisherProperty, value); }
    }
  }

  public class SortAdorner : Adorner
  {
    private readonly static Geometry _AscGeometry =
        Geometry.Parse("M 0,5 L 10,5 L 5,0 Z");
    private readonly static Geometry _DescGeometry =
        Geometry.Parse("M 0,0 L 10,0 L 5,5 Z");

    public ListSortDirection Direction { get; private set; }

    public SortAdorner(UIElement element, ListSortDirection dir)
      : base(element)
    { Direction = dir; }

    protected override void OnRender(
        DrawingContext drawingContext)
    {
      base.OnRender(drawingContext);

      if (AdornedElement.RenderSize.Width < 20)
        return;

      drawingContext.PushTransform(
          new TranslateTransform(
            AdornedElement.RenderSize.Width - 15,
            (AdornedElement.RenderSize.Height - 5) / 2));

      drawingContext.DrawGeometry(Brushes.Black, null,
          Direction == ListSortDirection.Ascending ?
            _AscGeometry : _DescGeometry);

      drawingContext.Pop();
    }
  }

  public class BoolToVisibilityConverter : IValueConverter
  {
    public object Convert(object value, Type targetType,
      object parameter, System.Globalization.CultureInfo culture)
    {
      bool param = bool.Parse(parameter as string);
      bool val = (bool)value;

      return val == param ? Visibility.Visible : Visibility.Hidden;
    }

    public object ConvertBack(object value, Type targetType,
      object parameter, System.Globalization.CultureInfo culture)
    {
      throw new NotImplementedException();
    }
  }

}

Oh, and guess what - all the sorting stuff from the previous tutorial still works exactly as you might expect. No changes needed. And I fixed the direction of the sorting arrows - thanks to Shtreber on the previous tutorial who pointed out that I had them going the wrong way.

You can grab the Visual Studio project for the above example here. The project includes all the examples for all the ListView tutorials, so you should be able to easily see how the code got to where it is now. If you have any questions or comments, or suggestions for the next ListView tutorial, please leave them below.

fblin
04/09/2009 - 09:23

This is scaring! Just to make a simple update...
Ufff.. It's going to be hard to leave Asp.Net or windows form considering thet you can do all these in a second!

reply

scromer17
06/09/2009 - 10:52

I agree, updating and even just pulling data seems to be much more difficult and less clear.

reply

Anonymous
04/15/2010 - 07:02

I totally have to agree with these guys. If updating a simple grid is not easier than this, I have to stick with more productive programming languages and environments!

reply

The Reddest
04/15/2010 - 09:04

Since .NET 4.0 has been released, you could just use the new DataGrid control.

reply

Anonymous
07/22/2011 - 06:45

I was faced with problem, that datagrid is too slow even if virtualization is used.

reply

bindy
04/16/2009 - 04:51

that's awesome man!

I had some difficulties to do it a few weeks before (I'm a beginner), but after testing a lot of different stuff, your tutos are so simple and clear!

nice job! hope to see some others pretty soon...

reply

scromer17
06/09/2009 - 10:59

Great article, can you post some code of this actually pulling data from SQL to fill the grid and then updating into SQL? I'm used to handling these in Update or Edit events, WPF seems to do away with these. I can see where to pull the data in from the database where you declare the ListViewTest() GameDataCollection but I have debugged it 5 times looking for a way to update the DB with the edited fields but I don't see it.

Thanks!
Sam

reply

The Tallest
06/09/2009 - 12:33

The WPF GridView is poor man's data grid - if you want a data grid functionalilty similar to what was available in WinForms, you should take a look at the WPF Toolkit, which has a full DataGrid (http://wpf.codeplex.com/). The DataGrid will eventually be shipped as part of WPF, but for now it is a separate piece. If you would like, I could write up a tutorial or two on using it.

reply

The Reddest
06/19/2009 - 10:05

A WPF DataGrid tutorial has been posted.

reply

scromer17
06/09/2009 - 13:56

That would be awesome.

Thanks!
Sam

reply

Omprakash
06/19/2009 - 09:48

Just like all the other WPF articles on this website, This post is also too good. Especially for a learner like me it is really useful.

reply

Anonymous
07/05/2009 - 03:00

I am also a WPF fan, You can directly contact with me by MSN: zhoujiguo1985@live.cn. so we can instantly
communicate with each other. thanks.

reply

Anonymous
07/05/2009 - 03:00

I am also a WPF fan, You can directly contact with me by MSN: zhoujiguo1985@live.cn. so we can instantly
communicate with each other. thanks.

reply

Anonymous
07/05/2009 - 03:01

I am also a WPF fan, You can directly contact with me by MSN: zhoujiguo1985@live.cn. so we can instantly
communicate with each other. thanks.

reply

tshaiman
08/14/2009 - 10:52

Great Article , thanks !

I wonder if there is a way to enable the editing ,not by selecting the item , but for example as a response to keyboard (lets say user presses F2 , the same as many application enables editing ).

what will be the strategy here ? thanks.

reply

RashmiWagh
10/07/2009 - 04:06

Hi,
Can you please share the code for in place editing a single cell in List View...Moreover I would also like to know which one is better between Datagrid and ListView in WPF in terms of functionality..

reply

frzjalali
10/29/2009 - 05:49

Great WPF article !

I really enjoy it.
May I ask two question :
How can I use nested Properties ?

reply

Anonymous
03/19/2010 - 05:18

Excellent article on a topic which I have seen a million workarounds that skirt around the point.

reply

Anonymous
03/25/2010 - 15:41

How do you upadate your collection ? If you update GameCollection after loading, data are not updated in the grid.

That's weird.

reply

zknight
03/27/2010 - 16:50

great article!!

I'm doing the same thing but I have two listview controls on for books and one for chapters.

I have coded the Library, Book, Chapter do objects to be observableCollections and all properties are public and dependency properties. I have my data in a layered object called Library and it has one Library even tho it is a observable collections as is my list of books and list of chapters. Can I set this Library object to data context?

ex: of xml data:

<lib title="lib1">
  <books>
    <book title="book1">
      <chapters>
        <chapter title="chapter1"/>
        <chapter title="chapter2"/>
      </chapters>
    <book>
  </books>
</lib>

You get the idea. I want to set one list to books and other to chapters. How do I do this and right now both list are blank showing no columns at all?

I have read both articles #1 and #3.

Sample: WPF XAML

<Window x:Class="VirtualLibrary.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:VirtualLibrary"
    Title="MainWindow" Closing="Window_Closing" Height="436" Width="622"
    DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Window.Resources>
        <local:BoolToVisibilityConverter x:Key="boolToVis" />
        <Style TargetType="{x:Type TextBlock}" x:Key="GridBlockStyle">
            <Setter Property="VerticalAlignment" Value="Center" />
            <Setter Property="Visibility" Value="{Binding Path=IsSelected,
                RelativeSource={RelativeSource FindAncestor,
                AncestorType={x:Type ListViewItem}},
                Converter={StaticResource boolToVis},
                ConverterParameter=False}"
/>
        </Style>
        <Style TargetType="{x:Type FrameworkElement}" x:Key="GridEditStyle">
            <Setter Property="VerticalAlignment" Value="Center" />
            <Setter Property="Visibility" Value="{Binding Path=IsSelected,
                RelativeSource={RelativeSource FindAncestor,
                AncestorType={x:Type ListViewItem}},
                Converter={StaticResource boolToVis},
                ConverterParameter=True}"
/>
        </Style>
    </Window.Resources>
    <Grid>
        <DockPanel Name="topPanel" Margin="0,0,0,12">
            <Menu Height="22" Name="appMenu" Width="Auto" DockPanel.Dock="Top" VerticalAlignment="Top">
                <MenuItem Header="File" Click="FileOpen_Click"/>
                <MenuItem Header="Help" />
                <MenuItem Header="About" Click="About_Click"/>
            </Menu>
        </DockPanel>
        <StackPanel Name="booksPanel" Margin="0,40,0,10">
            <Label Height="28" Margin="10,10,0,0" Name="booksLabel" VerticalAlignment="Top" HorizontalAlignment="Left" Width="69">Books</Label>
            <ListView ItemsSource="{Binding ElementName=This, Path=LibraryCollection.Library.Books}"  Height="100" Width="580" Name="booksListView">
                <GridView>
                    <GridViewColumn Width="140" >
                        <GridViewColumnHeader Click="SortClick" Tag="BookTitle" Content="Book Title"/>
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Grid>
                                    <TextBlock Text="{Binding Path=BookTitle}" Style="{StaticResource GridBlockStyle}"/>
                                    <TextBox Text="{Binding Path=BookTitle}" Style="{StaticResource GridEditStyle}"/>
                                </Grid>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Width="140" >
                        <GridViewColumnHeader Click="SortClick" Tag="BookDescription" Content="Book Description"/>
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Grid>
                                    <TextBlock Text="{Binding Path=BookDescription}" Style="{StaticResource GridBlockStyle}"/>
                                    <TextBox Text="{Binding Path=BookDescription}" Style="{StaticResource GridEditStyle}"/>
                                </Grid>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Width="140" >
                        <GridViewColumnHeader Click="SortClick" Tag="BookAuthor" Content="Book Author"/>
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Grid>
                                    <TextBlock Text="{Binding Path=BookAuthor}" Style="{StaticResource GridBlockStyle}"/>
                                    <TextBox Text="{Binding Path=BookAuthor}" Style="{StaticResource GridEditStyle}"/>
                                </Grid>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                </GridView>
            </ListView>
            <Label Height="28" Margin="10,10,0,0" Name="chaptersLabel" VerticalAlignment="Top" HorizontalAlignment="Left" Width="69">Chapters</Label>
            <ListView ItemsSource="{Binding ElementName=This, Path=LibraryCollection.Books[0].Chapters}"  Height="100" Width="580" Name="chaptersListView">
                <GridView>
                    <GridViewColumn Width="140" >
                        <GridViewColumnHeader Click="SortClick" Tag="ChapterTitle" Content="Chapter Title"/>
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Grid>
                                    <TextBlock Text="{Binding Path=Chapter.ChapterTitle}" Style="{StaticResource GridBlockStyle}"/>
                                    <TextBox Text="{Binding Path=Chapter.ChapterTitle}" Style="{StaticResource GridEditStyle}"/>
                                </Grid>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Width="140" >
                        <GridViewColumnHeader Click="SortClick" Tag="ChapterDescription" Content="Chapter Description"/>
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Grid>
                                    <TextBlock Text="{Binding Path=Chapter.ChapterDescription}" Style="{StaticResource GridBlockStyle}"/>
                                    <TextBox Text="{Binding Path=Chapter.ChapterDescription}" Style="{StaticResource GridEditStyle}"/>
                                </Grid>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Width="140" >
                        <GridViewColumnHeader Click="SortClick" Tag="Chapter.ChapterNumber" Content="Chapter Number"/>
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <Grid>
                                    <TextBlock Text="{Binding Path=Chapter.ChapterNumber}" Style="{StaticResource GridBlockStyle}"/>
                                    <TextBox Text="{Binding Path=Chapter.ChapterNumber}" Style="{StaticResource GridEditStyle}"/>
                                </Grid>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                </GridView>
            </ListView>
        </StackPanel>
        <!--<Label Height="28" Margin="12,33,0,0" Name="ContentLabel" VerticalAlignment="Top" HorizontalAlignment="Left" Width="69">Contents</Label>-->
        <Button Height="23" Margin="0,0,12,12" Name="exitButton" Click="ExitButton_Click" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="75">Exit</Button>
        <!--<TextBox Margin="12,62,12,0" Name="contentTextBox" Height="55" VerticalAlignment="Top" />-->
    </Grid>
</Window>

Help! zKnight

reply

zknight
03/27/2010 - 16:53

This seems like a great web site and I'll be spending more time here. Note to web master the edit box for posting code should be wider!! Thanks zKnight

reply

PD
11/29/2010 - 11:50

Excellent tutorial thanx

reply

PD
11/29/2010 - 11:52

Can it be created without xaml?

reply

PD
11/29/2010 - 11:53

is it possible to do it full in code

reply

The Reddest
11/29/2010 - 14:19

In the end, XAML simply compiles down to C# code. These files are named *.g.cs. Everything that can be done in XAML can be done in C# code. The code required to duplicate this example would be too much for a comment, however I can tell you it is possible - just search for each piece separately (binding, styles, etc.) and put the pieces together.

reply

Anonymous
12/16/2010 - 14:24

This was a great tutorial. Any idea though why my first row doesn't seem to work but the rest do. I select it and it goes into edit mode but when I select another row the readonly version of the first row does not reflect the changes. All the other rows work. I am binding to a DataView.

reply

Anonymous
12/16/2010 - 15:21

Nevermind. I was porting a WinForms screen and it was binding to a CurrencyManager. Binding directly to the DataView fixed this.

reply

Drew
12/21/2010 - 16:14

I am getting an error when adding

to my xaml file. its in the right place and i have added the BoolToVisibilityConverter function on the back end. The error says "The type 'local:BoolToVisibilityConverter' was not found. Verify that you are not missing an assembly reference and that all referenced assemblies have been built.'

what did i do wrong? thanks!

reply

Drew
12/21/2010 - 16:15

that should have said i am getting an error when adding : <my:BoolToVisibilityConverter x:Key="boolToVis" />

reply

Drew
12/21/2010 - 16:56

I am using an autocompletebox tool from the WPF Tool Kit, and they said to change my clr-namespace property to: xmlns:my="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input.Toolkit"

I think this is causing the problem as the BoolToVisibilityConverter function needed requires this value to point to my cs file. Not sure what to do

reply

The Reddest
12/21/2010 - 18:23

The namespace (e.g. xmlns:my) must point to the assembly that contains the class. Since you've pointed "my" to System.Windows.Controls.Input.Toolkit, the compiler can no longer find BoolToVisibilityConverter because that class is not in that assembly.

If you want to keep using "my" to point to the Toolkit, you'll have to create another namespace that points to the assembly that contains the BoolToVisibilityConverter class. This is probably the assembly you're currently building, since this object is defined by you.

xmlns:myNewNamespace="clr-namespace:YourProjectName"

Then reference BoolToVisibilityConverter using the new namespace:

<myNewNamesapce:BoolToVisibilityConverter />

reply

Drew
12/22/2010 - 08:18

thanks so much for the help. i didn't know you could add multiples. i am probably too new to WPF to be tackling this. now it is grumbling about the fact that i am using DataContext="{Binding RelativeSource={RelativeSource Self}}". i think i need to bind my datacontext after the window.resources section, but i dont see a way to do that and still use relativesource.

reply

Drew
12/22/2010 - 10:14

its working now and i dont know why. i need to stop flying by the seat of my pants! :) anyway thanks for the article man great stuff

reply

The Reddest
12/22/2010 - 10:47

Glad you got it working! The fastest way to learn is just to dive in and figure it out as you go. One of the best WPF books I've come across is WPF Unleashed - it may be worth checking it out.

reply

Anonymous
12/23/2010 - 11:17

Hi,

Thanks for a great tutorial!

Why did you choose to use two different Styles - toggling the Visibility - rather than using a DataTemplateSelector to toggle two different DataTemplates?

I would think toggling the DataTemplates would be a more efficient solution because it would cut your Bindings in half. It seems that by toggling the Visibility, you always have twice the elements being updated by the Binding engine than needed.

I am intrigued by the field level manipulation, but I am thinking that managing the fields at a DataTemplate level may be more readable / maintainable / efficient.

I am fairly new to WPF, so I welcome your thoughts. Thanks again for a great tutorial!

Bill

reply

Andrew
01/19/2011 - 10:09

is there a way to put the cursor focus in the textbox when you click in once? it seems that you have to click the textblock once to show the textbox, then click again to set the focus.

reply

Andrew
01/21/2011 - 15:33

this did the trick:

http://stackoverflow.com/questions/92328/how-can-i-access-the-listviewitems-of-a-wpf-listview

reply

Mrityunjay
02/25/2011 - 08:00

Hi,

Nice article can you please let me know how to change the Editing on F2 key press instead of selectioin.
It's urgent can u pls!!!!!

reply

Anonymous
05/24/2011 - 21:12

Hello,

I am aware that this is an old article, but still pretty awesome.
I took out the DependencyObject stuff and just used the old GameData class, guess what, it still works fine! Any particular purpose for that?

If it's not too much, a tutorial on FILTERING would be very much appreciated. I was hoping for filtering instead of in-edit placement based on your last comment of the 2nd tutorial.

-Aris

reply

sachin
07/21/2011 - 02:28

yaar its too good everything is perfect

reply

Devam
08/09/2011 - 20:49

I like the way you explain. I have one specific requirement. In my case, there are 2 lists, say Assigned and Unassigned users. How to add, remove, add all or remove all listviewitems to and from the given two lists.

any help will be appreciated. Thanks.

Devam

reply

Shaily
08/10/2011 - 01:52

.....

And Property is Declared like as under

private bool _isChecked = true;
public bool IsChecked
{
get
{
return _isChecked;
}
set
{
if (_isChecked == value)
return;
_isChecked = value;
NotifyPropertyChange("IsChecked");
}
}

reply

Shaily
08/10/2011 - 01:57

And Listview is bound like this..

<ListView ItemsSource="{Binding Data}">
     <ListView.View>
            <GridView>
            <GridViewColumn>
              <GridViewColumn.CellTemplate>
                <DataTemplate>
        <CheckBox IsChecked="{Binding Path=IsChec}"/>
                </DataTemplate>

          </GridViewColumn.CellTemplate>

But still checkbox is not binding...

reply

The Reddest
08/12/2011 - 18:38

Your property is named "IsChecked" and you're binding to a property named "IsChec"

reply

sravan
09/24/2011 - 09:18

protected void viewdb_Click(object sender, EventArgs e)
{
con = new SqlConnection("Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=SRAVAN;Data Source=HOME\\SQLEXPRESS");
con.Open();
str = "select * from emp";
da = new SqlDataAdapter(str, con);
ds = new DataSet();

da.Fill(ds, "emp");

GridView1.DataSource = ds.Tables[0];

}

this is code to display emp table in ASP web page using grid view but its not executing properly please help me .
thank u;

reply

Yasin
10/22/2011 - 01:57

Hi I need ,button click after edit row help me

reply

Dieter
11/21/2011 - 10:05

I know this is a old article but I am new(bee) and the theme is a evergreen. This article is very informative so I like to try some more.
If you have a lot of columns, can the listview a row wrap around in a second line?

reply

Mr-H
01/18/2012 - 18:03

thanks man, this tut is really helpfull :)

reply

Add Comment

Put code snippets inside language tags:
[language] [/language]

Examples:
[javascript] [/javascript]
[actionscript] [/actionscript]
[csharp] [/csharp]

See here for supported languages.

Javascript must be enabled to submit anonymous comments - or you can login.
CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.