ItemsControl: 'C' is for Collection

The series continues…

An ItemsControl would be nothing without its collection of Items. In this post, we investigate the “Items” of an ItemsControl, looking at each of the following areas:

If I were rating the technical level of each post in this series, I would put this particular post somewhere in the range of moderate to advanced (but still very approachable :) ).

The Items Collection (a.k.a, the ItemCollection)

The “Items” property of an ItemsControl provides access to a collection of objects, or data items, that make up the logical content of the control. The type of this property is ItemCollection. (I will use the terms “Items collection” and “ItemCollection” interchangeably.) The “Type” of each item within the Items collection is Object. So literally any CLR object can be added to an ItemCollection.

We will look at the ItemCollection class, itself, in more detail momentarily, but first, there are a couple of things to note about the Items property declaration on ItemsControl.

1) The Items property is a read-only CLR property.

This means that the collection exposed via the Items property must be instantiated by the control itself. In fact, the ItemCollection class does not even provide a public constructor.

2) The Items property is not backed by a dependency property.

This means that you cannot set a binding directly on the Items property. However, you can definitely bind an ItemsControl to a collection of items. We will look at how this works shortly, but before we do, we should look at the simpler, non-databound (or direct) scenario…

ItemCollection Modes: Direct and ItemsSource

Although the Items property is read only, the provided collection is not necessarily read only. In fact, in earlier posts, we’ve already seen that you can directly add items to an ItemsControl:

<ListBox>
  <sys:String>Item 1</sys:String>
  <sys:String>Item 2</sys:String>
  <sys:String>Item 3</sys:String>
</ListBox>

Because items are added directly to the ListBox, this is an example of using an ItemCollection in “direct mode”. This is by far the simplest mode to use conceptually. In direct mode, the ItemCollection class works exactly like every other .NET collection. You can directly access all of the expected members of an indexed collection: Add(), Insert(), Remove(), RemoveAt(), IndexOf(), Items[index], Count, etc.

The other mode for an ItemCollection is called “ItemsSource mode”. In ItemsSource mode, the items in the ItemCollection correspond to items in a source collection. That source collection is specified via a separate property on the ItemsControl that is appropriately named “ItemsSource”.

The following shows a typical scenario of an ItemsControl using ItemsSource mode:

<ListView ItemsSource="{Binding Path=Characters}">
  <ListView.View>
    <GridView>
      <GridViewColumn Width="100"
        DisplayMemberBinding="{Binding Last}"
        Header="Last Name" />
      <GridViewColumn Width="100"
        DisplayMemberBinding="{Binding First}"
        Header="First Name" />
      <GridViewColumn Width="60"
        DisplayMemberBinding="{Binding Gender}"
        Header="Gender" />
    </GridView>
  </ListView.View>
</ListView>

The ItemsSource property is a dependency property of type IEnumerable. This tells us two important things:

1) The source collection can be any enumerable collection.

2) The ItemsSource property can be established using a binding.

As such, it is the ItemsSource property (in conjunction with the ItemsSource mode of an ItemCollection) that enables an ItemsControl to be databound to a collection.

Sidenote: Although you will most often see the ItemsSource property of an ItemsControl set via a binding, there is no reason that the ItemsSource property cannot be directly set to an enumerable collection, as shown here:

<ListBox ItemsSource="{StaticResource Characters}" />

The Modes are Mutually Exclusive

It should be noted that direct mode and ItemsSource mode are mutually exclusive. An ItemCollection is either in direct mode or in ItemsSource mode, but never both.

Once Items have been explicitly added to the Items collection, it is in direct mode. A subsequent attempt to set the ItemsSource property after entering direct mode will result in an exception.

Similarly, once the ItemsSource property has been set, the Items collection is in ItemsSource mode. A subsequent attempt to directly modify the Items collection (using Add(), Insert(), Remove(), etc) will result in an exception.

The only way to change modes at runtime is to either 1) clear the Items collection via the Clear method (if in direct mode) prior to setting the ItemsSource property, or 2) set the ItemsSource property to null (if in ItemsSource mode) prior to calling the direct access methods of ItemCollection.

Observable Collections Support Dynamic Updates

As noted earlier, in direct mode, changes to the Items collection are made through direct access methods of the ItemCollection class. Any such direct changes made at runtime will cause the visuals to be updated immediately.

But what about dynamic collection changes in ItemsSource mode? How could the Items collection possibly know about changes to the source collection?

The answer is that the ItemCollection cannot know about any such changes unless the source collection chooses to announce those changes by providing change notifications. The way a source collection does this is by fully implementing and supporting the INotifyCollectionChanged interface. Any collection that provides these change notifications is said to be observable.

