WPF Binding Style.Trigger To Parent - wpf

I'm trying to implement a Watermark solution for my ComboBoxes I found somewhere on the web (I can't find the page again) but have problems with the binding. The original solution had static text which I would like to replace using a binding to the ComboBoxes Tag property.
This is what I have so far:
<Grid>
<Grid.Resources>
<VisualBrush x:Key="Watermark" TileMode="None" Opacity="0.4" Stretch="None" AlignmentX="Left">
<VisualBrush.Visual>
<TextBlock FontStyle="Italic" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ComboBox}}, Path=Tag}"/>
</VisualBrush.Visual>
</VisualBrush>
<Style TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="Margin" Value="5"/>
<Setter Property="IsEditable" Value="False"/>
<Setter Property="IsReadOnly" Value="True"/>
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="Background" Value="{DynamicResource Watermark}"/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ComboBox Grid.Column="0" ItemsSource="{Binding Categories}" Tag="Categories"/>
<ComboBox Grid.Column="1" ItemsSource="{Binding SubCategories}" Tag="SubCategories"/>
<ComboBox Grid.Column="2" ItemsSource="{Binding Whatever}" Tag="Whatever"/>
Unfortunately it looks like the "FindAncestor" part is not working.
Can anyone tell me why?
Thanks in advance!

I'm afraid it's not that easy.
First, you cannot set the ComboxBox Background property like this. It becomes obvious, as soon as you try to replace the RelativeSource-Binding by some hardcoded text.
Second, you have to make sure that the VisualBrush ressource is part of the VisualTree of your ComboBox, not somewhere else (in the containing Grid, for example).
Both problems could be solved by retemplating the ComboBox. This can be done in Blend or Visual Studio by first creating a new template (as copy). Then you need to change it a bit.
There will be a Grid named "templateRoot". Add your watermark ressource:
<Grid x:Name="templateRoot" SnapsToDevicePixels="True">
<Grid.Resources>
<VisualBrush x:Key="Watermark" TileMode="None" Opacity="0.4" Stretch="None" AlignmentX="Left">
<VisualBrush.Visual>
<TextBlock FontStyle="Italic" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ComboBox}}, Path=Tag}"/>
</VisualBrush.Visual>
</VisualBrush>
</Grid.Resources>
<Grid.ColumnDefinitions>...
Then, at the end of the <ControlTemplate.Triggers> part you need to insert another MultiDataTrigger:
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Text, RelativeSource={RelativeSource FindAncestor, AncestorLevel=1, AncestorType={x:Type ComboBox}}}" Value="" />
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{DynamicResource Watermark}"/>
</MultiDataTrigger>
It must be a MultiTrigger condition, because otherwise it's not bindable.

Related

How to bind tooltip's template Width to TextBox Width?

My problem is I can't set the width of a TextBox's Tooltip. The tooltip should have the same width as its containing TextBox.
please consider this code :
<TextBox Text="{Binding PatientSubStay.Observations, Mode=OneWay}" ToolTip="{Binding}" x:Name="ObservationsTextBox" MaxLength="100">
<TextBox.Resources>
<Style TargetType="ToolTip">
<Setter Property="Template" Value="{StaticResource ObservationsToolTipControlTemplate}"/>
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TextBox}}, Path=Width}"/>
</Style>
</TextBox.Resources>
</TextBox>
The Tooltip is set by a template, and my attempt to modify its Width with a setter is a failure:
thank you.
EDIT:
here is the template (it's a preversion):
<ControlTemplate x:Key="ObservationsToolTipControlTemplate">
<Border Background="White" BorderBrush="Black" BorderThickness="1" Padding="2" CornerRadius="2" MinWidth="150">
<Border.Effect>
<DropShadowEffect BlurRadius="10" ShadowDepth="0"/>
</Border.Effect>
<Grid>
<Grid.ColumnDefinitions>
<!-- Width="{Binding Source={StaticResource TooltipHeaderColumnWidth}}" MaxWidth="{Binding Source={StaticResource TooltipHeaderColumnWidth}}" -->
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="20"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
</Grid>
</Border>
</ControlTemplate>
EDIT:
With the setter like the one of Peregrine's answer, I get this rendering:
I wonder why the widths are not equal.
for information, here is the code corresponding to the picture above:
<TextBox Text="{Binding PatientSubStay.Observations, Mode=OneWay}" ToolTip="{Binding}" x:Name="ObservationsTextBox" MaxLength="100">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Background" Value="{StaticResource FocusedItemSolidColorBrush}"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
<TextBox.Resources>
<Style TargetType="ToolTip">
<Setter Property="Template" Value="{StaticResource ObservationsToolTipControlTemplate}"/>
<Setter Property="Width" Value="{Binding PlacementTarget.ActualWidth, RelativeSource={RelativeSource Self}}"/>
<Setter Property="Placement" Value="Bottom"/>
</Style>
</TextBox.Resources>
</TextBox>
The issue is that Tooltips exist outside of the standard visual tree, and so can't reference another control by name or as a typed ancestor. The only thing that a tooltip knows about is its PlacementTarget - the control that it is tied to.
The following code demonstates how to bind to a property of the PlacementTarget
<TextBox Text="Some text for the text box" ToolTip="tool tip">
<TextBox.Resources>
<Style TargetType="{x:Type ToolTip}">
<Setter Property="Width" Value="{Binding PlacementTarget.ActualWidth, RelativeSource={RelativeSource Self}}"/>
</Style>
</TextBox.Resources>
</TextBox>
Note that you should use ActualWidth rather than Width to get the size of another control as Width may not actually be set to a value.
Try setting the ActualWidth of TextBox to the Tooltip. When you don't set the Width (
or Width=Auto) the value is double.NaN. The below code worked for me.
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TextBox}}, Path=ActualWidth}"/>

