The WPF ListView Control – the complete Guide in 2024

WPF ListView Control - the complete Guide
WPF ListView Control – the complete Guide

The WPF ListView – listing items / things

In today’s post, we are going to talk about one of the most basic controls: The WPF ListView. We will take a look at the different use cases of the ListView and we will try out some of the architectural styles. As MVVM is like the „way to go“ in WPF, I will definitely examine this, but we won’t forget the „old way“. Like always, I will provide different source code examples in the two most used .NET languages, being C# and VB.NET.

💡 Hint: Need quick help? Jump to the corresponding sections by using the table of contents. When you are using the code behind based approach, jump there. If you are more the MVVM guy, you could jump and start with the „MVVM approach“ section.

What is a WPF ListView?

When stumbling upon this control, the first question coming to your mind could be: „Well, what is a ListView – in the Windows Presentation Foundation – in the first place?“. I mean, by the name, you could just assume, that it’s something for displaying like a „list“ of something, right? I mean, you are pretty much on the right side, but what makes it different from like a DataGrid, or even a ListBox? Don’t worry, we will cover this is a few minutes, further down below.

The definition, I guess?

Let’s first define, what a ListView really is by like definition:

A WPF ListView provides the basic tools needed, to help you displaying a set of data items. This can be easily done in like different views or layouts, depending on your usecase. Each item of those ListViews are displayed, using something called ListViewItem. A ListViewItem iteself is – more or less – just another template-ish element, being a ContentControl.

Examining the ListView

Let’s take a quick sneak peek into the inheritance-tree, of the WPF ListView Control, by pressing F12, while being on one of those with your mouse cursor. There you can see, that it actually inherits from the class ListBox.

WPF ListView Control Class - inheriting from ListBox
WPF ListView Control Class – inheriting from ListBox

Inheritance tree

Going further down, there are some other classes in the inheritance tree as well, like the Selector & the ItemsControl class. I think, for most persons, it’s actually a surprise / a „good to know“-thing, that the ListView actually inherits from the ListBox class.

WPF ListView Inheritance Tree
WPF ListView Inheritance Tree

At this point, you can see, that there’s pretty much going on in the background. The ListView itself is actually derived from a few levels of control hierarchy and is therefore bringing a lot „to the table“. For sure, we can’t go to every level in detail, but I think the most important ones for you are beginning at the ItemsControl.

I mean, in a real application scenario, you would most likely fetch some data like the „charts“ or something like that. Those could then be displayed inside your ListView by specifying what should actually appear. The chart object could be composed from many different objects and therefore you would need to specify a bit further.

A first example – with XAML & code behind

A first WPF ListView example – hardcoded in XAML
A first WPF ListView example – hardcoded in XAML

Let’s now focus on creating an actual first example of the ListView Control. To do so, we usually need to first have some kind of data to display. At this point, we will just create some hardcoded items, to see, how the ListView and its ListViewItems are created in the first place. Keep in mind, that we will do this and more in an MVVM manner, later – which is the preferred way for me personally.

We will surround the ListView by a GroupBox Control, to have some kind of labelling, etc. So go ahead and create a GroupBox XAML markup inside your MainWindow.xaml (inside the root Grid). You can write something like „Music stars“, etc. inside the Header Property of that GroupBox, like this, we have some kind of heading for our list.

After that, we can add some sort of styling like padding, a width and alignments to that GroupBox, to make it look less bruised. The final, but first testing XAML code could therefore look like this:

    <Grid>
        <GroupBox Header="Some items" Padding="8" Width="200" VerticalAlignment="Center" HorizontalAlignment="Center">
            <ListView>
                <ListView.Items>
                    <ListViewItem Content="Jimmy Hendrix" />
                    <ListViewItem Content="Michael Jackson" />
                    <ListViewItem Content="The Beatles" />
                </ListView.Items>
            </ListView>
        </GroupBox>
    </Grid>

The visual representation of that XAML code from above, will just look as simple as this.

ListView – Displaying Music stars as simple list
WPF ListView – Displaying Music stars as simple list

How to add items to the WPF ListView?