Dynamic changes to an observable collection will be immediately reflected in the Items collection of any ItemsControl that is bound to the collection. Consequently, these changes will be immediately reflected in the user interface.

The INotifyCollectionChanged interface is not super complex, but ensuring proper implementation does place an extra burden on the source collection. If developers had to implement this interface anytime they wanted to bind to a collection, it would be a huge inconvenience. Luckily, the .NET framework provides a very handy generic template class called ObservableCollection<T>. By creating an instance of this class, you automatically get all of the change notifications without having to do any extra work.

Typically, you will see a collection class derive from ObservableCollection<T>, to create a strongly typed collection, as follows:

    public class StringCollection : ObservableCollection<string>
    {
    }

Any instance of this StringCollection class is fully observable and will serve well as the ItemsSource of an ItemsControl. It can be used exactly like an instance of Collection<string>.

So what if the collection is not observable? Can it still serve as an ItemsSource?

Absolutely. As noted earlier, any enumerable collection can serve as the source of an Items collection. The only caveat is that the Items collection will not be updated dynamically if the source collection changes at runtime. Rather, the collection will be enumerated once and its members will be added to the Items collection when the ItemsSource property is first established. Thereafter, if you want the Items collection to be updated, you must explicitly call the Refresh() method of ItemCollection.

CollectionView: “The Great Equalizer”

If you’ve worked with ItemsControl much, you probably know that sorting, grouping, and filtering are supported via the CollectionView class. This class also supports the notion of currency, which means a CollectionView maintains a current item pointer that can be accessed and moved using methods on the CollectionView.

Every enumerable collection in WPF has a default view. The collection may have multiple other views, each with its own sorting, grouping, and filtering parameters. A common way to establish a view of a collection in markup is to leverage the CollectionViewSource class, as shown here:

<CollectionViewSource x:Key="characterView"
    Source="{StaticResource Characters} ">
  <CollectionViewSource.SortDescriptions>
    <componentModel:SortDescription PropertyName="First" />
  </CollectionViewSource.SortDescriptions>
  <CollectionViewSource.GroupDescriptions>
    <dat:PropertyGroupDescription PropertyName="Last" />
  </CollectionViewSource.GroupDescriptions>
</CollectionViewSource>

This CollectionViewSource can then be specified as the ItemsSource of an ItemsControl, thereby causing its associated view of the collection to serve as the CollectionView for the control.

“But Dad, I don’t WANT a CollectionView!”

“I didn’t ask what you WANT… As long as you’re living under my roof, you’ll use a CollectionView!”

With WPF, sometimes it’s not as much about what you want, as it is about what the framework needs. The CollectionView class is a classic example. When it comes to binding controls to a collection of data items, the framework needs a way to treat all collections in a consistent manner.

Unfortunately, not all enumerable collections were created equal. For example, IList provides direct index-based access to items, whereas IEnumerable requires that you enumerate all items starting from the beginning of the collection until you come to the index you care about. For another example, consider a collection class that supports the INotifyCollectionChanged interface to provide collection change notifications versus a simple Collection<T> class that provides no such notifications.

The framework architects wanted to support binding to as many different types of collections as possible. But imagine how ugly the code for the ItemsControl class would be if it had to account for differences among collection types with conditional code blocks… if it’s an observable collection, do this, or if it’s an IList, do this, or if it’s an IEnumerable, do this, etc.

Enter the CollectionView class…

To deal with this challenge of disparate collections, WPF introduces the CollectionView class to serve as the great equalizer of enumerable collections. It is the CollectionView class that internally looks at a collection’s supported interfaces and determines how best to deal with the collection. It then surfaces a view of the collection through a well-defined set of properties, methods, and events. Essentially, it allows all collections to be treated by the ItemsControl class as equals.

So whether or not you care about currency, grouping, sorting, filtering, change notifications, etc., the Items collection of an ItemsControl is always maintained internally using a CollectionView. In fact, the ItemCollection class is a CollectionView.

Although true, that last statement is a little misleading since really, ItemCollection is just a wrapper class for an internal CollectionView member. The type of the internal CollectionView is determined by the type of the source collection and the mode of the ItemCollection. In Direct mode, it is always of type InnerItemCollectionView (an internal class designed specifically for direct mode views). In ItemsSource mode, it will be of type CollectionView for an IEnumerable source, ListCollectionView for an IList source, or BindingListCollectionView for an IBindingList or IBindingListView source.

So you’re observable… Who cares?

CollectionView does. We’ve already noted that dynamic changes to observable collections are immediately reflected in the Items collection. It is the CollectionView class that actually listens to the events raised by the collection. So if you’ve ever wondered exactly who is monitoring these events, get a life! Sorry… I meant to say, it’s CollectionView. Now you know.

