Different template for items in ComboBox's drop-down list and for selected item
Asked Answered
U

2

17

I have a ComboBox with fairly complex template for individual items, which includes two images and several lines of text:

enter image description here

However, the selected item in the ComboBox itself doesn't display correctly, because the vertical space is too limited (I can't make it higher, because it is a part of a ToolBar).

How can I make the ComboBox use a different template for the item which is displayed in the ComboBox itself? (the default ToString representation would do just fine)

Thanks!

Ukase answered 26/11, 2011 at 14:30 Comment(6)
Is using DataTemplateSelector would do the trick for you or you are looking for an other soluion? Perhaps I do not get question right?Audy
@Audy I don't think DataTemplateSelector can do this (I didn't try it though), IIRC it is evaluated only when the items are being loaded. Anyways I would prefer some simpler solution that wouldn't involve a class for each ComboBox I create (there will be several of them).Panthea
Ok what are you trying to achieve? Different UI representation for items based on some criteria?Audy
@Audy As you can see on the picture, the item which is currently selected in the ComboBox is not displayed correctly. I just want to fix that.Panthea
How images are drawn? Is they loaded from file or somehow drawn using WPF facilities?Audy
@s11 The images are generated on runtime and translated into a BitmapSource, which is then simply displayed using WPF's Image.Panthea
S
30

The selected item (in the ComboBox itself, not the dropdown) is not inside a ComboBoxItem so you can do something like this:

<ComboBox.ItemTemplate>
    <DataTemplate>
        <ContentControl Content="{Binding}">
            <ContentControl.Style>
                <Style TargetType="{x:Type ContentControl}">
                    <!-- Complex default template -->
                    <Setter Property="ContentTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <Image Source="{Binding XPath=media:thumbnail/@url}" Width="100" Height="100" />
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                    <Style.Triggers>
                        <!-- Simple selection box template -->
                        <DataTrigger
                                Binding="{Binding RelativeSource={RelativeSource AncestorType=ComboBoxItem}}"
                                Value="{x:Null}">
                            <Setter Property="ContentTemplate">
                                <Setter.Value>
                                    <DataTemplate>
                                        <TextBlock Text="{Binding XPath=title}" />
                                    </DataTemplate>
                                </Setter.Value>
                            </Setter>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </ContentControl.Style>
        </ContentControl>
    </DataTemplate>
</ComboBox.ItemTemplate>

(Edit: Note that the binding in the for the selection box will throw errors because the RelativeSource is not found. There are various options of circumventing this, one being a custom value converter that returns true or false depending on whether the ancestor exists (manual tree walking).)

Successive answered 26/11, 2011 at 15:11 Comment(1)
I wonder what WPF team were thinking when designing ComboBox. What would be the naive approach to overcome this problem (when data template is used in 2 places: for list of items and for selected item when list is hidden)? ContentControl with styling seems a common hack to deal with multiple templates (instead of crappy idea with DataTemplateSelector), but I really hate that checking for ancestor, which in turn also has issues as per latest edit.Supreme
S
11

I was searching for a standard (not hacky and without binding errors) solution to this problem. And I found it here: using DataTemplateSelector.

It's the same idea as per @H.B. answer: check whenever there is a ComboBoxItem as a parent in visual tree.

public class ComboBoxItemTemplateSelector : DataTemplateSelector
{
    public DataTemplate SelectedTemplate { get; set; }
    public DataTemplate DropDownTemplate { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        while (container != null)
        {
            container = VisualTreeHelper.GetParent(container);
            if (container is ComboBoxItem)
                return DropDownTemplate;
        }
        return SelectedTemplate;
    }
}

Usage:

<ComboBox.ItemTemplateSelector>
    <local:ComboBoxItemTemplateSelector>
        <local:ComboBoxItemTemplateSelector.SelectedTemplate>
            <DataTemplate>
                ... simple template for selected item
            </DataTemplate>
        </local:ComboBoxItemTemplateSelector.SelectedTemplate>
        <local:ComboBoxItemTemplateSelector.DropDownTemplate>
            <DataTemplate>
                ... complex template used by dropdown items
            </DataTemplate>
        </local:ComboBoxItemTemplateSelector.DropDownTemplate>
    </local:ComboBoxItemTemplateSelector>
</ComboBox.ItemTemplateSelector>
Supreme answered 15/3, 2016 at 16:42 Comment(3)
Works great after selection. But on Init is SelectTemplate function not called.Backbreaker
@LWS, what is "on Init"? Why do you want it to be called on init and not when combobox decide? It may be worth to ask a question, I doubt to be able to help without minimal reproducible example.Supreme
When the Dialog is loaded. My ComboBox is empty. After first selection works everything fine. I just use your code without any modifications.Backbreaker

© 2022 - 2024 — McMap. All rights reserved.