Monthly Archives: October 2014

A generic version of ICollectionView used in a MVVM searchable list

In this post we will describe how to create a searchable list with WPF following MVVM principles. To this aim we will use a WPF ListView to display the searched items and a TextBox to enter the text used for the search. Most of the implementation that you will find on the web (e.g. this one) will recommend you to bind your ListView to an ICollectionView. However, this is not 100% satisfactory as long as ICollectionView does not have a built-in generic version. Consequently the ViewModel’s member exposing the binding items will return an ICollectionView which is a powerful object (see this for instance)  but is “only” an enumeration of System.Object. In this post we will show you that a generic version can be easily implemented and exposed by your ViewModel.

In this post we will create a very simple app that let you search a player in the list of all the players of the last Football World Cup in Brazil. The complete source code can be found on my Github here.

Searchable WPF ListView

Searching ‘dav’ in the ListView display a list of results starting with ex Chelsea’s player David Luis…

The key ingredients of such implementation is very simple in MVVM. First take the View which does not need more than the few lines of xaml below.

 <UserControl.DataContext>
 <Binding Path="PlayerSearchViewModel" Source="{StaticResource Locator}" />
</UserControl.DataContext>
<DockPanel>
 <TextBlock DockPanel.Dock="Top" Text="Search player"></TextBlock>
 <TextBox DockPanel.Dock="Top" Text="{Binding SearchPlayerText, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<ListView ItemsSource="{Binding DisplayedPlayers}" SelectionMode="Single" ScrollViewer.VerticalScrollBarVisibility="Auto">
  <ListView.View>
   <GridView >
    <GridViewColumn DisplayMemberBinding="{Binding Name}" Header="Name" />
    <GridViewColumn DisplayMemberBinding="{Binding NationalTeam}" Header="National Team" />
    <GridViewColumn DisplayMemberBinding="{Binding Age}" Header="Age" />
    <GridViewColumn DisplayMemberBinding="{Binding Club}" Header="Club"/>
    <GridViewColumn DisplayMemberBinding="{Binding Championship}" Header="Championship"/>
   </GridView>
  </ListView.View>
 </ListView>
</DockPanel>

Let us start by exposing the non-generic version: the DataContext of the control above is bound to an instance of an implementation of the interface IPlayerSearchViewModel below.

public interface IPlayerSearchViewModel
{
   string SearchPlayerText { get; set; }

   ICollectionView DisplayedPlayers { get; }
}

A very straightforward implementation that works is the following one.

public class PlayerSearchViewModel : IPlayerSearchViewModel
{
    private readonly ICollectionView _view;
    private string _textsearch;

    public PlayerSearchViewModel(IPlayerProvider playerProvider)
    {
        _view = CollectionViewSource.GetDefaultView(playerProvider.GetAllWorldCupPlayer());
        _view.Filter += (object item) =>
        {
                    if (_textsearch == null) return true;
                    var itemPl = (IPlayer) item;
                    return itemPl.Name.Contains(_textsearch) ||
                               itemPl.NationalTeam.Contains(_textsearch) ||
                               itemPl.Club.Contains(_textsearch) ||
                               itemPl.Championship.Contains(_textsearch);
        };
    }

    public string SearchPlayerText
    {
        get { return _textsearch; }
        set
        {
            _textsearch = value;
            _view.Refresh();
        }
    }

    public ICollectionView DisplayedPlayers { get { return _view; } }
}

As I said in the introduction, this is quite enoying. You may want to create screens with many ICollectionView bindings that may contain different types, then it is becoming error prone and we are loosing the benefit of C#’s type safety. The unit test example below is showing you that the elements need to be casted while they are accessed from the ICollectionView.

[TestMethod]
public void TestingTheSearchingCapabilitiesWithBedoya()
{
    var viewModel = new PlayerSearchViewModel(new PlayerProvider());
    viewModel.SearchPlayerText = "Bedoya";
    var searchResult = viewModel.DisplayedPlayers.Cast<IPlayer>().ToArray(); //Cast objects extracted from the ICollectionView
    Assert.AreEqual(1, searchResult.Length);
    IPlayer bedoya = searchResult[0];
    Assert.AreEqual("Nantes",bedoya.Club);
}

