为了账号安全,请及时绑定邮箱和手机立即绑定

我可以为WPF组合框中的选定项目使用与下拉部分中的项目不同的模板吗?

我可以为WPF组合框中的选定项目使用与下拉部分中的项目不同的模板吗?

当年话下 2019-10-19 14:40:22
我有一个WPF组合框,里面装有客户对象。我有一个DataTemplate:<DataTemplate DataType="{x:Type MyAssembly:Customer}">    <StackPanel>        <TextBlock Text="{Binding Name}" />        <TextBlock Text="{Binding Address}" />    </StackPanel></DataTemplate>这样,当我打开组合框时,可以看到不同的客户及其名称,然后在其下方显示地址。但是,当我选择一个客户时,我只想在组合框中显示名称。就像是:<DataTemplate DataType="{x:Type MyAssembly:Customer}">    <StackPanel>        <TextBlock Text="{Binding Name}" />    </StackPanel></DataTemplate>我可以为组合框中的选定项目选择另一个模板吗?解在答案的帮助下,我这样解决了它:<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}" />使它起作用的重要部分是Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBoxItem}}, Path=IsSelected}" Value="{x:Null}"(值应为x:Null,而不是True的部分)。
查看完整描述

3 回答

?
神不在的星期二

TA贡献1963条经验 获得超6个赞

使用上述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); 

    }

}

注意:为简单起见,我的示例代码在此处使用新的“?”。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)

为了方便在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

        };

    }

}

这就是您的使用方式。干净,整洁,美观,模板保持“纯净”


注意:“是:”这是我在代码中放置类的位置的xmlns映射。确保导入您自己的名称空间,并根据需要更改“ is:”。


<ComboBox x:Name="MyComboBox"

    ItemsSource="{Binding Items}"

    ItemTemplateSelector="{is:ComboBoxTemplateSelector

        SelectedItemTemplate={StaticResource MySelectedItemTemplate},

        DropdownItemsTemplate={StaticResource MyDropDownItemTemplate}}" />

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


<ComboBox x:Name="MyComboBox"

    ItemsSource="{Binding Items}"

    ItemTemplateSelector="{is:ComboBoxTemplateSelector

        SelectedItemTemplateSelector={StaticResource MySelectedItemTemplateSelector},

        DropdownItemsTemplateSelector={StaticResource MyDropDownItemTemplateSelector}}" />

或混合搭配!在这里,我为所选项目使用模板,但为DropDown项目使用模板选择器。


<ComboBox x:Name="MyComboBox"

    ItemsSource="{Binding Items}"

    ItemTemplateSelector="{is:ComboBoxTemplateSelector

        SelectedItemTemplate={StaticResource MySelectedItemTemplate},

        DropdownItemsTemplateSelector={StaticResource MyDropDownItemTemplateSelector}}" />

另外,如果您没有为选定的项目或下拉菜单项指定Template或TemplateSelector,它又会像您期望的那样退回到基于数据类型的常规数据模板解析。因此,例如,在以下情况下,所选项目的显式设置已明确设置,但下拉菜单将继承适用于数据上下文中对象的DataType的任何数据模板。


<ComboBox x:Name="MyComboBox"

    ItemsSource="{Binding Items}"

    ItemTemplateSelector="{is:ComboBoxTemplateSelector

        SelectedItemTemplate={StaticResource MyTemplate} />

请享用!


查看完整回答
反对 回复 2019-10-19
?
拉莫斯之舞

TA贡献1820条经验 获得超10个赞

我用了下一种方法


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

    }

}

像魅力一样运作。这里不太喜欢Loaded事件,但是您可以根据需要进行修复


查看完整回答
反对 回复 2019-10-19
  • 3 回答
  • 0 关注
  • 530 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信