Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
我可以为WPF组合框中的选定项使用与下拉部分中的项不同的模板吗?_Wpf_Templates_Combobox - Fatal编程技术网

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

我可以为WPF组合框中的选定项使用与下拉部分中的项不同的模板吗?,wpf,templates,combobox,Wpf,Templates,Combobox,我有一个WPF组合框,里面装满了客户对象。我有一个数据模板: <DataTemplate DataType="{x:Type MyAssembly:Customer}"> <StackPanel> <TextBlock Text="{Binding Name}" /> <TextBlock Text="{Binding Address}" /> </StackPanel> </Dat

我有一个WPF组合框,里面装满了客户对象。我有一个数据模板:

<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 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的部分,不是真的)。

是的。您可以使用来确定在运行时绑定哪个模板。因此,如果IsSelected=False,则使用此模板;如果IsSelected=True,则使用此其他模板

值得注意的是: 一旦实现了模板选择器,您将需要提供模板键名。

简单解决方案:

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

请注意,此方法将导致绑定错误,因为找不到所选项的相对源。有关另一种方法,请参见。

我打算建议对组合项目使用ItemTemplate的组合,并将Text参数作为标题选择,但我发现ComboBox不尊重Text参数

我通过重写ComboBox控件模板来处理类似的问题。这是MSDN,其中有一个.NET4.0的示例

在我的解决方案中,我将组合框模板中的ContentPresenter更改为将其绑定到文本,并将其ContentTemplate绑定到一个简单的DataTemplate,该DataTemplate包含一个TextBlock,如下所示:

<DataTemplate x:Uid="DataTemplate_1" x:Key="ComboSelectionBoxTemplate">
    <TextBlock x:Uid="TextBlock_1" Text="{Binding}" />
</DataTemplate>

在ControlTemplate中使用此选项:

<ContentPresenter Name="ContentSite" IsHitTestVisible="False" Content="{TemplateBinding Text}" ContentTemplate="{StaticResource ComboSelectionBoxTemplate}" Margin="3,3,23,3" VerticalAlignment="Center" HorizontalAlignment="Left"/>

有了这个绑定链接,我可以通过控件上的文本参数直接控制组合选择显示(我将其绑定到ViewModel上的适当值)。

我使用了下一种方法

 <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;
    }
}
public静态类SelectedItemTemplateBehavior
{
公共静态只读从属属性SelectedItemDataTemplateProperty=
DependencyProperty.RegisterAttached(“SelectedItemDataTemplate”、typeof(DataTemplate)、typeof(SelectedItemTemplateBehavior)、new PropertyMetadata(default(DataTemplate)、PropertyChangedCallback));
公共静态void SetSelectedItemDataTemplate(此UIElement元素,DataTemplate值)
{
element.SetValue(SelectedItemDataTemplateProperty,value);
}
公共静态数据模板GetSelectedItemDataTemplate(此组合框元素)
{
return(DataTemplate)元素.GetValue(SelectedItemDataTemplateProperty);
}
私有静态无效属性ChangedCallback(DependencyObject d、DependencyPropertyChangedEventArgs e)
{
var uiElement=d作为组合框;
if(e.Property==SelectedItemDataTemplateProperty&&uiElement!=null)
{
uiElement.Loaded-=UiElementLoaded;
更新选择模板(uiElement);
uiElement.Loaded+=UiElementLoaded;
}
}
静态void UiElementLoaded(对象发送器、路由目标)
{
UpdateSelectTemplate((组合框)发送方);
}
私有静态void updateSelectTemplate(组合框uiElement)
{
var contentPresenter=GetChildOfType(uiElement);
if(contentPresenter==null)
返回;
var template=uiElement.GetSelectedItemDataTemplate();
contentPresenter.ContentTemplate=模板;
}
公共静态T GetChildOfType(DependencyObject depObj)
其中T:DependencyObject
{
if(depObj==null)返回null;
for(int i=0;i

工作得很有魅力。我不太喜欢这里加载的事件,但如果您愿意,可以修复它。

使用上面提到的DataTrigger/Binding解决方案有两个问题。第一个问题是,您实际上会得到一个绑定警告,即无法找到所选项目的相对源。然而,更大的问题是,您已经将数据模板弄得乱七八糟,并使它们特定于组合框

我提出的解决方案更好地遵循了WPF的设计,因为它使用了一个
DataTemplateSelector
,您可以使用它的
SelectedItemTemplate
DropDownItemsTemplate
属性以及这两个属性的“selector”变体来指定单独的模板

注意:针对C#9更新,启用了可空性,并在搜索过程中使用模式匹配

公共类ComboBoxTemplateSelector:DataTemplateSelector{
公共数据模板?选择编辑模板{get;set;}
公共数据模板选择器?SelectedItemTemplateSelector{get;set;}
公共数据模板?DropdownItemsTemplate{get;set;}
公共DataTemplateSelector?DropdownItemsTemplateSelector{get;set;}
公共覆盖数据模板SelectTemplate(对象项,DependencyObject容器){
var itemToCheck=容器;
//搜索可视化树,在组合框或
//ComboBoxItem(或null)。这将决定使用哪个模板
while(itemToCheck)不为空
A.
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;
    }
}
<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>