WPF Resource Styles not showing first time

When I bind my collection to the following window and usercontrol, the styles do not work.
When I press a button on the window, the styles kick in.
What is stopping my styles from firing at initial bind?
<Grid>
<ItemsControl Name="LbItems" ItemsSource="{Binding MyData}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type viewModel:SomeViewModel}">
<control:SomeView Margin="5" />
</DataTemplate>
<DataTemplate DataType="{x:Type viewModel:AnotherViewModel}">
<control:AnotherView Margin="5" />
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
</Grid>
And I have a user control as follows:
<UserControl.Resources>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={x:Static
RelativeSource.Self}, Path=DataContext.Selected}" Value="False">
<Setter Property="Foreground">
<Setter.Value>
Red
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={x:Static
RelativeSource.Self}, Path=DataContext.Selected}" Value="True">
<Setter Property="Foreground">
<Setter.Value>
DarkSeaGreen
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Command="{Binding SelectCommand}" Content="+" HorizontalAlignment=
"Left" VerticalAlignment="Top" Width="25" Grid.Column="0"/>
<TextBlock HorizontalAlignment="Left" Margin="5" TextWrapping="Wrap" Text=
"{Binding Endorsement.Name}" VerticalAlignment="Top" Grid.Column="1" />
<Button Command="{Binding DeselectCommand}" Content="-" HorizontalAlignment=
"Right" VerticalAlignment="Top" Width="25" Grid.Column="2"/>
</Grid>
When the page loads and when the 'Selected' is set is two different times; hence selected is null when the page loads and nothing happens. Anticipate the null situation such as an added style
<DataTrigger Binding="{Binding RelativeSource={x:Static
RelativeSource.Self}, Path=DataContext.Selected}" Value="{x:Null}">
...
You shouldn't have to do this in your DataTrigger binding:
Binding="{Binding RelativeSource={x:Static
RelativeSource.Self}, Path=DataContext.Selected}"
A binding by default is referencing the DataContext so the equivalent simpler form is this:
Binding="{Binding Path=Selected}"
I don't think that will solve your problem though (but if it does that's great). One way around it is to define a default value in the style for your Foreground if neither trigger fires:
<UserControl.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Red" />
<Style.Triggers>
...
This assumes that all your items are deselected upon load. Hope this helps.
Cheers,
Eric

DataTemplateTrigger not working with datatemplateselector

I have defined a datatemplate that has a trigger that should simply change the background color if the item is selected. For some reason it doesn't seem to be working.
<DataTemplate x:Key="existingDeviceTemplate" >
<StackPanel Orientation="Horizontal">
<Border Name="bd" Background="Green" BorderThickness="1" Padding="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Path=Name}" Width="200"/>
<Button Grid.Column="1" Content="Settings" Click="cmdSettings_Clicked"/>
<Button Grid.Column="2" Content="Delete" Click="cmdDelete_Clicked"/>
</Grid>
</Border>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="true" >
<Setter TargetName="bd" Property="Background" Value="Red"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Try to bind to the IsSelected property of the ListBoxItem:
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" Value="true">
<Setter TargetName="bd" Property="Background" Value="Red" />
</DataTrigger>
</DataTemplate.Triggers>
If your list control is a ListView you have to replace x:Type ListBoxItem with x:Type ListViewItem.
Does your binded model had a property named IsSelected?
my guess is that you mean the rows' IsSelected property.
If thats the case you need to put a RelativeSource binding with FindAncestor to the ListItem

