我可以在WPF ComboBox中为所选项目使用不同的模板而不是下拉部分中的项目吗?

Pet*_*ter 61 wpf templates combobox

我有一个WPF组合框,它充满了,例如,客户对象.我有一个DataTemplate:

<DataTemplate DataType="{x:Type MyAssembly:Customer}">
    <StackPanel>
        <TextBlock Text="{Binding Name}" />
        <TextBlock Text="{Binding Address}" />
    </StackPanel>
</DataTemplate>
Run Code Online (Sandbox Code Playgroud)

这样,当我打开我的ComboBox时,我可以看到不同的客户的姓名,以及下面的地址.

但是当我选择一个Customer时,我只想在ComboBox中显示Name.就像是:

<DataTemplate DataType="{x:Type MyAssembly:Customer}">
    <StackPanel>
        <TextBlock Text="{Binding Name}" />
    </StackPanel>
</DataTemplate>
Run Code Online (Sandbox Code Playgroud)

我可以为ComboBox中的所选项目选择另一个模板吗?

在答案的帮助下,我解决了这个问题:

<UserControl.Resources>
    <ControlTemplate x:Key="SimpleTemplate">
        <StackPanel>
            <TextBlock Text="{Binding Name}" />
        </StackPanel>
    </ControlTemplate>
    <ControlTemplate x:Key="ExtendedTemplate">
        <StackPanel>
            <TextBlock Text="{Binding Name}" />
            <TextBlock Text="{Binding Address}" />
        </StackPanel>
    </ControlTemplate>
    <DataTemplate x:Key="CustomerTemplate">
        <Control x:Name="theControl" Focusable="False" Template="{StaticResource ExtendedTemplate}" />
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBoxItem}}, Path=IsSelected}" Value="{x:Null}">
                <Setter TargetName="theControl" Property="Template" Value="{StaticResource SimpleTemplate}" />
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>
</UserControl.Resources>
Run Code Online (Sandbox Code Playgroud)

然后,我的ComboBox:

<ComboBox ItemsSource="{Binding Customers}" 
                SelectedItem="{Binding SelectedCustomer}"
                ItemTemplate="{StaticResource CustomerTemplate}" />
Run Code Online (Sandbox Code Playgroud)

让它工作的重要部分是Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBoxItem}}, Path=IsSelected}" Value="{x:Null}"(值应为x的部分:Null,而不是True).

Mar*_*eIV 58

使用上面提到的DataTrigger/Binding解决方案的问题是双重的.第一个是你实际上最终得到一个绑定警告,你找不到所选项目的相对来源.然而,更大的问题是你的数据模板混乱,并使它们特定于ComboBox.

我提出的解决方案遵循WPF设计更好,因为它使用DataTemplateSelector,您可以使用SelectedItemTemplate和DropDownItemsTemplate属性以及两者的选择器指定单独的模板.

