GridView, ListView and ListBox – What Each of These Controls Is Good For

In this sample I will explore the capabilities of these 3 controls. In each one that supports

GridView inheritance:
Object -> DependencyObject -> UIElement -> FrameworkElement -> Control -> ItemsControl -> Selector -> ListViewBase, ISemanticZoomInformation -> GridView

ListView inheritance:
Object -> DependencyObject -> UIElement -> FrameworkElement -> Control -> ItemsControl -> Selector -> ListViewBase, ISemanticZoomInformation -> ListView

ListBox inheritance:
Object -> DependencyObject -> UIElement -> FrameworkElement -> Control -> ItemsControl -> Selector -> ListBox

GridViewItem, ListViewItem and ListBoxItem share exactly the same parent hierarchy, namely this one:
Object -> DependencyObject -> UIElement -> FrameworkElement -> Control -> ContentControl -> SelectorItem -> GridViewItem, ListViewItem, ListBoxItem

ListViewBase, ISemanticZoomInformation

Additional methods compared to the Selector class:

  • void CompleteViewChange()
  • void CompleteViewChangeFrom(SemanticZoomLocation, SemanticZoomLocation)
  • void CompleteViewChangeTo(SemanticZoomLocation source, SemanticZoomLocation destination)
  • IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync()
  • void MakeVisible(SemanticZoomLocation item)
  • void ScrollIntoView(object item, ScrollIntoViewAlignment alignment)
  • void ScrollIntoView(object item)
  • void SelectAll()
  • void StartViewChangeFrom(SemanticZoomLocation source, SemanticZoomLocation destination)
  • void StartViewChangeTo(SemanticZoomLocation source, SemanticZoomLocation destination)
  • bool CanDragItems { set; get; }
  • bool CanReorderItems { set; get; }
  • double DataFetchSize { set; get; }
  • object Header { set; get; } – It is almost the same as HeaderTemplate, except that HeaderTemplate is set in XAML and Header in C# code, where you simply create the Content and assign it. This way you can, for example, change the header programmatically.
  • DataTemplate HeaderTemplate { set; get; } – Because ListView/GridView does not generate a header on its own, you have to create it yourself if you want one. Again – I do not recommend doing this in C#, but in XAML instead. Do not forget to give all headers the same width as the columns in the ItemTemplate – you have to keep track of this yourself, the program does not take care of it.
  • TransitionCollection HeaderTransitions { set; get; }
  • double IncrementalLoadingThreshold { set; get; }
  • IncrementalLoadingTrigger IncrementalLoadingTrigger { set; get; }
  • bool IsActiveView { set; get; }
  • bool IsItemClickEnabled { set; get; }
  • bool IsSwipeEnabled { set; get; }
  • bool IsZoomedInView { set; get; }
  • IList SelectedItems { get; } – Returns the selected items, which you then only need to cast to the type used in the DataContext so that you can use all the methods of that type.
  • ListViewSelectionMode SelectionMode { set; get; } – You can use these values:
    None – no item can be selected, but all active items will remain active.
    Single – Only 1 item can be selected. If you click an item other than the selected one, the check mark moves to that item. You deselect an item the same way you selected it – by clicking it.
    Multiple – By clicking items one at a time you can select multiple items; clicking an item again deselects it. Extended – You can select multiple items using shift or ctrl. If you have items selected and click another item by mistake, that new item becomes the selection.
  • SemanticZoom SemanticZoomOwner { set; get; }
  • event DragItemsStartingEventHandler DragItemsStarting
  • event ItemClickEventHandler ItemClick

Selector

Compared to the ItemsControl class, it adds these protected members:

  • static bool GetIsSelectionActive(Windows.UI.Xaml.DependencyObject element)
  • bool? IsSynchronizedWithCurrentItem { set; get; }
  • int SelectedIndex { set; get; } – What it returns – see the table below
  • object SelectedItem{ set; get; } – What it returns – see the table below
  • object SelectedValue{ set; get; } – What it returns – see the table below
  • string SelectedValuePath{ set; get; } – Lets you specify which property of the DataContext should be returned in SelectedValuePath instead of the whole item.
  • event SelectionChangedEventHandler SelectionChanged