In addition we cannot use anymore the XAML validation and intellisense provided by Resharper.

Resharper complaining because of the unknown's member of the DataContext (typed as object)

Resharper complaining because of the unknown’s member of the DataContext (typed as object)

Fortunately we can create our generic version of ICollectionView and get back to the comfortable world of type safety. In this example, I will only provide the generic enumeration and the generic version for the SourceCollection member but you can add others. Indeed, you may create a generic version of the Filter predicate to avoid dealing with System.Object in your lambdas but with your generic type T instead.

public interface ICollectionView<T> : IEnumerable<T>, ICollectionView
{
    IEnumerable<T> SourceCollectionGeneric { get; }
    //Add here your "generic methods" e.g.
    //e.g. Predicate<T> Filter {get;set;} etc.
}

Actually, the implementation of the ICollectionView is really easy as long as you already have the non-generic instance at hand ICollectionView

public class MyCollectionViewGeneric<T> : ICollectionView<T>
{
    private readonly ICollectionView _collectionView;

    public MyCollectionViewGeneric(ICollectionView generic)
    {
        _collectionView = generic;
    }

    private class MyEnumerator : IEnumerator<T>
    {
        private readonly IEnumerator _enumerator;
        public MyEnumerator(IEnumerator enumerator)
        {
            _enumerator = enumerator;
        }

        public void Dispose()
        {
        }

        public bool MoveNext()
        {
            return _enumerator.MoveNext();
        }

        public void Reset()
        {
            _enumerator.Reset();
        }

        public T Current { get { return (T) _enumerator.Current; } }

        object IEnumerator.Current
        {
            get { return Current; }
        }
    }

    public IEnumerator<T> GetEnumerator()
        {
            return new MyEnumerator(_collectionView.GetEnumerator());
        }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return _collectionView.GetEnumerator();
    }

    public bool Contains(object item)
    {
     return _collectionView.Contains(item);
    }

    public void Refresh()
    {
        _collectionView.Refresh();
    }

       //Complete implementation can be found on github.co/bpatra/MVVMSample

    public event NotifyCollectionChangedEventHandler CollectionChanged
    {
        add
        {
            lock (objectLock)
            {
                _collectionView.CollectionChanged += value;
            }
        }
        remove
        {
            lock (objectLock)
            {
                _collectionView.CollectionChanged -= value;
            }
        }
    }

    public IEnumerable<T> SourceCollectionGeneric

        get { return _collectionView.Cast<T>(); }
    }
}

Therefore the implementation of the the ViewModel becomes cleaner and statically typed. First the new version of the ViewModel interface.

public interface IPlayerSearchViewModel
{
    string SearchPlayerText { get; set; }

    ICollectionView<IPlayer> DisplayedPlayers { get; }
}

Then, the implementation.

public class PlayerSearchViewModel : IPlayerSearchViewModel
{
    private readonly ICollectionView<IPlayer>; _view;
    private string _textsearch;

    public PlayerSearchViewModel(IPlayerProvider playerProvider)
    {
        _view = new MyCollectionViewGeneric<IPlayer>;(CollectionViewSource.GetDefaultView(playerProvider.GetAllWorldCupPlayer()));
        _view.Filter += (object item) =>;
        {
            if (_textsearch == null) return true;
            var itemPl = (IPlayer) item;
            return itemPl.Name.Contains(_textsearch) ||
                        itemPl.NationalTeam.Contains(_textsearch) ||
                        itemPl.Club.Contains(_textsearch) ||
                        itemPl.Championship.Contains(_textsearch);
        };
    }

    public string SearchPlayerText
    {
    get { return _textsearch; }
        set
        {
            _textsearch = value;
            _view.Refresh();
        }
    }

    public ICollectionView<IPlayer>; DisplayedPlayers { get { return _view; } }
}

You do not have to cast or worry anymore on the the type of the objects contained in you ICollectionView. You will also detect binding errors statically with Resharper.

resharperClever

Resharper handles typed generic collections in databinding