I want to display a开发者_StackOverflow中文版 custom template/item as selected item in ComboBox (this item does not actually exist in the list of items and is updated differently). This does not even needs to be an item, just providing a custom view would work.
How can I do this while staying within current ComboBox theme (so no ControlTemplate replacement possible)? As far as I see, all of SelectionBox* properties are not editable and internally ComboBox uses unnamed ContentPresenter.
I would do it like this:
<Window.Resources>
<DataTemplate x:Key="NormalItemTemplate" ...>
...
</DataTemplate>
<DataTemplate x:Key="SelectionBoxTemplate" ...>
...
</DataTemplate>
<DataTemplate x:Key="CombinedTemplate">
<ContentPresenter x:Name="Presenter"
Content="{Binding}"
ContentTemplate="{StaticResource NormalItemTemplate}" />
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource FindAncestor,ComboBoxItem,1}}"
Value="{x:Null}">
<Setter TargetName="Presenter" Property="ContentTemplate"
Value="{StaticResource SelectionBoxTemplate}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Window.Resources>
...
<ComboBox
ItemTemplate="{StaticResource CombinedTemplate}"
ItemsSource="..."
... />
The reason this works is that CombinedTemplate
normally just uses NormalItemTemplate to present its data, but if there is no ComboBoxItem
ancestor it assumes it is in the selection box so it uses SelectionBoxTemplate.
Note that the three DataTemplates
could be included in any level of ResourceDictionary
(not just at the Window
level) or even directly within the ComboBox
, depending on your preference.
If I have this straight, you want a control that has something arbitrary displayed along with a drop-down button that displays a list of items with checkboxes next to them?
I wouldn't even bother trying to restyle a ComboBox
to achieve this. The problem is that ComboBox
is more specialized down a different path than what you need. If you look at the ComboBox ControlTemplate Example, you'll see that it simply uses a Popup
control to display the list of possible values.
You can take pieces of that template as guidance to creating a UserControl
that is easier to understand and better provides what you want. You'll even be able to add a SelectedItems
property and such that ComboBox
doesn't provide.
An example of what I mean by guidance: the Popup
has an IsOpen
property. In the control template, it's set to {TemplateBinding IsDropDownOpen}
, which means that the ComboBox
class has an IsDropDownOpen
property that is changed in order to control the expand/collapse of the Popup
.
Alexey Mitev's comment on Ray Burns' answer inspired me to write the following reasonably short utility class, which I now use in all my WPF projects:
public class ComboBoxItemTemplateSelector : DataTemplateSelector
{
public List<DataTemplate> SelectedItemTemplates { get; } = new List<DataTemplate>();
public List<DataTemplate> DropDownItemTemplates { get; } = new List<DataTemplate>();
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
return GetVisualParent<ComboBoxItem>(container) == null
? ChooseFrom(SelectedItemTemplates, item)
: ChooseFrom(DropDownItemTemplates, item);
}
private static DataTemplate ChooseFrom(IEnumerable<DataTemplate> templates, object item)
{
if (item == null)
return null;
var targetType = item.GetType();
return templates.FirstOrDefault(t => (t.DataType as Type) == targetType);
}
private static T GetVisualParent<T>(DependencyObject child) where T : Visual
{
while (child != null && !(child is T))
child = VisualTreeHelper.GetParent(child);
return child as T;
}
}
With that in the toolbox, it's possible to write XAML like this:
<UserControl.Resources>
<DataTemplate x:Key="SelectedItemTemplateForInt" DataType="{x:Type system:Int32}">
<!-- ... -->
</DataTemplate>
<DataTemplate x:Key="SelectedItemTemplateForDouble" DataType="{x:Type system:Double}">
<!-- ... -->
</DataTemplate>
<DataTemplate x:Key="DropDownItemTemplateForInt" DataType="{x:Type system:Int32}">
<!-- ... -->
</DataTemplate>
<DataTemplate x:Key="DropDownItemTemplateForDouble" DataType="{x:Type system:Double}">
<!-- ... -->
</DataTemplate>
</UserControl.Resources>
<ComboBox>
<ComboBox.ItemTemplateSelector>
<local:ComboBoxItemTemplateSelector>
<local:ComboBoxItemTemplateSelector.SelectedItemTemplates>
<StaticResource ResourceKey="SelectedItemTemplateForInt" />
<StaticResource ResourceKey="SelectedItemTemplateForDouble" />
</local:ComboBoxItemTemplateSelector.SelectedItemTemplates>
<local:ComboBoxItemTemplateSelector.DropDownItemTemplates>
<StaticResource ResourceKey="DropDownItemTemplateForInt" />
<StaticResource ResourceKey="DropDownItemTemplateForDouble" />
</local:ComboBoxItemTemplateSelector.DropDownItemTemplates>
</local:ComboBoxItemTemplateSelector>
</ComboBox.ItemTemplateSelector>
</ComboBox>
You need to look into Triggers and Styles. You might also want to look into some of my older questions here on StackOverflow that helped me conquer these problems:
- Displaying Content only when ListViewItem is Selected
- Using Styles in Windows Presentation Foundation
精彩评论