To add some items to the ListView in a „code behind“-manner, we need to specify a usual handler for like the click event, of for example a button. We can then react to that click inside of the handler and actually add a new item to the ListView. Go ahead and add a button inside of your MainWindow (Grid) and specify the „Click“-Property.

<Button Click="Button_Click" />

It will suggest, to create an actual handler, you can confirm this and press F12 while being on the „Button_Click“ text. After that, it will automatically jump to the corresponding part in your code – the code behind file. Feel free to rename that handler (in XAML & code) to pick a better name, like „btnAddItem_Click“.

Inside of the click handler, we need to somehow say: „Hey, ListView, please remove an item!“. And we are only able to do so, if we have something to talk to. Currently, the ListView doesn’t have a name, so you should go back and specify a name for it:

<ListView x:Name="MusicArtistList" ....>
    <!-- .... -->
</ListView>

The button click handler itself could just look like the following. We are just telling the property of the „MusicArtistList“, called „Items“: „Hey, please add another entry to yourself, thanks!“.

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        MusicArtistList.Items.Add("Sam Smith");
    }
    Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
        MusicArtistList.Items.Add("Sam Smith")
    End Sub

How to remove items from the ListView again?

If you have followed the above steps to actually add an item inside of our ListView, then you could be ready to remove it again. Keep in mind, that you should have like the click handler ready (by specifying the Buttons click property with like „btnRemove_Click“) and that the ListView needs a name. Otherwise, we wouldn’t be able to target it from the code behind file.

    private void btnRemove_Click(object sender, RoutedEventArgs e)
    {
        // this only works that easy due to our current "strings" example
        MusicArtistList.Items.Remove("Sam Smith");
    }
    Private Sub btnRemove_Click(sender As Object, e As RoutedEventArgs)
        ' this only works that easy due to our current "strings" example
        MusicArtistList.Items.Remove("Sam Smith")
    End Sub

How to delete the selected item in the ListView?

This one here is a bit different, because we can’t know at compile time, which one of the items, the user will select. We can’t therefore hardcode like a specific string thing, to be deleted later. We need a possibility, to get the currently selected item and delete it somehow.

This is actually pretty easy, as we get everything we need to know, from the ListView itself. We will first check, if there’s an actual selection and if so, we will delete that item by providing its index.

    private void btnRemoveSelectedItem_Click(object sender, RoutedEventArgs e)
    {
        bool noItemSelected = MusicArtistList.SelectedIndex == -1;
        if (noItemSelected)
            return;
        MusicArtistList.Items.RemoveAt(MusicArtistList.SelectedIndex);
    }
Private Sub btnRemoveSelectedItem_Click(sender As Object, e As RoutedEventArgs)
    Dim noItemSelected As Boolean = MusicArtistList.SelectedIndex = -1
    If noItemSelected Then
        Return
    End If
    MusicArtistList.Items.RemoveAt(MusicArtistList.SelectedIndex)
End Sub

Okay, but not flexible, nor practical – imo

So in the end, we just created a small ListView representation with like 3 entries for our music stars / artists. Currently, this isn’t really good, as it’s not dynamic or based on „real data“. We just hardcoded those ListViewItems inside the ListView and this isn’t pretty much practical.

The other problem with our current example is, that it’s not pretty flexible, currently we are having only like „1 Column“. With that, we can only display one „thing“ per item and we are not really different from a ListBox. What if we wanted to publish some sort of „year of birth“ information? I mean sure, we could just pump it into one string, but nah, that doesn’t feel right.

What we really need are some sort of columns and guess what, the ListView supports this out of the box, by just specifying the View property.

Working with „real data“ – MVVM approach

Working with a databound ListView – dynamic data
Working with a databound ListView – dynamic data

Moving away from that unpractical non-databased „stringish“ approach from above, we will now start a newer, clean version of our ListView Control. But first, we need to actually define „what“ to display, I mean, we need some sort of a so called „Model“ (or even a ViewModel – but I’ll leave it at that for now). As we wanted to display some sort of „music star“-overview, we could think of a better term in the first place. I will call it list of artists, so let’s now create a new class called „MusicArtist“ inside a matching folder.