Performance Considerations around Bound Collections

Keeping in mind that CollectionView serves as the great equalizer of collections, we should look at some performance considerations around binding to collections. We’ve already acknowledged that not all collections support the same features. Often, CollectionView must perform extra work to support its common interface for collections.

One of the major features provided by CollectionView is indexing for an enumerable collection. That is, CollectionView provides direct access to members of the collection by an integer-based index. This means it must support properties like this[int index] and Count, as well as methods like Contains() and IndexOf().

If the source collection already supports indexing, you will see much better performance when binding to the collection. This means that the best candidates for a source collection are those that support the IList interface. It should be noted that ObservableCollection<T> implements IList, so it’s a great choice.

If the source collection does not support index-based access (ICollection or IEnumerable, for example), then CollectionView must do a lot more work to surface the view as an indexed collection. In order to support a property like Count, it may be necessary to enumerate the entire collection. And some methods like Contains(), IndexOf(), and GetItemAt() can be super expensive, since the performance of the algorithm to support these operations is directly proportional to the size of the collection.

So the key perf takeaway is that the source collection should support IList whenever possible.

Items and the Element Trees

The items within the Items collection make up the logical children of the ItemsControl. They are said to be members of the logical tree. For a HeaderedItemsControl, the headers associated with each item will also be logical children of the ItemsControl. (Btw, if you’re not sure what a HeaderedItemsControl is, you should revisit The ICIQ Test and spend a little more time exploring the tooltips after receiving your score.)

If the items in the Items collection happen to be visuals, then they will also be members of the visual tree. If they are not visuals, they will instead be represented visually using an inflated template of visuals. Since the template represents data items, it is called a DataTemplate. For more on that, please tune in for the next episode in this series… ‘D’ is for DataTemplate.

