Switching ListBox ItemTemplate based on both item type and view option

3.5k views Asked by At

I'm currently implementing a listbox in WPF that will have 2 alternative layouts for its items:

Alternative list item styles of wrapping icons and detailed tiles

So far, I've done this using a DataTrigger to switch the ItemTemplate for the ListBox and it's working well:

<ListBox ItemsSource="{Binding Runs}" SelectedItem="{Binding SelectedRun}">
  <ListBox.Style>
    <Style TargetType="ListBox">
      <Setter Property="ItemTemplate" Value="{StaticResource tileTemplate}"/>
      <Style.Triggers>
        <DataTrigger Binding="{Binding ShowRunsAsIcons}" Value="True">
          <Setter Property="ItemTemplate" Value="{StaticResource iconTemplate}"/>
        </DataTrigger>
      </Style.Triggers>
    </Style>
  </ListBox.Style>
</ListBox>

However, the Runs collection to which the list is bound will also contain different types of object:

interface IRunItem
{
  // ...
}

class CompletedRunItem : IRunItem
{
  // ...
}

class PendingRunItem : IRunItem
{
  // ...
}

Each of the object types should have its own 'tile' and 'icon' templates (making 4 templates in total). What's the best way of switching on these two properties to according to the boolean ShowRunsAsIcons and the type of the list item?

I've considered using a pair of DataTemplateSelector subclasses -- one to choose between tile templates based on item type, and one to choose between icon templates based on item type -- but this just feels horribly clunky. I feel as though I should be taking advantage of WPF's ability to choose the correct template based on the object's type, but in this instance, I don't see how to combine that with the list's different view options.

Any ideas of how to do this that's more in the spirit of WPF?

Thanks.

1

There are 1 answers

0
Mal Ross On BEST ANSWER

Although I'm not convinced it's the best answer, I've changed my approach to take advantage of WPF's automatic template selection. I now have 'top-level' data templates defined for each of my concrete data classes.

These data templates contain nothing but a ContentControl whose ContentTemplate property is set via a DataTrigger, binding to the data context's ShowRunsAsIcons property.

As an example, here's the keyless data template for PendingRunItem:

<DataTemplate DataType="{x:Type Common:PendingRunItem}">
  <ContentControl Content="{Binding}">
    <ContentControl.Style>
      <Style TargetType="ContentControl">
        <Setter Property="ContentTemplate" Value="{StaticResource pendingTileTemplate}"/>
        <Style.Triggers>
          <DataTrigger Binding="{Binding DataContext.ShowRunsAsIcons, RelativeSource={RelativeSource FindAncestor, AncestorType=ListBox}}" Value="True">
            <Setter Property="ContentTemplate" Value="{StaticResource pendingIconTemplate}"/>
          </DataTrigger>
        </Style.Triggers>
      </Style>
    </ContentControl.Style>
  </ContentControl>
</DataTemplate>

The icon and tile representations for the relevant classes are then just regular data templates. And the ListBox no longer needs its Style property defined:

<ListBox ItemsSource="{Binding Runs}" SelectedItem="{Binding SelectedRun}"/>

I'd be interested to know people's thoughts on this approach and its benefits and drawbacks when compared to using a DataTemplateSelector or two.