Go ahead and create the corresponding folder inside your WPF project, first. We will call that folder like „Models“ – remember, I don’t want to go too deep into the MVVM cosmos. I would encourage you to refer to my C# MVVM post for that and don’t be confused, it will for work VB.NET as well. I mean the characteristics of Model-View-ViewModel are in the focus, not the language itself :).

The „MusicArtist“ model class

Thinking about the class itself and keeping our example from above in mind, we could say, that a music artist has the following properties. For sure, it’s just for our example and I’m trying to stay as easy as possible, so feel free to change anything you would like to.

using System;

namespace WpfListViewExampleCs.Models;

public class MusicArtist
{

    public string FirstName { get; set; }

    public string LastName { get; set; }

    public DateTime Birthday { get; set; }

    public MusicArtist(string firstName, string lastName, DateTime birthday)
    {
        FirstName = firstName;
        LastName = lastName;
        Birthday = birthday;
    }
}
Imports System

Namespace WpfListViewExampleCs.Models

    Public Class MusicArtist

        Public Property FirstName As String

        Public Property LastName As String

        Public Property Birthday As DateTime

        Public Sub New(firstName As String, lastName As String, birthday As DateTime)
            Me.FirstName = firstName
            Me.LastName = lastName
            Me.Birthday = birthday
        End Sub

    End Class

End Namespace

In my example here, the music artist has two properties for the name, meaning a first- and a lastname. I don’t like the „name“ only approach, as you can’t easily separate the names in some situations. Well, I mean yeah, before you mention it: „The Beatles“ isn’t really nicely separatable in this manner, but, welcome to the world of software development. I would say, this isn’t the right moment to argue about this, so feel free – again – to change that as you wish. Additionally I thought about using the birthday property, to show some sort of formatting in the XAML as well.

Displaying bound data inside the WPF ListView

Displaying bound data inside the ListView
Displaying bound data inside the WPF ListView

After specifying some kind of model to actually display inside the ListView, we now go ahead and tell the ListView about its datasource. The ListView will then know: „Where can I fetch my data from? What is my data?“.

Preventing a known error

But before, we need to clean up the old code, otherwise we could run into an error like this:

System.InvalidOperationException: „The items collection must be empty before using itemssource“

To avoid this error, you need to remove the manually added items inside our XAML code. You can’t like say: „Hey, fetch the items from this datasource here“ AND put them in manually by XAML. Please go ahead and remove this marked area from the XAML code:

Removing the hardcoded XAML Items from the ListView
Removing the hardcoded XAML Items from the ListView

Giving the ListView its datasource

In the next step, we will actually care for some items being displayed. For this, we will use the DataSource property of the ListView and bind to some property from our ViewModel. Don’t be scared, if you don’t know what a ViewModel is! Maybe this is another chance, to refer to my blog post considering the MVVM pattern.

<ListView ItemsSource="{Binding MusicArtists}">
<!-- rest of the listview.. -->
</ListView>

We won’t create a separate ViewModel right now, for this simple example, we will use the MainWindow itself. This is pretty much easier at this point and it won’T push you too far away from the actual topic. Never the less, we still need a bindable property inside our MainWindow, which we are going to create, now.

Creating a bindable property

To create the announced property, just build some normal property inside your MainWindow (for example) called „MusicArtists“. This could look like the following code, keep in mind, that we should use something which implements the „INotifyCollectionChanged“ interface. But yes, I hear you, this should be a topic for another blog post, so just take it like that for now.

Next, create that property called „MusicArtists“ of type of „ObservableCollection“, with the generic type argument „MusicArtist“. We could also just make it like readonly, but those are fine tuning things, which aren’t necessary right now.

The last important step is, to say: „Hey, MainWindow, your DataContext is actually.. you!“. Then the MainWindow will know where to find the corresponding „MusicArtists“, which we are going to bind very soon from the XAML side. Take a look at the following example XAML code:

using System;
using System.Collections.ObjectModel;
using System.Windows;
using WpfListViewExampleCs.Models;

