English 中文(简体)
我能否在WPF ComboBox的选定项目中使用一个不同于 drop落部分的模板?
原标题:Can I use a different Template for the selected item in a WPF ComboBox than for the items in the dropdown part?

我有一个WPF Combo盒,填满客户物体。 我有一个数据模板:

<DataTemplate DataType="{x:Type MyAssembly:Customer}">
    <StackPanel>
        <TextBlock Text="{Binding Name}" />
        <TextBlock Text="{Binding Address}" />
    </StackPanel>
</DataTemplate>

这样,当我打开我的ComboBox时,我可以看到不同的客户,他们的姓名和地址如下。

但在我选择客户时,我只想在ComboBox展示姓名。 类似:

<DataTemplate DataType="{x:Type MyAssembly:Customer}">
    <StackPanel>
        <TextBlock Text="{Binding Name}" />
    </StackPanel>
</DataTemplate>

Can 我为ComboBox的选定项目选择另一个模板?

<>Solution

在答复的帮助下,我解决了这一问题:

<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>

接着,我的ComboBox:

<ComboBox ItemsSource="{Binding Customers}" 
                SelectedItem="{Binding SelectedCustomer}"
                ItemTemplate="{StaticResource CustomerTemplate}" />

使其开展工作的重要部分是<条码>Bled=”{具有约束力的相关来源: Mode=FindAncestor, AncestorType={x:Type ComboBoxItem, Path=IsS selected}" Value="{x:Null}"(部分价值应为x:Null,而不是真实)。

最佳回答

采用上述数据触发/具有约束力的解决办法的问题有两个方面。 第一,你实际上最后发出具有约束力的警告,你可以找到选定项目的相对来源。 然而,最大的问题是,你删除了你的数据模板,将其具体化为ComboBox。

解决办法一采用WPF设计较好,因为它使用<条码>DataTemplateSelector,根据该编码,你可使用<条码>、S selectedItemTemplate和<条码>DropDownItemsTemplate,以及“sele”两种特性的变式。

<>说明:C#9的更新版,在检索期间能够使用配对模式。

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 is not null
        and not ComboBox
        and not ComboBoxItem)
            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); 
    }
}

为了便于在XAML中使用,我也列入一个标记展期,该展期只是在其<编码>ProvideValue功能中创造和回收上述类别。

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)
        => new ComboBoxTemplateSelector(){
            SelectedItemTemplate          = SelectedItemTemplate,
            SelectedItemTemplateSelector  = SelectedItemTemplateSelector,
            DropdownItemsTemplate         = DropdownItemsTemplate,
            DropdownItemsTemplateSelector = DropdownItemsTemplateSelector
        };
}

And here s how you use it. Nice, clean and clear and your templates stay pure

注:此处是我的xmlns绘图,我把这一分类列入法典。 确保进口自己的名称空间和变化是:酌情。

<ComboBox x:Name="MyComboBox"
    ItemsSource="{Binding Items}"
    ItemTemplateSelector="{is:ComboBoxTemplateSelector
        SelectedItemTemplate={StaticResource MySelectedItemTemplate},
        DropdownItemsTemplate={StaticResource MyDropDownItemTemplate}}" />

如果你更愿意的话,你也可以使用数据模板。

<ComboBox x:Name="MyComboBox"
    ItemsSource="{Binding Items}"
    ItemTemplateSelector="{is:ComboBoxTemplateSelector
        SelectedItemTemplateSelector={StaticResource MySelectedItemTemplateSelector},
        DropdownItemsTemplateSelector={StaticResource MyDropDownItemTemplateSelector}}" />

或混合和配对! 在此,使用选定项目模板的Im,但DroopDown项目模板选择器。

<ComboBox x:Name="MyComboBox"
    ItemsSource="{Binding Items}"
    ItemTemplateSelector="{is:ComboBoxTemplateSelector
        SelectedItemTemplate={StaticResource MySelectedItemTemplate},
        DropdownItemsTemplateSelector={StaticResource MyDropDownItemTemplateSelector}}" />