SelectionMode SelectedIndex SelectedItem SelectedValue SelectedValuePath
None –1 null null Whatever you set it to; it does not change when the selection changes
Single Index of the selected item Object of the selected item Object of the selected item Whatever you set it to; it does not change when the selection changes
Multiple Index of the first selected item Object of the first selected item Object of the first selected item Whatever you set it to; it does not change when the selection changes
Extended Index of the first selected item Object of the first selected item Object of the first selected item Whatever you set it to; it does not change when the selection changes

ItemsControl

Compared to the Control class, it adds:

  • void ClearContainerForItemOverride(DependencyObject element, object item)
  • DependencyObject GetContainerForItemOverride()
  • bool IsItemItsOwnContainerOverride(object item)
  • void PrepareContainerForItemOverride(Windows.UI.Xaml.DependencyObject element, object item)

Compared to the ItemsControl class, it adds these public members:

  • static ItemsControl GetItemsOwner(DependencyObject element)
  • static Windows.UI.Xaml.Controls.ItemsControl ItemsControlFromItemContainer(Windows.UI.Xaml.DependencyObject container)
  • string DisplayMemberPath { set; get; }
  • IObservableVector<GroupStyle> GroupStyle { get; }
  • GroupStyleSelector GroupStyleSelector { set; get; }
  • bool IsGrouping { get; }
  • ItemContainerGenerator ItemContainerGenerator { get; }
  • Style ItemContainerStyle { set; get; }
  • StyleSelector ItemContainerStyleSelector { set; get; }
  • TransitionCollection ItemContainerTransitions { set; get; }
  • ItemCollection Items{ get; } – For when you want to add items derived from ListBoxItem/ListViewItem/GridViewItem directly. However, I do not recommend this approach, see below.
  • ItemsPanelTemplate ItemsPanel { set; get; } – The template for the elements loaded from ItemsSource.
  • object ItemsSource { set; get; } – An ObservableCollection<T> object containing the individual items the user wants to display.
  • DataTemplate ItemTemplate{ set; get; } – The template for a single item.
  • DataTemplateSelector ItemTemplateSelector { set; get; } – used for dynamically assigning different DataTemplates based on different properties. For more information, take a look at https://stackoverflow.com/questions/2302250/how-to-style-different-items-differently-when-using-data-template-in-xaml/2304265#2304265

Control

We will not go any further into the members of the Control class here.

FrameworkElement

From this class I will examine only one property here:

  • object DataContext { set; get; } – the object from which the individual properties are read during data binding.

GridView

Good for when:
You display things horizontally

Not suitable when:

You need compatibility with classic .NET controls

Additional methods compared to the ListViewBase class: none

ListView

Good for when:
You display things vertically

Not suitable when:

You need compatibility with classic .NET controls

Additional methods compared to the ListViewBase class: none

ListBox

Good for when:
You need backward compatibility

Not suitable when:

The control has to be optimized for touch input

Additional methods compared to the Selector class:

  • void ScrollIntoView(object item) –
  • void SelectAll()
  • IList<object> SelectedItems { get; }
  • SelectionMode SelectionMode { set; get; }

SelectorItem

Compared to ContentControl, it adds these properties:

  • bool IsSelected { get; set; }

GridViewItem

Compared to the SelectorItem class, it adds:

  • ListViewItemTemplateSettings TemplateSettings { get; }

Normally you do not derive from this class or write presentation code in C#. Instead, you just create a class with public properties and then map those properties via data binding to the template you have in <GridView.ItemTemplate><DataTemplate> … XAML code of the template … </DataTemplate></GridView.ItemTemplate>

ListViewItem

Compared to the SelectorItem class, it adds:

  • ListViewItemTemplateSettings TemplateSettings { get; }