public class ComboBoxTemplateSelector : DataTemplateSelector
{
    public DataTemplate         SelectedItemTemplate          { get; set; }
    public DataTemplateSelector SelectedItemTemplateSelector  { get; set; }
    public DataTemplate         DropdownItemsTemplate         { get; set; }
    public DataTemplateSelector DropdownItemsTemplateSelector { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        var itemToCheck = container;

        // Search up the visual tree, stopping at either a ComboBox or
        // a ComboBoxItem (or null). This will determine which template to use
        while(itemToCheck != null && !(itemToCheck is ComboBoxItem) && !(itemToCheck is ComboBox))
            itemToCheck = VisualTreeHelper.GetParent(itemToCheck);

        // If you stopped at a ComboBoxItem, you're in the dropdown
        var inDropDown = (itemToCheck is ComboBoxItem);

        return inDropDown
            ? DropdownItemsTemplate ?? DropdownItemsTemplateSelector?.SelectTemplate(item, container)
            : SelectedItemTemplate  ?? SelectedItemTemplateSelector?.SelectTemplate(item, container); 
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:为简单起见,我的示例代码使用了新的"?".C#6(VS 2015)的特点.如果您使用的是旧版本,只需删除"?" 并在上面调用'SelectTemplate'之前显式检查null并返回null,否则如下:

return inDropDown
    ? DropdownItemsTemplate ??
        ((DropdownItemsTemplateSelector != null)
            ? DropdownItemsTemplateSelector.SelectTemplate(item, container)
            : null)
    : SelectedItemTemplate ??
        ((SelectedItemTemplateSelector != null)
            ? SelectedItemTemplateSelector.SelectTemplate(item, container)
            : null)
Run Code Online (Sandbox Code Playgroud)

我还添加了一个标记扩展,它简单地创建并返回上面的类,以方便XAML.

public class ComboBoxTemplateSelectorExtension : MarkupExtension
{
    public DataTemplate         SelectedItemTemplate          { get; set; }
    public DataTemplateSelector SelectedItemTemplateSelector  { get; set; }
    public DataTemplate         DropdownItemsTemplate         { get; set; }
    public DataTemplateSelector DropdownItemsTemplateSelector { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return new ComboBoxTemplateSelector(){
            SelectedItemTemplate          = SelectedItemTemplate,
            SelectedItemTemplateSelector  = SelectedItemTemplateSelector,
            DropdownItemsTemplate         = DropdownItemsTemplate,
            DropdownItemsTemplateSelector = DropdownItemsTemplateSelector
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是你如何使用它.漂亮,干净,清晰,模板保持"纯净"

注意:'是:'这是我的xmlns映射,用于将类放在代码中.确保导入您自己的命名空间并根据需要更改"is:".

<ComboBox x:Name="MyComboBox"
    ItemsSource="{Binding Items}"
    ItemTemplateSelector="{is:ComboBoxTemplateSelector
        SelectedItemTemplate={StaticResource MySelectedItemTemplate},
        DropdownItemsTemplate={StaticResource MyDropDownItemTemplate}}" />
Run Code Online (Sandbox Code Playgroud)

如果您愿意,也可以使用DataTemplateSelectors ...

<ComboBox x:Name="MyComboBox"
    ItemsSource="{Binding Items}"
    ItemTemplateSelector="{is:ComboBoxTemplateSelector
        SelectedItemTemplateSelector={StaticResource MySelectedItemTemplateSelector},
        DropdownItemsTemplateSelector={StaticResource MyDropDownItemTemplateSelector}}" />
Run Code Online (Sandbox Code Playgroud)

或者混搭!这里我使用的是所选项目的模板,但是DropDown项目的模板选择器.

<ComboBox x:Name="MyComboBox"
    ItemsSource="{Binding Items}"
    ItemTemplateSelector="{is:ComboBoxTemplateSelector
        SelectedItemTemplate={StaticResource MySelectedItemTemplate},
        DropdownItemsTemplateSelector={StaticResource MyDropDownItemTemplateSelector}}" />
Run Code Online (Sandbox Code Playgroud)

此外,如果您没有为选定项或下拉项指定模板或TemplateSelector,它只会再次回到基于数据类型的数据模板的常规解析,如您所料.因此,例如,在下面的情况中,所选项目的模板显式设置,但下拉列表将继承适用于数据上下文中对象的DataType的数据模板.

<ComboBox x:Name="MyComboBox"
    ItemsSource="{Binding Items}"
    ItemTemplateSelector="{is:ComboBoxTemplateSelector
        SelectedItemTemplate={StaticResource MyTemplate} />
Run Code Online (Sandbox Code Playgroud)

请享用!

  • 我把你的帽子带给你一个真正可重复使用的解决方案! (3认同)

H.B*_*.B. 31

简单方案:

<DataTemplate>
    <StackPanel>
        <TextBlock Text="{Binding Name}"/>
        <TextBlock Text="{Binding Address}">
            <TextBlock.Style>
                <Style TargetType="TextBlock">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=ComboBoxItem}}" Value="{x:Null}">
                            <Setter Property="Visibility" Value="Collapsed"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </TextBlock.Style>
        </TextBlock>
    </StackPanel>
</DataTemplate>
Run Code Online (Sandbox Code Playgroud)

(注意,在框中选择并显示的元素而不是列表中的元素不在其中,ComboBoxItem因此触发器上Null)

如果您想要切换整个模板,您也可以使用触发器来执行此操作,例如将不同的ContentTemplate应用于aContentControl.DataType如果您只是更改此选择案例的模板,这也允许您保留基于默认的模板选择,例如:

<ComboBox.ItemTemplate>
    <DataTemplate>
        <ContentControl Content="{Binding}">
            <ContentControl.Style>
                <Style TargetType="ContentControl">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=ComboBoxItem}}"
                                        Value="{x:Null}">
                            <Setter Property="ContentTemplate">
                                <Setter.Value>
                                    <DataTemplate>
                                        <!-- ... -->
                                    </DataTemplate>
                                </Setter.Value>
                            </Setter>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </ContentControl.Style>
        </ContentControl>
    </DataTemplate>
</ComboBox.ItemTemplate>
Run Code Online (Sandbox Code Playgroud)

请注意,此方法将导致绑定错误,因为找不到所选项的相对源.对于另一种方法,请参阅MarqueIV的答案.

  • `IsSelected`不可为空,因此永远不能为空.你不需要`Path = IsSelected`,因为对周围ComboBoxItem的NULL检查就足够了. (4认同)