此外,如果你没有为选定或减少的项目指定一个模板或一个模版,那么就象你所期望的那样,它只是回到根据数据类型定期解决数据模板。 因此,例如,在以下情况下,选定的项目有其模板,但每当数据环境下该物体的数据类型适用数据模板时,则会继承该模板。

<ComboBox x:Name="MyComboBox"
    ItemsSource="{Binding Items}"
    ItemTemplateSelector="{is:ComboBoxTemplateSelector
        SelectedItemTemplate={StaticResource MyTemplate} />

Enjoy!

问题回答

简单解决办法:

<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>

(Note that the element that is selected and displayed in the box and not the list is not inside a ComboBoxItem hence the trigger on Null)

如果您想从整个模板中转出,你也可以这样做,并且将触发器用于例如,适用不同的<代码>ContentTemplate至ContentControl。 这还使你保留了一个基于标准的模板选择,如果你只是修改了这一有选择案件的模板,例如:

<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>

请注意,这种方法会造成具有约束力的错误,因为选定项目没有找到相对来源。 备选方法见MarqueIV 页: 1

除了H.B. 回答外,可与Converter一道避免电离辐射。 The following example is based from the Solutions edited by the OP home.

The idea is very simple: bind to something that alway exists (Control) and do the relevant check inside the converter. The relevant part of the modified XAML is the following. Please note that Path=IsSelected was never really needed and ComboBoxItem is replaced with Control to avoid the binding errors.

<DataTrigger Binding="{Binding 
    RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Control}},
    Converter={StaticResource ComboBoxItemIsSelectedConverter}}"
    Value="{x:Null}">
  <Setter TargetName="theControl" Property="Template" Value="{StaticResource SimpleTemplate}" />
</DataTrigger>

C# Converter代码如下:

public class ComboBoxItemIsSelectedConverter : IValueConverter
{
    private static object _notNull = new object();
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        // value is ComboBox when the item is the one in the closed combo
        if (value is ComboBox) return null; 

        // all the other items inside the dropdown will go here
        return _notNull;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

我采用了下一个办法。

 <UserControl.Resources>
    <DataTemplate x:Key="SelectedItemTemplate" DataType="{x:Type statusBar:OffsetItem}">
        <TextBlock Text="{Binding Path=ShortName}" />
    </DataTemplate>
</UserControl.Resources>
<StackPanel Orientation="Horizontal">
    <ComboBox DisplayMemberPath="FullName"
              ItemsSource="{Binding Path=Offsets}"
              behaviors:SelectedItemTemplateBehavior.SelectedItemDataTemplate="{StaticResource SelectedItemTemplate}"
              SelectedItem="{Binding Path=Selected}" />
    <TextBlock Text="User Time" />
    <TextBlock Text="" />
</StackPanel>

行为

public static class SelectedItemTemplateBehavior
{
    public static readonly DependencyProperty SelectedItemDataTemplateProperty =
        DependencyProperty.RegisterAttached("SelectedItemDataTemplate", typeof(DataTemplate), typeof(SelectedItemTemplateBehavior), new PropertyMetadata(default(DataTemplate), PropertyChangedCallback));

    public static void SetSelectedItemDataTemplate(this UIElement element, DataTemplate value)
    {
        element.SetValue(SelectedItemDataTemplateProperty, value);
    }

    public static DataTemplate GetSelectedItemDataTemplate(this ComboBox element)
    {
        return (DataTemplate)element.GetValue(SelectedItemDataTemplateProperty);
    }

    private static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var uiElement = d as ComboBox;
        if (e.Property == SelectedItemDataTemplateProperty && uiElement != null)
        {
            uiElement.Loaded -= UiElementLoaded;
            UpdateSelectionTemplate(uiElement);
            uiElement.Loaded += UiElementLoaded;

        }
    }