Normally you do not derive from this class or write presentation code in C#. Instead, you just create a class with public properties and then map those properties via data binding to the template you have in <ListView.ItemTemplate><DataTemplate> … XAML code of the template … </DataTemplate></ListView.ItemTemplate>

ListBoxItem

Compared to the SelectorItem class, it adds: nothing

Normally you do not derive from this class or write presentation code in C#. Instead, you just create a class with public properties and then map those properties via data binding to the template you have in <ListBox.ItemTemplate><DataTemplate> … XAML code of the template … </DataTemplate></ListBox.ItemTemplate>

Classes Inheriting from INotifyPropertyChanged

The core of this tutorial is implementing it both in the class of a single item and in the collection that wraps this class.

1) The first step is therefore to define the item class.

C#
public class Item
{
}

2) Create public properties and private fields that will hold the item data.

C#
string header = &quot;&quot;;
public string Header
{
    get
    {
        return header;
    }
    set
    {
        header = value;
    }
}
string description = &quot;&quot;;
public string Description
{
    get
    {
        return description;
    }
    set
    {
        description = value;
    }
}

3) Write a class derived from INotifyPropertyChanged, which can look something like this:

C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace W8Learn.DataControls
{
    public class NotifyPropertyChanged : INotifyPropertyChanged
    {
        protected void OnPropertyChanged(object sourceClass, string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(sourceClass, new PropertyChangedEventArgs(propertyName));
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }
}

4) Derive your item class from this newly created class and add a call to OnPropertyChanged() in the set accessor of every property you want to be notified about when it changes.

C#
public class Item : NotifyPropertyChanged
{
    string header = &quot;&quot;;
    public string Header
    {
        get
        {
            return header;
        }
        set
        {
            OnPropertyChanged(this, &quot;Header&quot;);
            header = value;
        }
    }
    string description = &quot;&quot;;
    public string Description
    {
        get
        {
            return description;
        }
        set
        {
            OnPropertyChanged(this, &quot;Description&quot;);
            description = value;
        }
    }
}

5) Create a NotifyPropertyChangedCollection<T> class, where T is the item type. You can use this class for all collections around elements that implement INotifyPropertyChanged.

C#
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//-The System.Reflection namespace has to be imported manually
using System.Reflection;
namespace W8Learn.DataControls
{
    class NotifyPropertyChangedCollection<T> : NotifyPropertyChanged
    {
        /// <summary>
        /// It has to be an ObservableCollection because T implements the INotifyPropertyChanged interface, and only ObservableCollection can work with it
        /// </summary>
        public ObservableCollection<T> Collection = new ObservableCollection<T>();
        /// <summary>
        /// It cannot be static, because then I would have to specify the type T with every method call
        /// </summary>
        /// <param name="propertyClass">
        /// <param name="propertyName">
        /// <returns> </returns>
        public string GetPropertyValue(object propertyClass, string propertyName)
        {
            object o = propertyClass.GetType().GetRuntimeProperty(propertyName).GetValue(propertyClass);
            if (o != null)
            {
                return o.ToString();
            }
            return string.Empty;
        }
    }
}

6) Write an ItemCollection class – a wrapper class for the items. Add each item to its collection field and assign the same event handler to each item – the OnPropertyChanged method in this class. Admittedly, this is unnecessary here, because in W8, after the Text property of a TextBlock control is updated, this new text is not rendered, but in a classic WPF application it would be rendered.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace W8Learn.DataControls
{
    class ItemCollection : NotifyPropertyChangedCollection<Item>
    {
        public ItemCollection()
        {
            for (int i = 0; i < 100; i++)
            {
                Item item = new Item();
                item.Header = "Item #" + i.ToString();
                item.Description = "";
                item.PropertyChanged += item_PropertyChanged;
                Collection.Add(item);
            }
        }
        void item_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            OnPropertyChanged(sender, e.PropertyName);
        }
    }
}

7) That is all. Now just assign the Collection property of the created ItemCollection class to the ItemsSource property of the GridView/ListView/ListBox, and set the data binding mode to TwoWay on the elements whose values you want to store in the Item class.

Categories

Leave a Reply