简体   繁体   English

WPF:如何在ComboBox中自定义SelectionBoxItem

[英]WPF: How to customize SelectionBoxItem in ComboBox

I want to display a custom template/item as selected item in ComboBox (this item does not actually exist in the list of items and is updated differently). 我想在ComboBox中显示一个自定义模板/项目作为选定项目(该项目实际上不存在于项目列表中,并且更新方式不同)。 This does not even needs to be an item, just providing a custom view would work. 这甚至不需要是一个项目,只需提供自定义视图即可。

How can I do this while staying within current ComboBox theme (so no ControlTemplate replacement possible)? 如何在保持当前ComboBox主题的同时执行此操作(因此无法替换ControlTemplate)? As far as I see, all of SelectionBox* properties are not editable and internally ComboBox uses unnamed ContentPresenter. 据我所知,所有SelectionBox *属性都不可编辑,内部ComboBox使用未命名的ContentPresenter。

I would do it like this: 我会这样做:

<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="..."
  ... />

The reason this works is that CombinedTemplate normally just uses NormalItemTemplate to present its data, but if there is no ComboBoxItem ancestor it assumes it is in the selection box so it uses SelectionBoxTemplate. 这样做的原因是CombinedTemplate通常只使用NormalItemTemplate来呈现它的数据,但是如果没有ComboBoxItem祖先,它假定它在选择框中,所以它使用SelectionBoxTemplate。

Note that the three DataTemplates could be included in any level of ResourceDictionary (not just at the Window level) or even directly within the ComboBox , depending on your preference. 请注意,三个DataTemplates可以包含在任何级别的ResourceDictionary (不仅仅是在Window级别),甚至可以直接包含在ComboBox ,具体取决于您的偏好。

If I have this straight, you want a control that has something arbitrary displayed along with a drop-down button that displays a list of items with checkboxes next to them? 如果我直截了当,你想要一个随意显示的控件以及一个下拉按钮,它会显示一个带有复选框的项目列表吗?

I wouldn't even bother trying to restyle a ComboBox to achieve this. 我甚至不愿意尝试重新设计ComboBox来实现这一目标。 The problem is that ComboBox is more specialized down a different path than what you need. 问题是ComboBox在不同于您需要的路径上更加专业化。 If you look at the ComboBox ControlTemplate Example , you'll see that it simply uses a Popup control to display the list of possible values. 如果查看ComboBox ControlTemplate示例 ,您将看到它只是使用Popup控件来显示可能的值列表。

You can take pieces of that template as guidance to creating a UserControl that is easier to understand and better provides what you want. 您可以将该模板的各个部分作为创建更容易理解的UserControl指导,并更好地提供您想要的内容。 You'll even be able to add a SelectedItems property and such that ComboBox doesn't provide. 您甚至可以添加一个SelectedItems属性,以及ComboBox不提供的属性。

An example of what I mean by guidance: the Popup has an IsOpen property. 我的意思是指导的一个例子: Popup有一个IsOpen属性。 In the control template, it's set to {TemplateBinding IsDropDownOpen} , which means that the ComboBox class has an IsDropDownOpen property that is changed in order to control the expand/collapse of the Popup . 在控件模板中,它设置为{TemplateBinding IsDropDownOpen} ,这意味着ComboBox类具有更改的IsDropDownOpen属性,以便控制Popup的展开/折叠。

Alexey Mitev's comment on Ray Burns' answer inspired me to write the following reasonably short utility class, which I now use in all my WPF projects: Alexey Mitev对Ray Burns 回答的评论激发了我写下以下相当短的实用工具类,我现在在所有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;
    }
}

With that in the toolbox, it's possible to write XAML like this: 在工具箱中,可以像这样编写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>

You need to look into Triggers and Styles . 您需要查看触发器样式 You might also want to look into some of my older questions here on StackOverflow that helped me conquer these problems: 您可能还想在StackOverflow上查看我的一些旧问题,这些问题帮助我克服了这些问题:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM