我可以使用WPFcombobox中的选定项目的不同模板比下拉部分中的项目?
我有一个WPFcombobox,里面充满了Customer对象。 我有一个DataTemplate:
<DataTemplate DataType="{x:Type MyAssembly:Customer}"> <StackPanel> <TextBlock Text="{Binding Name}" /> <TextBlock Text="{Binding Address}" /> </StackPanel> </DataTemplate>
这样,当我打开我的combobox,我可以看到不同的客户与他们的名字,并在下面,地址。
但是,当我select一个客户时,我只想在ComboBox中显示名称。 就像是:
<DataTemplate DataType="{x:Type MyAssembly:Customer}"> <StackPanel> <TextBlock Text="{Binding Name}" /> </StackPanel> </DataTemplate>
我可以为ComboBox中的选定项目select另一个模板吗?
解
在答案的帮助下,我解决了这个问题:
<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)。
上面提到的使用DataTrigger / Binding解决scheme的问题是双重的。 第一个是你实际上最终有一个绑定的警告,你无法find所选项目的相对来源。 然而更大的问题是,你已经混乱了你的数据模板,并使它们特定于一个combobox。
我提供的解决scheme更好地遵循WPFdevise,因为它使用DataTemplateSelector,您可以在其中使用SelectedItemTemplate和DropDownItemsTemplate属性以及两者的select器来指定单独的模板。
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 parent = container; // Search up the visual tree, stopping at either a ComboBox or // a ComboBoxItem (or null). This will determine which template to use while(parent != null && !(parent is ComboBoxItem) && !(parent is ComboBox)) parent = VisualTreeHelper.GetParent(parent); // If you stopped at a ComboBoxItem, you're in the dropdown var inDropDown = (parent 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中创build并返回上面的类。
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项目使用模板select器。
<ComboBox x:Name="MyComboBox" ItemsSource="{Binding Items}" ItemTemplateSelector="{is:ComboBoxTemplateSelector SelectedItemTemplate={StaticResource MySelectedItemTemplate}, DropdownItemsTemplateSelector={StaticResource MyDropDownItemTemplateSelector}}" />
另外,如果您没有为所选或下拉项目指定模板或模板select器,则会像您期望的那样简单地回退到基于数据types的数据模板的常规parsing。 因此,例如,在下面的情况下,所选项目的模板显式设置,但下拉将inheritance哪个数据模板适用于数据上下文中的对象的DataType。
<ComboBox x:Name="MyComboBox" ItemsSource="{Binding Items}" ItemTemplateSelector="{is:ComboBoxTemplateSelector SelectedItemTemplate={StaticResource MyTemplate} />
请享用!
简单的scheme:
<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>
(请注意,选中并显示在框中而不是列表中的元素不在ComboBoxItem
因此Null
上的触发器)
如果您想要切换出整个模板,您也可以使用触发器将不同的ContentTemplate
应用于ContentControl
。 这也可以让你保留一个默认的基于DataType
的模板select,如果你只是改变这个select性案例的模板,例如:
<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的答案 。
我打算build议使用组合项ItemTemplate的组合,Text参数作为标题select,但是我看到ComboBox不尊重Text参数。
我通过覆盖ComboBox ControlTemplate来处理类似的事情。 这是MSDN 网站的一个.NET 4.0示例。
在我的解决scheme中,我更改了ComboBox模板中的ContentPresenter以将其绑定到Text,并将其ContentTemplate绑定到包含TextBlock的简单DataTemplate,如下所示:
<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"/>
通过这个绑定链接,我可以直接通过控件上的Text参数来控制组合select显示(我将其绑定到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; } }
像魅力一样工作。 不要在这里喜欢很多的Loaded事件,但是如果你愿意,你可以修复它
是。 您可以使用模板select器来确定在运行时绑定哪个模板。 因此,如果IsSelected = False,那么使用这个模板,如果IsSelected = True,使用这个其他模板。
注意:一旦你实现你的模板select器,你将需要给模板的关键字。