Conditional Content Presenting via WPF ContentPresenter

October 28, 2016
2 min read
❤️ Thank You for Supporting Ukraine!
As Russia wages a genocidal war against my country, I'm grateful to everyone who continues to stand with Ukraine in our fight for freedom.

Sometimes you may find yourself facing a problem of having to switch between multiple presentations based on some condition. For example, you may want to display the same list of items differently depending on whether multi-select is enabled or not.

The easiest and the most obvious way to achieve this is to bind the Visibility property of the elements so that one is shown while the other is collapsed or hidden. However, this approach comes at a cost.

Downsides of switching visibility

Changing the value of the Visibility property tends to get the job done, but it has some noticeable drawbacks:

  • You have to use a converter to turn the bound value into Visibility
  • It gets complicated when there are more than two presentations to switch between or when there is more than one condition
  • Even when collapsed or hidden, all the UI elements still remain in the visual tree

The last point is something that tends to get overlooked and may lead to problems. When you make an element invisible, be it by setting the Visibility to Hidden or Collapsed, it doesn't actually disappear from the visual tree. This means it still takes part in most interactions, including bindings. Additionally, if a hidden element has a binding that fails, an exception will be raised and silently suppressed by the framework, potentially causing performance issues.

Using ContentPresenter

To address this problem more efficiently you can use ContentPresenter instead. This control can switch its content using predefined triggers and data templates.

Here's an example of how a typical setup may look like using ContentPresenter:

<ContentPresenter Content="{Binding}">

    <!-- Presentations -->
    <ContentPresenter.Resources>
        <DataTemplate x:Key="ComboBoxPresenter">
            <ComboBox ItemsSource="{Binding Items}" IsReadOnly="True" />
        </DataTemplate>
        <DataTemplate x:Key="ListBoxPresenter">
            <ListBox ItemsSource="{Binding Items}" SelectionMode="Multiple" />
        </DataTemplate>
    </ContentPresenter.Resources>

    <!-- Triggers -->
    <ContentPresenter.Style>
        <Style TargetType="{x:Type ContentPresenter}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsMultiselect}" Value="False">
                    <Setter Property="ContentTemplate" Value="{StaticResource ComboBoxPresenter}" />
                </DataTrigger>
                <DataTrigger Binding="{Binding IsMultiselect}" Value="True">
                    <Setter Property="ContentTemplate" Value="{StaticResource ListBoxPresenter}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ContentPresenter.Style>

</ContentPresenter>

Here we bind the Content property to the current DataContext so that it can be accessed by the data templates.

The ContentPresenter.Resources lists data templates used for each separate presentation — in this case it's a combobox and a listbox bound to the same collection. To configure which data template is used when, we declare a set of triggers that check the value of the IsMultiselect property and switch the ContentTemplate accordingly.

Note how using triggers gives us more flexibility — we don't need to use converters in order to turn IsMultiselect into an instance of Visibility, we just bind to it directly.

Using this approach, we're also not leaving any unnecessary mess in the visual tree because we're changing the actual content instead of just hiding it.

🔔 Subscribe for more
Want to know when I post a new article? Follow me on Twitter or subscribe to the RSS Feed