20 Responses to “ItemsControl: 'C' is for Collection”

  1. Mark Collins says:

    Hey just wanted to say “You are the Man!” or woman….

    Seriously tho thanks for all the awsome blog posts

  2. Marcus says:

    After reading this article, I migrated my bound collection to a ColllectionViewSource. However, I ran into an odd problem I have not seen anyone specifically address. Although I can verify the properties are updated in the UI when they are programmatically changed. I cannot seem to get the CollectionViewSource’s filter(s) to be re-executed after they have been run for the first time. What do I need to do to make that happen?

  3. Dr. WPF says:

    Hi Marcus,

    To cause the filter to be reapplied to the collection, you would need to call the Refresh() method on the Items collection.

    Cheers,
    -dw

  4. Klaus says:

    Dr. WPF

    As a beginner I seem to miss something:

    I have a business object class, I want to display a ObservableCollection(of BusinessObject) in a datagrid.
    All works fine, I can see my data, I can change it using the UI, but: When I programmatically add NEW ITEM to my ObservableCollection after the binding is done, I see them directly in my UI. If I programmatically change some properties to an EXISTING ITEM after binding, this is not directly visible but only after I reset the binding source.

    Here is my xaml
    <ListView Name="ListObjects"
    IsSynchronizedWithCurrentItem="True"
    Margin="30,0,0,30"
    Width="280"
    ItemsSource="{Binding }"
    HorizontalAlignment="Left">
    <ListView.View>
    <GridView x:Name="GridObjects">
    <GridViewColumn Header="ModuleName"
    Width="Auto"
    DisplayMemberBinding="{Binding Path=ModuleName}" />
    </GridView>
    </ListView.View>
    </ListView>

    And here is my binding code:

    Dim mltModule As New ObservableCollection(Of BE.BEModule)

    If oDataService.GetModuleListAll(mltModule).Status = SF.ReturnStatusEnum.RS_SUCCESS Then
    ListObjects.ItemsSource = mltModule
    End If

    and here the signature of
    Public Function GetModuleListAll(ByRef ltModule As ObservableCollection(Of BE.BEModule)) As SF.ReturnInfo

    Three questions:
    1) I do not necessarily need a CollectionView to be able to see changes directly, right?
    2) If I use ObservableCollection, the base class in question does not necessarily have top implement INotifyChanged
    3) Any idea what is missing

    Thanks for your support
    Regards
    Klaus

  5. Dr. WPF says:

    Hi Klaus,

    The problem in your scenario is that your assumption regarding #2 is incorrect. The class within your observable collection *does* still need to implement INotifyPropertyChanged.

    Cheers,
    -dw

  6. James Miles says:

    Hi Dr WPF,

    Thanks for the info! Just one thing.

    The ItemCollection has an internal functionality that causes the wrapped CollectionView to share (among other things) the same filter object.

    This means that if you apply a filter to myItemCollection.Filter the underlying collection view is also filtered.

    This is not consistent with the collection view base class that it inherits from.

    I think it is by design. If they had not done this, it would be difficult for one to achieve this behaviour; however it can be easily negated by wrapping the source in a 2nd collection view.

    Your thoughts/opinions would be appreciated :)

    Cheers,
    James Miles

  7. Vincent Yang says:

    Dr.W,
    You are not only a dr, but try to make us a dr as well !.
    Looking forward to awsome blogs on content control, decorator, 2D, 3D..
    Am I too greedy?

  8. Dr. WPF says:

    Hi James,

    Yes, if you apply a filter to the ItemCollection, it is applied to the actual source collection. If you need to present a filtered collection, then you should really use a CollectionViewSource (or create the CollectionView in code).

    Cheers,
    -dw

  9. Dr. WPF says:

    Hi Vincent,

    No, you are not too greedy… I just wish I had the bandwidth to live up to your expectations! :-)

    Regards,
    -dw

  10. Lax says:

    Hi Dr.WPF,
    Thanx for a gud post on collections in WPF:).
    I want to know, can I apply custom validation using WPF validationrule to a TreeView? Wish if you could write something on Validations in WPF:)….

    Regards,
    Lax

  11. Dr. WPF says:

    Thanks Lax. It’s on my todo list. (Just need to find some time! :-s)

    The short answer is yes, you can perform custom validation using a custom validation rule, but it’s almost always easier to perform such validation within the viewmodel class using IDataErrorInfo or IEditableObject (see ‘E’ is for Editable Collection).

  12. MacGyver says:

    First of all; thank you dear Doctor for all the information you’ve spread around the internet (msdn fora, this blog etc.), they’ve helped me tremendously in understanding WPF.

    Now for my question:
    Does binding to the ObservableCollection.Count property work (in a DataTrigger) ?
    Ie. {Binding Path=Accounts.Count} where Accounts is an ObservableCollection.
    What I assume is that since ObservableCollection does not implement INotifyPropertyChanged it will not work.

    Thanks.

  13. Dr. WPF says:

    Hi MacGyver,

    Yes, that binding should work fine. The ObservableCollection does implement INotifyPropertyChanged and it raises a change notification for the Count property whenever an item is removed or added to the collection.

    Cheers,
    -dw

  14. MacGyver says:

    Thanks!
    Now that I re-read the msdn docs (http://msdn.microsoft.com/en-us/library/ms668604.aspx) it does indeed implement the INotifyPropertyChanged event!
    I guess we can never read them thoroughly enough.

  15. Trey says:

    Excellent article. Thank you.

    Specifically the Collection View as I found examples all over, but no one explaining quite what it does or why it’s necessary. Though I cannot say I am entirely clear on the topic, I have a decent idea now.

    Perhaps this is because my situation is a bit different, in that my data is coming from a LINQ to Entities models, and I am trying to use a DataGrid.

    I have been able to use ItemsSource and tie it directly to an entity like my Customers EntityObject class (the mapped table basically) and it displays items correctly.

    All adds, edits, and deletes appear on the DataGrid regardless of further coding.

    At this point I am trying to resolve the issue with changes being persisted to the database back through the entities layer.

    The commits are made after most of the events are fired, so I am not sure where to write the code that pushes the changes through the entity model.

    I’ve tried using the RowEditEnding handler to some success (using an IEditableCollectionView for queueing Adds and Edits to be commited), but again the event finishes before the commit to the collection is actually performed… so if I call entities.SaveChanges() I get concurrency exceptions and other issues with the model not matching the collection.

    Any help here? Again, I’ve found many examples, but none of them very helpful when using Entities model for CRUD.

    Thank you.

  16. Dr. WPF says:

    Hi Trey,

    The entities model works great with WPF for data retrieval and presentation. I haven’t actually tried using it for transactional updates. If you have a simple sample that demonstrates the problem you describe, I wouldn’t mind having a look. Feel free to send it to my ask[at]drwpf[dot]com address.

    Cheers,
    -dw

  17. George says:

    Great Post!! Thank you very much.

  18. ykab says:

    extremely well written and enlightening. can’t wait to read more of your posts.

  19. Catherine says:

    I think I love you.

  20. Catherine says:

    To be a bit more specific about the “I think I love you” comment, I am struggling as a WPF newbie to get a simple window working showing a DataGrid of items, and a combobox that allows you to filter the datagrid based on the contents of one of the fields. Obviously I want the combo to refresh when the datagrid changes, but to have its sort order independent of that of the datagrid. After hours of web searching, I read this. Now it works!!!

Leave a Reply