Month: December 2008

Using ObservableCollection with Linq

It’s not easy. Let’s say you’ve got a composite object which contains an observable collection (because you want to bind it to a list box for example). Then let’s say you want to populate this from a Linq query (Linq to XML for example). You soon run into the rather large gap becaue, unlike List<T>, ObservableCollection doesn’t have a suitable way to initialise it with a collection (not one that’s suitable for use in a Linq query.

Here’s how I fixed it in my project first. Rather than simply using ObservableCollection<PictureData> directly, create a new class that overrides it and adds an additional constructor:


    public class PictureDataList : ObservableCollection<PictureData>
    {
        public PictureDataList()
        {
        }

        public PictureDataList(IEnumerable<PictureData> items)
        {
            foreach (var item in items)
            {
                Add(item);
            }
        }
    }

This method can be safely used in any Linq query.

But there’s a slightly better way – one which avoids having to code up a specific class for each type of object we want. We can add an extension method to ObservableCollection to populate the list from an IEnumerable, like this:


public static class LinqHelper
{
    public static ObservableCollection<T> PopulateFrom<T>(this ObservableCollection<T> collection, IEnumerable<T> range)
    {
        foreach (var x in range)
        {
            collection.Add(x);
        }
        return collection;
    }
}

You can use this in any linq select queryby doing:


var query = from regel in document.Element("Regions").Elements("Region")
                             select new MapRegion(this)
                             {
                                 Category = regel.Element("Category").Value,
                                 Description = regel.Element("Description").Value,
                                 Title = regel.Element("Title").Value,
                                 Route = new Route(regel.Element("Route")) { map = mapControl },
                                 Pictures = new ObservableCollection<PictureData>().PopulateFrom( 

                                     from pixel in regel.Element("Pictures").Elements("Picture")
                                     select new PictureData()
                                     {
                                         Picture = pixel.Element("Url").Value,
                                         Audio = pixel.Element("Audio").Value
                                     })
                             }

Why does my WrapPanel scroll when it’s used as an ItemsPanel?

I’m customising a ListBox (in WPF, not Silverlight) and I want it to live in a WrapPanel rather than a vertically scrollingStackPanel. So my template looks like this:


        <ItemsPanelTemplate x:Key="DrivePanelTemplate">
                <WrapPanel IsItemsHost="True" Orientation="Vertical" />
        </ItemsPanelTemplate>

Simple enough. And my ListBox definition looks like this:


<ListBox x:Name="Drives" 
                     ItemsSource="{Binding}" 
                     Height="80" 
                     ItemsPanel="{DynamicResource DrivePanelTemplate}" 
                     ItemTemplate="{DynamicResource DriveTemplate}">

Simple enough. The Height constraint makes sure the items will wrap if there’s enough of them. But when I run it, I get a scroll bar and a single column, not the two columns I want. This seemed odd, since I haven’t specified a scrollviewer.

What I’m missing is that ListBox does specify a scrollviewer and it’s kicking in for me. The fix is to add


ScrollViewer.VerticalScrollBarVisibility="Disabled" 

to the ListBox declaration, and this stops the implicit scrollviewer kicking in, and the WrapPanel does its job as required. Easy.