WPF Style Triggers in DataTemplate

i hope you can help me. I got following Code in the Resources:
<UserControl.Resources>
<BitmapImage x:Key="img_src_lock" UriSource="/EEBase;component/Images/Lock_24x32.png" />
<BitmapImage x:Key="img_src_unlock" UriSource="/EEBase;component/Images/Unlock_24x32.png" />
<Style TargetType="{x:Type ToggleButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}" Value="True">
<Setter Property="Content">
<Setter.Value>
<Image Source="{StaticResource img_src_lock}" />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}" Value="False">
<Setter Property="Content">
<Setter.Value>
<Image Source="{StaticResource img_src_unlock}" />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
<!-- TypeComboTemplateCollapsed -->
<DataTemplate x:Key="TypeComboTemplateCollapsed">
<TextBlock
Text="{Binding Path=Text, Mode=OneWay}"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="5,0,0,5"
/>
</DataTemplate>
<!-- TypeComboTemplateExpanded -->
<DataTemplate x:Key="TypeComboTemplateExpanded">
<TextBlock
Text="{Binding Path=Text, Mode=OneWay}"
VerticalAlignment="Center"
Margin="5,0,0,5"
/>
</DataTemplate>
<!-- EditCircleTemplate -->
<DataTemplate x:Key="EditCircleTemplate">
<!-- some content here, no ToggleButton -->
</DataTemplate>
<!-- EditRectangleTemplate -->
<DataTemplate x:Key="EditRectangleTemplate">
<!-- some other content here, including the ToggleButtons -->
<ToggleButton
IsChecked="{Binding Path=BaseLocked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
Margin="5"
/>
<ToggleButton
IsChecked="{Binding Path=HeightLocked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
Margin="5"
/>
</DataTemplate>
</UserControl.Resources>
To me that looks all correct.
Now, the problem is:
When i do the following, an exceptions occurs:
Specified element is already the logical child of another element. Disconnect it first.
1. load the control, selected type is CIRC
2. change the dropdown to select RECT (template triggers and togglebuttons are shown correctly)
3. change the dropdown back to CIRC
--> now the Exception occurs.
4. if i ignore the exception, the template "EditCircleTemplate" does not get loaded, and the normal ToString of the model object gets displayed.
Additional info:
originally there are 4 different types in the WPF, two of them with ToggleButtons (that's why i use templates). I cut them out, they dont differ really. But what i found out using all 4 templates is that the error does not occur when switching to a new Template, but when unloading a template with the toggle buttons. Which is kinda strange.
Also if i remove the DataTriggers for the ToggleButtons everything works like a charm.
The Exception comes from the XAML-Interpreter, so the Stacktrace is not useful at all.
Can anyone give me a hint what i am doing wrong?
Edit:
oops, i guess i forgot the content code:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition />
</Grid.RowDefinitions>
<ComboBox
Margin="5"
Grid.Row="0"
Grid.Column="0"
ItemsSource="{Binding Path=PossibleTypes, Mode=OneTime}"
SelectedItem="{Binding Path=SelectedType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<ContentControl x:Name="content" Content="{Binding}" ContentTemplate="{StaticResource TypeComboTemplateExpanded}"/>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ComboBoxItem}}" Value="{x:Null}">
<Setter TargetName="content" Property="ContentTemplate" Value="{StaticResource TypeComboTemplateCollapsed}"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ContentControl
Grid.Column="0"
Grid.Row="1"
Grid.ColumnSpan="2"
Content="{Binding Mode=OneWay}">
<ContentControl.ContentTemplate>
<DataTemplate >
<ContentControl
Name="inputContent"
Content="{Binding Mode=OneWay}"
ContentTemplate="{x:Null}"
/>
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding Path=SelectedType.Type, Mode=OneWay}"
Value="CIRC">
<Setter
TargetName="inputContent"
Property="ContentTemplate"
Value="{StaticResource EditCircleTemplate}"
/>
</DataTrigger>
<DataTrigger
Binding="{Binding Path=SelectedType.Type, Mode=OneWay}"
Value="RECT">
<Setter
TargetName="inputContent"
Property="ContentTemplate"
Value="{StaticResource EditRectangleTemplate}"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
Edit2/Solution:
I just found a workaround - it just does not satisfy me:
Instead of putting the style in the UserControl.Resources, which for me would be the more clean and intuitive solution, i have to set the style with the triggers on each ToggleButton separately.
So removing the and adding following code to each ToggleButton did the trick:
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}" Value="True" >
<Setter Property="Content">
<Setter.Value>
<Image Source="{StaticResource img_src_lock}" />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}" Value="False">
<Setter Property="Content">
<Setter.Value>
<Image Source="{StaticResource img_src_unlock}" />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
The big question still persists: WHY?
The problem is that if you create an Image in a style there is only one instance, so as soon as multiple controls use the style there is going to be a fight over this one instance which can only be owned by one control.
The easiest solution to this is placing the Style in a resource and setting x:Shared to false, that way a copy of the style is used whereever referenced.
Why do you need to create BitmapImages and set them as Source to your Content Images in Triggers? Why arent you using the URI source to Images directly?
<Style TargetType="{x:Type ToggleButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}" Value="True">
<Setter Property="Content">
<Setter.Value>
<Image Source="/EEBase;component/Images/Lock_24x32.png" />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked}" Value="False">
<Setter Property="Content">
<Setter.Value>
<Image Source="/EEBase;component/Images/Unlock_24x32.png" />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
Let me know if this helps.

