I have a ComboBox
displaying a list of items. I've created a converter to map from this object type to a string
value.
<ComboBox ItemsSource="{Binding SelectedDrawing.Icons}" Name="TrackElementSelector" SelectedItem="{Binding SelectedElement}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=., Converter={StaticResource LayoutElementToStringConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
The problem is that when I select/click a value in the ComboBox
, the ComboBox
renders the SelectedItem
directly (it's a UserControl
defined elsewhere in my project). For example, imagine seeing a literal Button
after clicking the string element "Button" on the ComboBox
. I do not want this behavior.
Why isn't the ItemTemplate
being used here? It seems like the ItemTemplate
is only used until a value is selected.
I've discovered another unexpected result. When I drop down the ComboBox
to make a selection, the instances where the controls appear in other places of my application disappear. I'm wondering if there's something more complex going on due to stuff in my codebase, or if this is all consequence of trying to display UserControl
objects inside a ComboBox
.
You discovered an interesting behavior of the the ComboBox
control. When binding items, how the items are presented sometimes depends on their type. For example, if you bind items that are already of type ComboBoxItem
, it will not create additional containers, since that would be redundant. This is a common design decision for other ItemsControl
s as well.
However, your issue originates from a different special treatment of the selected item in ComboBox
regarding ContentControl
s. If the selected item is of this type or any of its direct or indirect derivatives (like UserControl
), ComboBox
will use its Content
, ContentTemplate
and ContentStringFormat
, instead of the ItemTemplate
or ItemStringFormat
defined for the items on the ComboBox
itself, thus ignoring the ItemTemplate
.
You can see this in the implementation of ComboBox
within the method UpdateSelectionBoxItem
:
// if Items contains an explicit ContentControl, use its content instead
// (this handles the case of ComboBoxItem)
ContentControl contentControl = item as ContentControl;
if (contentControl != null)
{
item = contentControl.Content;
itemTemplate = contentControl.ContentTemplate;
stringFormat = contentControl.ContentStringFormat;
}
I do not know why it was implemented like this, but the original comment suggests that this might be intended to handle the case where the bound items are already ComboBoxItem
. Maybe this behavior was not intented, but accidential.
If you wanted to change this behavior, you have these options:
Create your own custom implementation of ComboBox
.
Add the item template to UserControl
as content template as well.
Although it works with the given implementation, it is sort of a hack.
Create a custom control template which ignores the special handling.
You would create a copy of the default style and control template and adapt its ContentPresenter
. By default it is bound to the special SelectionBoxItemTemplate
property. You could ignore it by binding to the ItemTemplate
property instead.
<ContentPresenter x:Name="contentPresenter"
Content="{TemplateBinding SelectedItem}"
ContentTemplate="{TemplateBinding ItemTemplate}"
...>
Do not use a ContentControl
or derived type for items, but use data templating with data items as source instead, as it is intended, see Data Templating Overview.
You should not bind UI elements as items source of the ComboBox
. Instead, bind the data encapsulated in a model instead and provide a suitable data template to display it. The data could be an Icon
class with a property Path
or even simply a string
representing the path.
The data template could reuse your UserControl
type to display it (if it is correctly implemented to bind data using its data context). This will result in a better design and will most likely eliminate the need for a value converter, too. Futhermore, it will solve your second issue automatically, the disappearing controls.
I strongly recommend you to use this option, since it does not require any customization of framework elements and is much easier to do and maintain.
In WPF UI controls are attached to the so-called visual tree, which is a hierarchical object graph representation of your user interface. In this graph, each UI element instance can only have a single parent. Consequently, if you "use" an existing control in a different place, it is effectively reassigned to the corresponding parent be it in the same or a different visual tree (e.g. another window) and "disappears" from the original place.