我可以为WPF组合框中的选定项使用与下拉部分中的项不同的模板吗?
我有一个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
<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>