我可以为 WPF ComboBox 中的所选项目使用与下拉部分中的项目不同的模板吗?
我有一个 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:
<ComboBox ItemsSource="{Binding Customers}"
SelectedItem="{Binding SelectedCustomer}"
ItemTemplate="{StaticResource CustomerTemplate}" />
让它工作的重要部分是 Binding="{BindingrelativeSource={RelativeSourceMode=FindAncestor, AncestorType={x:Type ComboBoxItem}}, Path=IsSelected}" Value="{x:Null}"
(值应为 x:Null,而不是 True 的部分)。
I have a WPF Combobox which is filled with, say, Customer objects. I have a DataTemplate:
<DataTemplate DataType="{x:Type MyAssembly:Customer}">
<StackPanel>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Address}" />
</StackPanel>
</DataTemplate>
This way, when I open my ComboBox, I can see the different Customers with their Name and, below that, the Address.
But when I select a Customer, I only want to display the Name in the ComboBox. Something like:
<DataTemplate DataType="{x:Type MyAssembly:Customer}">
<StackPanel>
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
Can I select another Template for the selected item in a ComboBox?
Solution
With help from the answers, I solved it like this:
<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>
Then, my ComboBox:
<ComboBox ItemsSource="{Binding Customers}"
SelectedItem="{Binding SelectedCustomer}"
ItemTemplate="{StaticResource CustomerTemplate}" />
The important part to get it to work was Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBoxItem}}, Path=IsSelected}" Value="{x:Null}"
(the part where value should be x:Null, not True).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
使用上述 DataTrigger/Binding 解决方案的问题有两个。第一个是您实际上最终会收到一条绑定警告,提示您找不到所选项目的相对源。然而,更大的问题是您已经弄乱了数据模板并使它们特定于 ComboBox。
我提出的解决方案更好地遵循 WPF 设计,因为它使用
DataTemplateSelector
,您还可以在其中使用其SelectedItemTemplate
和DropDownItemsTemplate
属性指定单独的模板作为两者的“选择器”变体。注意:针对 C#9 进行了更新,启用了可空性并在搜索过程中使用模式匹配
为了使其更易于在 XAML 中使用,我还包含了一个标记扩展,它可以简单地创建并返回上述类它的
ProvideValue
函数。以下是如何使用它。漂亮、干净、清晰,您的模板保持“纯粹”
如果您愿意,您还可以使用 DataTemplateSelectors...
或者混合搭配!在这里,我对所选项目使用模板,但对 DropDown 项目使用模板选择器。
此外,如果您没有为所选或下拉项指定 Template 或 TemplateSelector,它只会像您所期望的那样,再次根据数据类型退回到数据模板的常规解析。因此,例如,在下面的情况下,所选项目显式设置了其模板,但下拉列表将继承适用于数据上下文中对象的数据类型的任何数据模板。
享受!
The issue with using the DataTrigger/Binding solution mentioned above are two-fold. The first is you actually end up with a binding warning that you can't find the relative source for the selected item. The bigger issue however is that you've cluttered up your data templates and made them specific to a ComboBox.
The solution I present better follows WPF designs in that it uses a
DataTemplateSelector
on which you can specify separate templates using itsSelectedItemTemplate
andDropDownItemsTemplate
properties as well as ‘selector’ variants for both.Note: Updated for C#9 with nullability enabled and using pattern matching during the search
To make it easier to use in XAML, I've also included a markup extension that simply creates and returns the above class in its
ProvideValue
function.And here's how you use it. Nice, clean and clear and your templates stay 'pure'
You can also use DataTemplateSelectors if you prefer...
Or mix and match! Here I'm using a template for the selected item, but a template selector for the DropDown items.
Additionally, if you don't specify a Template or a TemplateSelector for the selected or dropdown items, it simply falls back to the regular resolving of data templates based on data types, again, as you would expect. So, for instance, in the below case, the selected item has its template explicitly set, but the dropdown will inherit whichever data template applies for the DataType of the object in the data context.
Enjoy!
简单的解决方案:
(请注意,在框中而不是列表中选择并显示的元素不在
ComboBoxItem
内,因此会在Null
上触发)如果您想切换整个模板,您也可以使用触发器来执行此操作,例如 应用不同的
ContentTemplate
到ContentControl
。如果您只是更改此选择性情况的模板,这还允许您保留默认的基于DataType
的模板选择,例如:请注意,此方法将导致绑定错误,因为找不到对应的相对源所选项目。有关替代方法,请参阅 MarqueIV 的回答。
Simple solution:
(Note that the element that is selected and displayed in the box and not the list is not inside a
ComboBoxItem
hence the trigger onNull
)If you want to switch out the whole template you can do that as well by using the trigger to e.g. apply a different
ContentTemplate
to aContentControl
. This also allows you to retain a defaultDataType
-based template selection if you just change the template for this selective case, e.g.:Note that this method will cause binding errors as the relative source is not found for the selected item. For an alternate approach see MarqueIV's answer.
除了HB回答所说的之外,还可以使用转换器来避免绑定错误。以下示例基于 OP 本人编辑的解决方案。
这个想法非常简单:绑定到始终存在的东西(
Control
)并在转换器内进行相关检查。修改后的 XAML 的相关部分如下。请注意,从未真正需要
Path=IsSelected
,并且将ComboBoxItem
替换为Control
以避免绑定错误。C#转换器代码如下:
In addition to what said by H.B. answer, the Binding Error can be avoided with a Converter. The following example is based from the Solution edited by the OP himself.
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 andComboBoxItem
is replaced withControl
to avoid the binding errors.The C# Converter code is the following:
我本来建议对组合项使用 ItemTemplate 的组合,并使用 Text 参数作为标题选择,但我发现 ComboBox 不尊重 Text 参数。
我通过重写 ComboBox ControlTemplate 处理了类似的问题。下面是包含 .NET 4.0 示例的 MSDN 网站。
在我的解决方案中,我更改 ComboBox 模板中的 ContentPresenter 以使其绑定到 Text,其 ContentTemplate 绑定到包含 TextBlock 的简单 DataTemplate,如下所示:
在 ControlTemplate 中使用此内容:
通过此绑定链接,我能够控制组合选择直接通过控件上的 Text 参数显示(我将其绑定到 ViewModel 上的适当值)。
I was going to suggest using the combination of an ItemTemplate for the combo items, with the Text parameter as the title selection, but I see that ComboBox doesn't respect the Text parameter.
I dealt with something similar by overriding the ComboBox ControlTemplate. Here's the MSDN website with a sample for .NET 4.0.
In my solution, I change the ContentPresenter in the ComboBox template to have it bind to Text, with its ContentTemplate bound to a simple DataTemplate that contains a TextBlock like so:
with this in the ControlTemplate:
With this binding link, I am able to control the Combo selection display directly via the Text parameter on the control (which I bind to an appropriate value on my ViewModel).
我使用了下一种方法
,这种行为
就像魅力一样。不太喜欢这里的 Loaded 事件,但如果您愿意,您可以修复它
I used next approach
And the behavior
worked like a charm. Don't like pretty much Loaded event here but you can fix it if you want
是的。您使用模板选择器< /a> 确定在运行时绑定哪个模板。因此,如果 IsSelected = False 则使用此模板,如果 IsSelected = True,则使用此其他模板。
值得注意的是:
实现模板选择器后,您将需要提供模板键名。
Yes. You use a Template Selector to determine which template to bind at run-time. Thus if IsSelected = False then Use this template, if IsSelected = True, use this other template.
Of Note:
Once you implement your template selector, you will need to give the templates keynames.
我建议这个解决方案没有
DataTemplateSelector
、Trigger
、binding
和behavior
。第一步是将
ItemTemplate
(所选元素的)放入ComboBox
资源中,并将ItemTemplate
(下拉列表中项目的菜单)在ComboBox.ItemsPanel
资源中,并为这两个资源提供相同的键。第二步是通过在实际的
ComboBox.ItemTemplate 中使用
ContentPresenter
和DynamicResource
来推迟运行时的ItemTemplate
解析实施。I propose this solution without
DataTemplateSelector
,Trigger
,binding
norbehavior
.The first step is to put the
ItemTemplate
(of the selected element) in theComboBox
resources and theItemTemplate
(of the item in the drop down menu) in theComboBox.ItemsPanel
resources and give both resources the same key.The second step is to postpone the
ItemTemplate
resolution at run time by using both aContentPresenter
and aDynamicResource
in the actualComboBox.ItemTemplate
implementation.