    static void UiElementLoaded(object sender, RoutedEventArgs e)
    {
        UpdateSelectionTemplate((ComboBox)sender);
    }

    private static void UpdateSelectionTemplate(ComboBox uiElement)
    {
        var contentPresenter = GetChildOfType<ContentPresenter>(uiElement);
        if (contentPresenter == null)
            return;
        var template = uiElement.GetSelectedItemDataTemplate();
        contentPresenter.ContentTemplate = template;
    }


    public static T GetChildOfType<T>(DependencyObject depObj)
        where T : DependencyObject
    {
        if (depObj == null) return null;

        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
        {
            var child = VisualTreeHelper.GetChild(depObj, i);

            var result = (child as T) ?? GetChildOfType<T>(child);
            if (result != null) return result;
        }
        return null;
    }
}

worked like a charm. Don t like pretty much Loaded event here but you can fix it if you want

我提出这一解决办法,没有<代码>DataTemplateSelector,Trigger, binding, 或behavior

第一步是将<条码>ItemTemplate(部分内容)放在<条码>、ComboBox 资源和<条码>(正本项中正文)放在<条码>。 ComboBox.ItemsPanel resources and given both resources the same key.

第二步是,在实际<代码>中使用<代码>/Contentpresenter<>/code>和<代码>DynamicResource,从而将决议暂时搁置。 ComboBox.ItemTemplate 执行。

<ComboBox ItemsSource="{Binding Items, Mode=OneWay}">

    <ComboBox.Resources>
        <!-- Define ItemTemplate resource -->
        <DataTemplate x:Key="ItemTemplate" DataType="viewModel:ItemType">
            <TextBlock Text="{Binding FieldOne, Mode=OneWay}" />
        </DataTemplate>
    </ComboBox.Resources>

    <ComboBox.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Grid.IsSharedSizeScope="True"
                        IsItemsHost="True">
                <StackPanel.Resources>
                    <!-- Redefine ItemTemplate resource -->
                    <DataTemplate x:Key="ItemTemplate" DataType="viewModel:ItemType">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" SharedSizeGroup="GroupOne" />
                                <ColumnDefinition Width="10" SharedSizeGroup="GroupSpace" />
                                <ColumnDefinition Width="Auto" SharedSizeGroup="GroupTwo" />
                            </Grid.ColumnDefinitions>
                
                            <TextBlock Grid.Column="0" Text="{Binding FieldOne, Mode=OneWay}" />
                            <TextBlock Grid.Column="2" Text="{Binding FieldTwo, Mode=OneWay}" />
                        </Grid>
                    </DataTemplate>
                </StackPanel.Resources>
            </StackPanel>
        </ItemsPanelTemplate>
    </ComboBox.ItemsPanel>

    <ComboBox.ItemTemplate>
        <DataTemplate>
            <ContentPresenter ContentTemplate="{DynamicResource ItemTemplate}" />
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>




相关问题
WPF Datagrid, Setting the background of combox popup

I would like to change the color of the popup background when using a DatagridComboboxColumn in the WPF Toolkit datagrid. I ve edited the Template for a normal Combobox and it works great for selected ...

How to insert ComboBox item into ListBox? [winforms]

The question is very simple, How to insert ComboBox selected item into ListBox using c#? I have tried with this: listbox.Items.Add(combobox.SelectedItem); and some other permutations but it always ...

How do I bind a ComboBox to a one column list

I ve seen how to bind a ComboBox to a list that has columns like this: ItemsSource="{Binding Path=Entries}" DisplayMemberPath="Name" SelectedValuePath="Name" SelectedValue="{Binding Path=Entry}" But ...

Wpf Combobox Limit to List

We are using Wpf Combobox to allow the user to do the following things: 1) select items by typing in the first few characters 2) auto complete the entry by filtering the list 3) suggesting the first ...

热门标签