Multiple item combo box with headers?

Is it possible to have "column headers" on a combo box bound to multiple items? For example a combo box that displays a persons name. The combo box would display John Doe. But I'd like to display column headers:
First Last
John Doe
Jane Doe
Jimmy Doe
Is this possible without the use of a data grid?
What about a simple solution that includes the use of a data grid? I found one solution for embedding a data grid into a combo box but it looks difficult and requires MS Blend.
I'd be happy if I could just get some headers as the first row in the drop down.
G
Here is my xaml code with HB's attempt that produces a compile error as mentioned in the comments.
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dg="http://schemas.microsoft.com/wpf/2008/toolkit"
<ComboBox Name="cboPlaceNames" Grid.IsSharedSizeScope="True" ItemsSource="{DynamicResource items}" Height="22" Width="285" Margin="0,6,165,0" SelectedIndex="0" HorizontalAlignment="Right" VerticalAlignment="Top" SelectionChanged="cboPlaceNames_SelectionChanged">
<ComboBox.Resources>
<CompositeCollection x:Key="items">
<ComboBoxItem IsEnabled="False">
<Grid TextElement.FontWeight="Bold">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="A"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition SharedSizeGroup="B"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition SharedSizeGroup="C"/>
</Grid.ColumnDefinitions>
<Grid.Children>
<TextBlock Grid.Column="0" Text="Name"/>
<TextBlock Grid.Column="2" Text="CLLI"/>
<TextBlock Grid.Column="4" Text="Street"/>
</Grid.Children>
</Grid>
</ComboBoxItem>
<Separator/>
<CollectionContainer Collection="{Binding Source={x:Reference cboPlaceNames}, Path=DataContext.Data}"/>
</CompositeCollection>
<DataTemplate DataType="x:Type obj:PlaceName">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="A"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition SharedSizeGroup="B"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition SharedSizeGroup="C"/>
</Grid.ColumnDefinitions>
<Grid.Children>
<TextBlock Grid.Column="0" Text="{Binding Name}"/>
<TextBlock Grid.Column="2" Text="{Binding CLLI}"/>
<TextBlock Grid.Column="4" Text="{Binding Street}"/>
</Grid.Children>
</Grid>
</DataTemplate>
</ComboBox.Resources>
</ComboBox>
Example:
<ComboBox Name="cb" Grid.IsSharedSizeScope="True" ItemsSource="{DynamicResource items}">
<ComboBox.Resources>
<CompositeCollection x:Key="items">
<ComboBoxItem IsEnabled="False">
<Grid TextElement.FontWeight="Bold">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="A"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition SharedSizeGroup="B"/>
</Grid.ColumnDefinitions>
<Grid.Children>
<TextBlock Grid.Column="0" Text="Name"/>
<TextBlock Grid.Column="2" Text="Occupation"/>
</Grid.Children>
</Grid>
</ComboBoxItem>
<Separator/>
<CollectionContainer Collection="{Binding Source={x:Reference cb}, Path=DataContext.Data}"/>
</CompositeCollection>
<DataTemplate DataType="{x:Type obj:Employee}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="A"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition SharedSizeGroup="B"/>
</Grid.ColumnDefinitions>
<Grid.Children>
<TextBlock Grid.Column="0" Text="{Binding Name}"/>
<TextBlock Grid.Column="2" Text="{Binding Occupation}"/>
</Grid.Children>
</Grid>
</DataTemplate>
</ComboBox.Resources>
</ComboBox>
Note that getting the Collection-binding right is not that easy because there is neither DataContext nor VisualTree to rely on, ElementName and RelativeSource does not work, this is because CompositeCollection is just a collection, not a FrameworkElement.
Other than that the way this is done is via Grids that have shared size columns. The DataTemplate is applied automatically via the DataType.
Edit: Setting the header-ComboBoxItem's IsHitTestVisible property to False is not enough since it still can be selected using the keyboard. I now changed it to IsEnabled="False" which fades out the item a bit. You could probably re-template that item to not do that. Or if you find another way of disabling it from selection that would of course work out too.
The simplest way to add columns headers to combobox is to use listview in combobox.
The following code is give the solution to it.
<ComboBox HorizontalAlignment="Center"
IsTextSearchEnabled="False" Width="200"
IsEditable="True" Text="{Binding }">
<ListView ItemsSource="{Binding YOURITEMSOURCE}"
SelectedItem="{Binding Path=SELECTEDITEMSOURCE}"
Height="200" ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListView.View>
<GridView>
<GridViewColumn Width="130" Header="Name" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Width="130" Header="Occupation" DisplayMemberBinding="{Binding Occupation}" />
<GridViewColumn Width="130" Header="Age" DisplayMemberBinding="{Binding Age}" />
<GridViewColumn Width="130" Header="Salary" DisplayMemberBinding="{Binding Salary}" />
</GridView>
</ListView.View>
</ListView>
</ComboBox>
I liked Nandha's answer because this was what I was trying to achieve. However, the text field of the combo box will not work correctly.
Since I am lazy, to get around this I created a StackPanel of Horizontal orientation, with a TextBox bound to a field from the selected item, and the Combo Box with the embedded ListView. The Combo Box now has a width of 20 so just the down arrow shows (you can play with the width and margins to get just the right look. This makes the text box look like a combo box, with all the benefits of the ListView, and much less coding.
I hope this helps.enter image description here
Apply the following style to the ComboBox.
<Style x:Key="ListViewComboBox" TargetType="{x:Type ComboBox}">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}"
Color="LightBlue"/>
</Style.Resources>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FontFamily" Value="Arial"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="3"
SnapsToDevicePixels="True">
<Grid>
<Border x:Name="Border">
<Popup x:Name="PART_Popup" AllowsTransparency="true" IsOpen="{TemplateBinding IsDropDownOpen}" Placement="Bottom" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Focusable="False">
<Border x:Name="Shdw"
MaxHeight="{TemplateBinding MaxDropDownHeight}"
MinWidth="{Binding ActualWidth, ElementName=Border}">
<Border x:Name="DropDownBorder"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="1">
<ListView KeyboardNavigation.DirectionalNavigation="Contained"
ItemsSource="{TemplateBinding ItemsSource}"
SelectedItem="{Binding Mode=TwoWay, Path=SelectedItem, RelativeSource={RelativeSource Mode=TemplatedParent}}"
View="{TemplateBinding Tag}"/>
</Border>
</Border>
</Popup>
</Border>
<DockPanel Margin="2">
<FrameworkElement DockPanel.Dock="Right"
Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}"/>
<Border x:Name="SelectedItemBorder" Margin="{TemplateBinding Padding}">
<Grid>
<ContentPresenter Content="{TemplateBinding SelectionBoxItem}"
ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="1,1,1,1"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
</Border>
</DockPanel>
<ToggleButton x:Name="DropDownToggleButton"
ClickMode="Press"
Focusable="false"
Foreground="{TemplateBinding BorderBrush}"
IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
Margin="2"
MinHeight="0"
MinWidth="0"
Width="Auto"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelectionBoxHighlighted" Value="true"/>
<Condition Property="IsDropDownOpen" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
</MultiTrigger>
<Trigger Property="IsSelectionBoxHighlighted" Value="true">
<Setter Property="Background" TargetName="SelectedItemBorder" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="MinHeight" TargetName="DropDownBorder" Value="95"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
<Setter Property="Margin" TargetName="Shdw" Value="0,0,5,5"/>
</Trigger>
<Trigger Property="IsReadOnly" Value="True">
<Setter Property="Visibility" TargetName="DropDownToggleButton" Value="Collapsed"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsEditable" Value="true">
<Setter Property="IsTabStop" Value="false"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template" Value="{StaticResource ComboBoxEditableTemplate}"/>
</Trigger>
</Style.Triggers>
</Style>
Apply the View you want to the Tag property of the ComboBox
<ComboBox ItemsSource={Binding Items}>
<ComboBox.ItemTemplate>
<DataTemplate>
<!-- Enter your item template shown as the selected item. -->
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.Tag>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding Name}"
Header="Name"
Width="100"/>
</GridView>
</ComboBox.Tag>
</ComboBox>
Thats all folks.
I like H.B.'s answer, but unfortunately when I use it I see databinding errors in the output for the header ComboBoxItem's HorizontalContentAlignment and VerticalContentAlignment properties:
Cannot find source for binding with reference 'RelativeSource
FindAncestor, AncestorType='System.Windows.Controls.ItemsControl',
AncestorLevel='1''. BindingExpression:Path=HorizontalContentAlignment;
DataItem=null; target element is 'ComboBoxItem' (Name=''); target
property is 'HorizontalContentAlignment' (type 'HorizontalAlignment')
These don't crash the program, but they do clutter up the output and cause perceptible delays when running debug builds. Whatever's causing them seems to be deep in the bowels of ComboBox or ComboBoxItem; in any case, I couldn't figure out any way to prevent them (setting these properties manually or via a Style didn't help). So I ended up going with a slight variation. This is longer and hackier than I like things to be, but it gets the job done:
<ComboBox Name="cb" Grid.IsSharedSizeScope="True" ItemsSource="{DynamicResource items}">
<ComboBox.Resources>
<!-- We'll use this dummy value to represent the header row. -->
<!-- The type and value are arbitrary; we just need a unique type -->
<!-- for DataTemplate selection to work with. -->
<system:Int32 x:Key="HeaderPlaceholder">-1</system:Int32>
<CompositeCollection x:Key="items">
<StaticResource ResourceKey="HeaderPlaceholder" />
<CollectionContainer Collection="{Binding Source={x:Reference cb},
Path=DataContext.Data}"/>
</CompositeCollection>
<!-- DataTemplate for the header item -->
<DataTemplate DataType="{x:Type system:Int32}">
<DataTemplate.Resources>
<!-- Make the TextBlocks black even though they are disabled -->
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="Black" />
</Trigger>
</Style.Triggers>
</Style>
</DataTemplate.Resources>
<StackPanel>
<Grid TextElement.FontWeight="Bold">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="A"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition SharedSizeGroup="B"/>
</Grid.ColumnDefinitions>
<Grid.Children>
<TextBlock Grid.Column="0" Text="Name"/>
<TextBlock Grid.Column="2" Text="Occupation"/>
</Grid.Children>
</Grid>
<Separator />
</StackPanel>
</DataTemplate>
<!-- DataTemplate for a normal, selectable item -->
<DataTemplate DataType="{x:Type obj:Employee}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="A"/>
<ColumnDefinition Width="5"/>
<ColumnDefinition SharedSizeGroup="B"/>
</Grid.ColumnDefinitions>
<Grid.Children>
<TextBlock Grid.Column="0" Text="{Binding Name}"/>
<TextBlock Grid.Column="2" Text="{Binding Occupation}"/>
</Grid.Children>
</Grid>
</DataTemplate>
</ComboBox.Resources>
<ComboBox.ItemContainerStyle>
<!-- Make sure the header item is disabled so it can't be selected -->
<Style TargetType="ComboBoxItem">
<Style.Triggers>
<Trigger Property="DataContext" Value="{StaticResource HeaderPlaceholder}">
<Setter Property="IsEnabled" Value="False" />
</Trigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>

Resources