namespace WpfListViewExampleCs;

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{

    public ObservableCollection<MusicArtist> MusicArtists { get; set; }

    public MainWindow()
    {
        InitializeComponent();
        MusicArtists = new ObservableCollection<MusicArtist>();
        DataContext = this;
    }
}
Imports System
Imports System.Collections.ObjectModel
Imports System.Windows
Imports WpfListViewExampleCs.Models

Namespace WpfListViewExampleCs

    Public Partial Class MainWindow
        Inherits Window

        Public Property MusicArtists As ObservableCollection(Of MusicArtist)

        Public Sub New()
            InitializeComponent()
            MusicArtists = New ObservableCollection(Of MusicArtist)()
            DataContext = Me
        End Sub

    End Class

End Namespace

Filling up some data

Currently, the ListView wouldn’t be able to display much, because there just aren’t no items inside the „MusicArtists“, yet. So in the next step we are going to fill up some items, to make the ListView be able to display those. This is the reason, why we added a quick constructor inside the „MusicArtist“ class: Creating quick example artists.

Let’s re-create the MainWindow constructor for this:

// rest of the MainWindow code behind file

    public MainWindow()
    {
        InitializeComponent();
        MusicArtists = new ObservableCollection<MusicArtist>();
        MusicArtists.Add(new MusicArtist("Jimmy", "Hendrix", new DateTime(1942, 11, 27)));
        MusicArtists.Add(new MusicArtist("Michael", "Jackson", new DateTime(1958, 8, 29)));
        MusicArtists.Add(new MusicArtist("The", "Beatles", new DateTime(1960, 1, 1)));
        DataContext = this;
    }

// rest of the MainWindow code behind file
' rest of the MainWindow code behind file

Public Sub New()
    InitializeComponent()
    MusicArtists = New ObservableCollection(Of MusicArtist)()
    MusicArtists.Add(New MusicArtist("Jimmy", "Hendrix", New DateTime(1942, 11, 27)))
    MusicArtists.Add(New MusicArtist("Michael", "Jackson", New DateTime(1958, 8, 29)))
    MusicArtists.Add(New MusicArtist("The", "Beatles", New DateTime(1960, 1, 1)))
    DataContext = Me
End Sub

' rest of the MainWindow code behind file

But it’s displaying rubbish now?

If you are looking at the current results, you would pretty much only see rubbish. If you didn’t look until now, please watch the following screenshot, then you probably will. The items are more or less being displayed, but there’s only something like „Blabla.Bla.MusicArtist“ written.

Bound ListView Items displaying rubbish without Column Bindings
Bound ListView Items displaying rubbish without Column Bindings

The reason for that is, that the corresponding ListViewItem actually knows: „Hey, there is something called MusicArtist“. But it’s also asking itself: „well, how am I supposed to display this? Property A, B, C, WHAT!?“. In the end it thinks: „You know what, dev? I’m just going to display THAT over here“.

By „that“ I’m meaning the complete path to the corresponding class instance type. We are having a class called „MusicArtist“ inside our project called „WpfListViewExampleCs“ AND inside the „Models“ namespace. So this is basically why that „rubbish“ comes out of it, it displays the complete namespace to class path!

Overriding that „default-rubbish“

Before we are going to use the final solution for this, I first want to explain and show you, how you could potentially solve it at this point. Please keep in mind, that this solution depends on your usecase! Usually, if you want to display like „multiple columns“, you would actually need – well – multiple columns.

