WPF: как настроить SelectionBoxItem в ComboBox

Я хочу отобразить пользовательский шаблон / элемент как выбранный элемент в ComboBox (этот элемент фактически не существует в списке элементов и обновляется по-разному). Это даже не должно быть элементом, просто предоставление пользовательского представления будет работать.

Как я могу это сделать, оставаясь в текущей теме ComboBox (так что замена ControlTemplate невозможна)? Насколько я вижу, все свойства SelectionBox* не редактируются и внутренне ComboBox использует неназванное объект ContentPresenter.

4 ответов


Я бы сделал так:

<Window.Resources>

  <DataTemplate x:Key="NormalItemTemplate" ...>
    ...
  </DataTemplate>

  <DataTemplate x:Key="SelectionBoxTemplate" ...>
    ...
  </DataTemplate>

  <DataTemplate x:Key="CombinedTemplate">
    <ContentPresenter x:Name="Presenter"
       Content="{Binding}"
       ContentTemplate="{StaticResource NormalItemTemplate}" />
    <DataTemplate.Triggers>
      <DataTrigger
        Binding="{Binding RelativeSource={RelativeSource FindAncestor,ComboBoxItem,1}}"
        Value="{x:Null}">
        <Setter TargetName="Presenter" Property="ContentTemplate"
                Value="{StaticResource SelectionBoxTemplate}" />
      </DataTrigger>
    </DataTemplate.Triggers>
  </DataTemplate>

</Window.Resources>

...

<ComboBox
  ItemTemplate="{StaticResource CombinedTemplate}"
  ItemsSource="..."
  ... />

причина, по которой это работает, заключается в том, что CombinedTemplate обычно просто использует NormalItemTemplate для представления своих данных, но если нет ComboBoxItem предок предполагает, что он находится в поле выбора, поэтому он использует SelectionBoxTemplate.

обратите внимание, что три DataTemplates может быть включен в любой уровень ResourceDictionary (не только в


если у меня есть это прямо, вы хотите элемент управления, который имеет что-то произвольное, отображаемое вместе с раскрывающейся кнопкой, которая отображает список элементов с флажками рядом с ними?

я бы даже не стал пытаться перестроить ComboBox для достижения этой цели. Проблема в том, что ComboBox более специализирован по другому пути, чем то, что вам нужно. Если вы посмотрите на ComboBox ControlTemplate Пример, вы увидите, что он просто использует Popup управление для отображения список возможных значений.

вы можете взять кусочки этого шаблона в качестве руководства для создания UserControl что легче понять и лучше обеспечивает то, что вы хотите. Вы даже сможете добавить SelectedItems свойство и такое, что ComboBox не предоставляет.

пример того, что я подразумеваю под руководствам:Popup есть IsOpen собственность. В шаблоне элемента управления установлено значение {TemplateBinding IsDropDownOpen}, что означает ComboBox классе IsDropDownOpen свойство, которое изменяется в порядке для управления расширением / сворачиванием Popup.


комментарий Алексея Митева к Рэю Бернсу ответ вдохновил меня написать следующий достаточно короткий служебный класс, который я теперь использую во всех своих проектах WPF:

public class ComboBoxItemTemplateSelector : DataTemplateSelector
{
    public List<DataTemplate> SelectedItemTemplates { get; } = new List<DataTemplate>();
    public List<DataTemplate> DropDownItemTemplates { get; } = new List<DataTemplate>();

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        return GetVisualParent<ComboBoxItem>(container) == null
            ? ChooseFrom(SelectedItemTemplates, item)
            : ChooseFrom(DropDownItemTemplates, item);
    }

    private static DataTemplate ChooseFrom(IEnumerable<DataTemplate> templates, object item)
    {
        if (item == null)
            return null;
        var targetType = item.GetType();
        return templates.FirstOrDefault(t => (t.DataType as Type) == targetType);
    }

    private static T GetVisualParent<T>(DependencyObject child) where T : Visual
    {
        while (child != null && !(child is T))
            child = VisualTreeHelper.GetParent(child);
        return child as T;
    }
}

С этим в панели инструментов можно написать XAML следующим образом:

<UserControl.Resources>
     <DataTemplate x:Key="SelectedItemTemplateForInt" DataType="{x:Type system:Int32}">
         <!-- ... -->
     </DataTemplate>

     <DataTemplate x:Key="SelectedItemTemplateForDouble" DataType="{x:Type system:Double}">
         <!-- ... -->
     </DataTemplate>

     <DataTemplate x:Key="DropDownItemTemplateForInt" DataType="{x:Type system:Int32}">
         <!-- ... -->
     </DataTemplate>

     <DataTemplate x:Key="DropDownItemTemplateForDouble" DataType="{x:Type system:Double}">
         <!-- ... -->
     </DataTemplate>
</UserControl.Resources>

<ComboBox>
    <ComboBox.ItemTemplateSelector>
        <local:ComboBoxItemTemplateSelector>
            <local:ComboBoxItemTemplateSelector.SelectedItemTemplates>
                <StaticResource ResourceKey="SelectedItemTemplateForInt" />
                <StaticResource ResourceKey="SelectedItemTemplateForDouble" />
            </local:ComboBoxItemTemplateSelector.SelectedItemTemplates>

            <local:ComboBoxItemTemplateSelector.DropDownItemTemplates>
                <StaticResource ResourceKey="DropDownItemTemplateForInt" />
                <StaticResource ResourceKey="DropDownItemTemplateForDouble" />
            </local:ComboBoxItemTemplateSelector.DropDownItemTemplates>
        </local:ComboBoxItemTemplateSelector>
    </ComboBox.ItemTemplateSelector>
</ComboBox>

вам нужно заглянуть в триггеры и стили. Вы также можете изучить некоторые из моих старых вопросов здесь, в StackOverflow, которые помогли мне победить эти проблемы: