Using ContentPresenter to switch between different controls for the same property

I was developing an application where at some point a user needs to choose one option from a list of multiple variants. To make the application as user-friendly as possible, I decided to use two different controls to display the list of options:

  1. ListBox when there are few items. One click to select.
  2. ComboBox when there are many. Two clicks to select.

I used to follow an approach, which I later figured out was very common - make two controls and a DataTrigger to switch their visibility, based on some property. Something like this:

It is a valid solution and will produce correct visual results, however it has some downsides:

  • Even when Visibility is set to Collapsed, all the bindings are still working and any changes made in the view model will reflect in both controls.
  • If the bindings are not equivalent (same property, but different expected values, for example) a silent exception will be raised.
  • Both controls are present in the visual tree at the same time, which can make debugging problematic.

All those points contribute to visual tree messiness and also negatively impact performance.

The master solution is to use a ContentPresenter. By switching the active ContentTemplate, only one control will ever exist at the same time, eliminating all the performance issues and ghost binding problems.

In order to utilize it, Content needs to be bound to the current DataContext. Although you may need a more specific binding in your application, binding to the data context will make sure that embedded controls will act in the same way as if they weren’t in the ContentPresenter at all. The data trigger will stay, but instead of changing visibility, it will change the active ContentTemplate. Alternatively, you may want to use the ContentTemplateSelector delegate if your use case is more complicated.

Note: in case you actually have a base style for ContentPresenters, don’t forget to set the BasedOn property in the Style.