In the image from above, you are seeing the default object class „ToString“ output. To change it, you can just override it in your derived class – being the „MusicArtist“-class like in a second. Hint: I’m using the newer, shorter version of the overriding functionality. This means, no body parantheses and an arrow (for C#), as well as string interpolation (for both languages).

You could add this to the „MusicArtist“ class:

// rest of the MusicArtist class

public override string ToString()
    => $"{FirstName} {LastName}";

// rest of the MusicArtist class
' rest of the MusicArtist class

Public Overrides Function ToString() As String
    Return $"{FirstName} {LastName}"
End Function

' rest of the MusicArtist class

So the current / new output would look like this:

Bound ListView Items with overriden ToString Function
Bound ListView Items with overriden ToString Function

Using the ListViews „View“ Property creating columns

Creating columns using the ListView View Property
Creating columns using the ListView View Property

After cleaning up that „namespace and class“-mess we saw in the last section, we now can go the last step (I think). Currently, our ListView is only displaying some kind of single dimensioned data, which is not thaaaat bad, but I guess bad for multi-dimensioned data? What if I wanted to have some sort of separated columns for first and last name? This is exactly, where the so called „View“-property of the WPF ListView comes into play!

But why a ListView, why no DataGrid instead?

Sure, you could ask yourself: „Why would I use a ListView, when I could use a DataGrid instead?“. The answer is: „It depends“, yeah, I hate those answers as well.. Usually there’s one rule floating around in the internet: Use a DataGrid, if you need editing, use a ListView otherwise – bang. I would pretty much love going deeper into this topic, but well, I think this post is already too big, isn’t it?

Specifying a „View“-template

To start creating those columns we talked about, we need to specify some sort of template for the View property. But it’s not a usual „DataTemplate“, it’s something more special, lemme give you an example. The basic view provided from WPF itself, is something called a „GridView“. I mean, the name pretty much suggests what it is. If needed, you could create something similiar for your own needs, but again, I think this would blow up this post even more..

So let’s stick with the basic GridView for now. Back into your MVVM prepared ListView, we can now provide a GridView template for our ListView. Watch out for that nice string format functionality, where I specified a custom format for that birthday binding. Basically all you have to do is, providing the columns you want, with a header of your choice. Then you need to tell the ListViewItem, where it should pull the data from (being the DisplayMemberPath-Binding).

            <ListView ItemsSource="{Binding MusicArtists}">
                <ListView.View>
                    <GridView>
                        <GridView.Columns>
                            <GridViewColumn Header="FirstName" DisplayMemberBinding="{Binding FirstName}" />
                            <GridViewColumn Header="LastName" DisplayMemberBinding="{Binding LastName}" />
                            <GridViewColumn Header="Birthday" DisplayMemberBinding="{Binding Birthday, StringFormat={}{0:dd.MM.yyyy}}" />
                        </GridView.Columns>
                    </GridView>
                </ListView.View>
            </ListView>

And finally, you end up having this nice thing here:

WPF ListView with specified View Property as GridView
WPF ListView with specified View Property as GridView

Adding, removing, etc. the MVVM way – preparations

Because it’s a bit harder, I saved this topic (and other) for the end of this blog post. You actually need some sort of „ICommand“ implementation, which we won’t cover here, but I will give you the example code needed. There will be another blog post about those commands in english, currently, it’s only available in german, sorry.

A DelegateCommand base class

Please go ahead and create another folder inside your project, called „Utils„. We will deposit a small class over there, which helps us with the buttons in MVVM style. The class I’m talking about looks like this, it’s a basic implementation of a delegated action (with check) for the ICommand-interface:

using System.Windows.Input;
using System;

namespace Utils
{
    public class DelegateCommand : ICommand
    {
        private Action<object?> _action;

        private Func<object?, bool>? _canExecute;

        public DelegateCommand(Action<object?> action)
        {
            _action = action;
        }

        public DelegateCommand(Action<object?> action, Func<object?, bool> canExecute)
        {
            _action = action;
            _canExecute = canExecute;
        }

        public void Execute(object? parameter)
        {
            _action(parameter);
        }

        public bool CanExecute(object? parameter)
        {
            if (_canExecute == null)
                return true;
            return _canExecute(parameter);
        }

        public void RaiseCanExecuteChanged()
        {
            CanExecuteChanged?.Invoke(this, EventArgs.Empty);
        }

        public event EventHandler? CanExecuteChanged;
    }
}
Namespace Utils
    Public Class DelegateCommand
        Implements ICommand
        Private _action As Action(Of Object)
        Private _canExecute As Func(Of Object, Boolean)
        Sub New(action As Action(Of Object))
            _action = action
        End Sub
        Sub New(action As Action(Of Object), canExecute As Func(Of Object, Boolean))
            _action = action
            _canExecute = canExecute
        End Sub
        Public Sub Execute(parameter As Object) Implements ICommand.Execute
            ' führt unsere von außen mitgegebene Aktion aus und übergibt auch den Parameter
            _action(parameter)
        End Sub
        Public Function CanExecute(parameter As Object) As Boolean Implements ICommand.CanExecute
            If _canExecute Is Nothing Then
                Return True
            End If
            Return _canExecute(parameter)
        End Function
        Public Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
    End Class
End Namespace

A PropertyChangedBase class

Without going into too much detail, please copy this class and put it into the „Utils“ folder as well. There is a blog post for the INotifyPropertyChanged-interface, but currently it’s only in german. Maybe you could use a browser-translator for now, but I will add the english version soon. So now, please take this class:

using System.Runtime.CompilerServices;
using System.ComponentModel;

namespace Utils;

public abstract class PropertyChangedBase : INotifyPropertyChanged
{
    protected void NotifyOfPropertyChange([CallerMemberName] string? propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    public event PropertyChangedEventHandler? PropertyChanged;
}
Imports System.ComponentModel
Imports System.Runtime.CompilerServices

Namespace Utils

    Public Class PropertyChangedBase
        Implements INotifyPropertyChanged

        Protected Sub NotifyOfPropertyChange(<CallerMemberName> Optional propertyName As String = Nothing)
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
        End Sub

        Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    End Class

End Namespace

Corresponding buttons

Please add something like a StackPanel and 3 Buttons inside of it. There will be one button for adding and one for removing an item at a specific position. The third button will be for removing the selected item. At this point, we will also those commands, please have a look at the following XAML. Please pardon me for not making this visually appealing, it’s only for its functional purpose.

Keep in mind, that you also need to create 3 new properties of type of „DelegateCommand“, 1 for each command, being:

  • AddItemCommand
  • RemoveItemCommand
  • RemoveItemAtIndexCommand
        <GroupBox Header="Music stars" Padding="8" Width="400" VerticalAlignment="Center" HorizontalAlignment="Center">
            <StackPanel>
                <ListView SelectedItem="{Binding SelectedMusicArtist}" ItemsSource="{Binding MusicArtists}">
                    <ListView.View>
                        <GridView>
                            <GridView.Columns>
                                <GridViewColumn Header="FirstName" DisplayMemberBinding="{Binding FirstName}" />
                                <GridViewColumn Header="LastName" DisplayMemberBinding="{Binding LastName}" />
                                <GridViewColumn Header="Birthday" DisplayMemberBinding="{Binding Birthday, StringFormat={}{0:dd.MM.yyyy}}" />
                            </GridView.Columns>
                        </GridView>
                    </ListView.View>
                </ListView>
                <StackPanel Orientation="Horizontal" Margin="0 12 0 0" HorizontalAlignment="Right">
                    <Button Command="{Binding AddItemCommand}" Content="Add item" Margin="0 0 8 0" />
                    <Button Command="{Binding RemoveItemCommand}" Content="Remove item" Margin="0 0 8 0" />
                    <Button Command="{Binding RemoveItemAtIndexCommand}" Content="Remove item at index" />
                </StackPanel>
            </StackPanel>
        </GroupBox>

The „new“ UI now looks like this:

MVVM based UI
MVVM based UI

The MainViewModel

Now create a new folder called „ViewModels“ and a „MainViewModel“ class inside. Let the „MainViewModel“ class inherit from the „PropertyChangedBase“ you copied from the previous paragraph. The view (code behind) file should now be abandoned as it’s getting too much otherwise.

Over here we fully implemented the „SelectedMusicArtist“-property to actually tell the „RemoveItemCommand“: „Hey, here was something selected, check if you can be executed now“. We’re subscribing to the „CollectionChanged“ event of the ObservableCollection as well. This way, we can also refresh the commands state, when the collection is changed.

In the next steps, we are going to instantiate the commands and therefore telling them, what to do and when.

using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using Utils;
using WpfListViewExampleCs.Models;

namespace WpfListViewExampleCs.ViewModels;

public class MainViewModel : PropertyChangedBase
{

    public ObservableCollection<MusicArtist> MusicArtists { get; set; }

    private MusicArtist? _selectedMusicArtist;

    public MusicArtist? SelectedMusicArtist
    {
        get => _selectedMusicArtist;
        set
        {
            if (_selectedMusicArtist == value)
                return;
            _selectedMusicArtist = value;
            RemoveItemCommand.RaiseCanExecuteChanged();
        }
    }

    public DelegateCommand AddItemCommand { get; set; }

    public DelegateCommand RemoveItemCommand { get; set; }

    public DelegateCommand RemoveItemAtIndexCommand { get; set; }

    public MainViewModel()
    {
        MusicArtists = new ObservableCollection<MusicArtist>();
        MusicArtists.CollectionChanged += MusicArtists_CollectionChanged;
        MusicArtists.Add(new MusicArtist("Jimmy", "Hendrix", new DateTime(1942, 11, 27)));
        MusicArtists.Add(new MusicArtist("Michael", "Jackson", new DateTime(1958, 8, 29)));
        MusicArtists.Add(new MusicArtist("The", "Beatles", new DateTime(1960, 1, 1)));
    }

    private void MusicArtists_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
    {
        RemoveItemCommand.RaiseCanExecuteChanged();
        RemoveItemAtIndexCommand.RaiseCanExecuteChanged();
    }

}
Imports System
Imports System.Collections.ObjectModel
Imports System.Collections.Specialized
Imports Utils
Imports WpfListViewExampleCs.Models

Namespace WpfListViewExampleCs.ViewModels

    Public Class MainViewModel
        Inherits PropertyChangedBase

        Public Property MusicArtists As ObservableCollection(Of MusicArtist)

        Private _selectedMusicArtist As MusicArtist?

        Public Property SelectedMusicArtist As MusicArtist?
            Get
                Return _selectedMusicArtist
            End Get
            Set(ByVal value As MusicArtist?)
                If _selectedMusicArtist = value Then Return
                _selectedMusicArtist = value
                RemoveItemCommand.RaiseCanExecuteChanged()
            End Set
        End Property

        Public Property AddItemCommand As DelegateCommand

        Public Property RemoveItemCommand As DelegateCommand

        Public Property RemoveItemAtIndexCommand As DelegateCommand

        Public Sub New()
            MusicArtists = New ObservableCollection(Of MusicArtist)()
            AddHandler MusicArtists.CollectionChanged, AddressOf MusicArtists_CollectionChanged
            MusicArtists.Add(New MusicArtist("Jimmy", "Hendrix", New DateTime(1942, 11, 27)))
            MusicArtists.Add(New MusicArtist("Michael", "Jackson", New DateTime(1958, 8, 29)))
            MusicArtists.Add(New MusicArtist("The", "Beatles", New DateTime(1960, 1, 1)))
        End Sub

        Private Sub MusicArtists_CollectionChanged(ByVal sender As Object?, ByVal e As NotifyCollectionChangedEventArgs)
            RemoveItemCommand.RaiseCanExecuteChanged()
            RemoveItemAtIndexCommand.RaiseCanExecuteChanged()
        End Sub

    End Class

End Namespace

Assign a ViewModel to the MainWindow

To be able to this all work, make sure, you set an appropriate ViewModel for your MainWindow. Otherwise, the binding system wouldn’t know, where to search for your bindings. This is done by adding this code to your MainWindow XAML file:

<!-- create a namespace mapping at the top of your file -->
xmlns:vm="clr-namespace:WpfListViewExampleCs.ViewModels"

<!-- Set the DataContext MainViewModel instance by XAML -->
<Window.DataContext>
    <vm:MainViewModel />
</Window.DataContext>

How to add WPF ListView items with MVVM

After you got our little helper class from above inside your „Utils“ folder and everything else ready, we can now continue. Adding items is now kinda different, because our items are actually bound and not just simple strings. Our commands wants at least to know, what it should do, when executed. We can pass a function, to let the command determine, if it can be executed. I mean, we don’t want a „remove selected item“ to happen, when there’s no item, right?

The following code is needed for the actual functionality: Instantiate the command inside the constructor (don’t forget to take the command property from above!).

    // other properties

    public DelegateCommand AddItemCommand { get; set; }

    public MainViewModel()
    {
        AddItemCommand = new DelegateCommand(AddItem);

        // list instantiation etc..
    }

    // the delegate function to be executed on click
    private void AddItem(object? parameter)
    {
        // adding miley cyrus, as we have no dynamic dialog, as it's not a part of this here..
        MusicArtists.Add(new MusicArtist("Miley", "Cyrus", new DateTime(1992, 11, 23)));
    }
    ' other properties
   
    Public Property AddItemCommand As DelegateCommand

    Sub New()
        AddItemCommand = new DelegateCommand(AddItem);

        ' list instantiation etc..
    End Sub

    Private Sub AddItem(parameter As Object)
        MusicArtists.Add(New MusicArtist("Miley", "Cyrus", New DateTime(1992, 11, 23)))
    End Sub

Removing the selected item MVVM style

The important thing here is, that we can only remove a selected item, if there is actually one selected. Please check the „SelectedMusicArtist“-property from the above MainViewModel for this. If the selected item (therefore the SelectedMusicArtist) changes, we tell the command to refresh.

    // other properties

    public DelegateCommand RemoveItemCommand { get; set; }

    public MainViewModel()
    {
        RemoveItemCommand = new DelegateCommand(RemoveItem, CanRemoveItem);

        // list instantiation etc..
    }

    private void RemoveItem(object? parameter)
    {
        // we should know that SelectedMusicArtist isn't null, therefore the !
        MusicArtists.Remove(SelectedMusicArtist!);
    }

    private bool CanRemoveItem(object? parameter)
    {
        return SelectedMusicArtist != null;
    }
    ' other properties
   
    Public Property RemoveItemCommand As DelegateCommand

    Sub New()
        RemoveItemCommand = new DelegateCommand(RemoveItem, CanRemoveItem);

        ' list instantiation etc..
    End Sub

    Private Sub RemoveItem(parameter As Object)
        MusicArtists.Remove(SelectedMusicArtist)
    End Sub

    Private Function CanRemoveItem(parameter As Object) As Boolean
        Return SelectedMusicArtist IsNot Nothing
    End Function

Deleting an item by index, MVVM like

In the last example, we do pretty much the same, but with some hardcoded index, just as an example!

    // other properties

    public DelegateCommand RemoveItemAtIndexCommand { get; set; }

    public MainViewModel()
    {
        RemoveItemAtIndexCommand = new DelegateCommand(RemoveItemAtIndex, CanRemoveItemAtIndex);

        // list instantiation etc..
    }

    private void RemoveItemAtIndex(object? parameter)
    {
        // we need at least one item inside
        MusicArtists.RemoveAt(0);
    }

    private bool CanRemoveItemAtIndex(object? parameter)
    {
        return MusicArtists.Count > 0;
    }
    ' other properties
   
    Public Property RemoveItemAtIndexCommand As DelegateCommand

    Sub New()
        RemoveItemAtIndexCommand = new DelegateCommand(RemoveItemAtIndex, CanRemoveItemAtIndex);

        ' list instantiation etc..
    End Sub

    Private Sub RemoveItemAtIndex(parameter As Object)
        MusicArtists.RemoveAt(0)
    End Sub

    Private Function CanRemoveItemAtIndex(parameter As Object) As Boolean
        MusicArtists.Count > 0
    End Function

Summing up

If you want to have a control, which is able to display things in like a list, being able to select one or more items out of it, the ListView is possibly your choice. When thinking about: „Should I use a ListView or a DataGrid“, you should choose the ListView, if editing is not a concern. As mostly always, you have the option to go for the code behind, or the MVVM approach, I would personally always choose the MVVM one. It seems much cleaner for my personal preferences and doesn’t mix up UI with business code. Feel free to leave any comment on how to improve this article, or if I may explained something wrong.

Downloads

If you need quick and ready to use examples, just go ahead and download the ones you need. Maybe I’ll add some more later.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert