Question

I have a listbox and it's bound to a list of simple objects. As we know the listbox by default has it's items host as a Stackpanel and so it lays out items in this fashion

item1
item2
item3
item4
item5
item6

However i have some conditions through which i check whether the item is to be displayed horizontally or vertically and therefore the items could be laid out in this fashion

Eg :-

item1
item2
item3 item4 item 5
item6

How is it possible to do this?

(If you are wondering why would i ever need such a thing, an example scenario would be "facebook style updates" where if a user uploads 3-4 photos continously they don't always appear on the next line but it may appear horizontally whereas if he posts some event it appears in next line.)

Thanks in advance :)

Was it helpful?

Solution

The fundementals of the solution is to use another ItemsControl in the ItemTemplate of the ListBox. This ItemsControl should have a horizontal oriented StackPanel as its ItemsPanel.

Here is a basic example. Lets start with some very simple testdata:-

public class TestStringList : List<string>
{
    public TestStringList()
    {
        AddRange(new[] {"Anthony", "Kar", "Martin", "Jon", "Erik", "Darin",
            "Balus", "Mike", "Hans", "Alex", "Anomie", "David" });

    }
}

Now we want to display this list in a ListBox but keep all the names that have the same first initial on the same line. I'm going to use an implementation of IValueConverter to deal with the grouping that we need. If you are using MVVM then you'd have your ViewModel take of this.

public class Grouper : IValueConverter
{

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return ((IEnumerable<string>)value).OrderBy(s => s).GroupBy(s => s[0]);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

The output of this converter is basically IEnumerable<IEnumerable<string>> which is what we want. The outer ListBox will enumerate the outer set and the inner ItemsControl will enumerate the inner set of strings which will be a set of names with the same initial.

Here is the xaml:-

<UserControl x:Class="SilverlightApplication1.SimpleGrouping"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:SilverlightApplication1"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <UserControl.Resources>
        <local:TestStringList x:Key="TestData" />
        <local:Grouper x:Key="grouper" />
    </UserControl.Resources>

    <Grid x:Name="LayoutRoot">
        <ListBox ItemsSource="{Binding Converter={StaticResource grouper}, Source={StaticResource TestData}}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <ItemsControl ItemsSource="{Binding}">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <StackPanel Orientation="Horizontal" />
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding}" Margin="5" />
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</UserControl>

OTHER TIPS

If using the MVVM pattern I would do something like this:

  1. Create a ViewModel for the view your list box is in. This VM contains a collection of ListItemViewModel instances (see next point)
  2. Create a ViewModel called ListItemViewModel (give it a more appropriate name, based on your domain). This view model contains a collection of ItemViewModel instances (see next point).
  3. Create a ViewModel called ItemViewModel. Each of these backs a single item in the list. Give this a more appropriate name based on your domain.
  4. Create a View that contains your listbox. Bind your listbox to the collection of ListItemViewModels in the VM. The item template for this list box will be a ListItemView (see next point). The items panel template will be the default StackPanel.
  5. Create a ListItemView which has a ListItemViewModel data context. This view consists of a horizontal StackPanel of ItemViews (see next point).
  6. Create an ItemView which is backed by the ItemViewModel.

Your view would look something like this, each with a correspoding ViewModel.

Like I said above, you'll definitely want to change the name of your views/view models, mine are for demonstration purposes only :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top