WPF: can't set ItemsPresenter to use a VirtualizingStackPanel - wpf

I've seen a few posts about this, including on StackOverflow - but I can't find an answer.
In WPF, I created a ControlTemplate for a ComboBox, simply by clicking "Edit Template -> Copy" in visual studio.
Now my problem is that the items in the dropdown are presented in an ItemsPresenter, and I simply can't figure out how to use a VirtualizingStackPanel inside that ItemsPresenter. I tried settings the ItemsPanel of the control in the control's style, but that doesn't help.
Here is the XAML of the ControlTemplate. It is pretty standard. The ItemsPresenter is called PART_ItemsPresenter.
<ControlTemplate x:Key="ComboBoxControlTemplate" TargetType="{x:Type ComboBox}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}"
Width="0" />
</Grid.ColumnDefinitions>
<Popup
x:Name="PART_Popup"
AllowsTransparency="True"
Grid.ColumnSpan="2"
IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}"
PopupAnimation="Fade"
Placement="Bottom">
<!--<themes:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=templateRoot}">-->
<Border
x:Name="DropDownBorder"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
MinWidth="{Binding ActualWidth, ElementName=templateRoot}">
<ScrollViewer
x:Name="DropDownScrollViewer">
<Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
<Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top"
Width="0">
<Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=DropDownBorder}"
Height="{Binding ActualHeight, ElementName=DropDownBorder}"
Width="{Binding ActualWidth, ElementName=DropDownBorder}" />
</Canvas>
<ItemsPresenter
x:Name="PART_ItemsPresenter"
KeyboardNavigation.DirectionalNavigation="Contained"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
</ItemsPresenter>
</Grid>
</ScrollViewer>
</Border>
<!--</themes:SystemDropShadowChrome>-->
</Popup>
<ToggleButton
x:Name="toggleButton"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Grid.ColumnSpan="2"
IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}">
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Focusable" Value="False" />
<Setter Property="ClickMode" Value="Press" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border x:Name="templateRoot" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{StaticResource ControlBackgroundBrush}"
SnapsToDevicePixels="True">
<Border x:Name="splitBorder" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="1" HorizontalAlignment="Right" Margin="0,-1,-1,-1"
Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}">
<Path
x:Name="Arrow"
Data="M0,0C0,0 3.5,4 3.5,4 3.5,4 7,0 7,0 7,0 0,0 0,0z"
Fill="{TemplateBinding BorderBrush}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" TargetName="templateRoot"
Value="{StaticResource ControlBackgroundMouseOverBrush}" />
<Setter Property="BorderBrush" TargetName="templateRoot"
Value="{StaticResource ControlBorderMouseOverBrush}" />
<Setter Property="BorderBrush" TargetName="splitBorder"
Value="{StaticResource ControlBorderMouseOverBrush}" />
<Setter Property="Fill" TargetName="Arrow"
Value="{StaticResource ControlBorderMouseOverBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" TargetName="templateRoot" Value="#FFC4E5F6" />
<Setter Property="BorderBrush" TargetName="templateRoot" Value="#FF2C628B" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" TargetName="templateRoot" Value="#FFF4F4F4" />
<Setter Property="BorderBrush" TargetName="templateRoot" Value="#FFADB2B5" />
<Setter Property="Background" TargetName="splitBorder" Value="#FFF4F4F4" />
<Setter Property="BorderBrush" TargetName="splitBorder" Value="#FFADB2B5" />
<Setter Property="Fill" TargetName="Arrow"
Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.Style>
</ToggleButton>
<Border x:Name="Border" Background="{StaticResource ControlBackgroundBrush}"
Margin="{TemplateBinding BorderThickness}">
<TextBox x:Name="PART_EditableTextBox"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
IsReadOnly="{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}"
Margin="{TemplateBinding Padding}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="AllowDrop" Value="True" />
<Setter Property="MinWidth" Value="0" />
<Setter Property="MinHeight" Value="0" />
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst" />
<Setter Property="Stylus.IsFlicksEnabled" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<ScrollViewer
x:Name="PART_ContentHost"
Template="{Binding PART_ItemsPresenter.Template}"
Background="Transparent"
Focusable="False"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TextBox.Style>
</TextBox>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" TargetName="Border" Value="0.56" />
</Trigger>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="Foreground" Value="Black" />
</Trigger>
<!--<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="True">
<Setter Property="Margin" TargetName="shadow" Value="0,0,5,5"/>
<Setter Property="Color" TargetName="shadow" Value="#71000000"/>
</Trigger>-->
<Trigger Property="HasItems" Value="False">
<Setter Property="Height" TargetName="DropDownBorder" Value="95" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsGrouping" Value="True" />
<Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="False" />
</MultiTrigger.Conditions>
<Setter Property="ScrollViewer.CanContentScroll" Value="False" />
</MultiTrigger>
<Trigger Property="CanContentScroll" SourceName="DropDownScrollViewer" Value="False">
<Setter Property="Canvas.Top" TargetName="OpaqueRect"
Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}" />
<Setter Property="Canvas.Left" TargetName="OpaqueRect"
Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>

This should be all you need in your ComboBox style:
<Setter Property="VirtualizingStackPanel.IsVirtualizing"
Value="True" />
<Setter Property="VirtualizingStackPanel.VirtualizationMode"
Value="Recycling" />
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel
IsItemsHost="True"
IsVirtualizing="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=(VirtualizingStackPanel.IsVirtualizing)}"
VirtualizationMode="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=(VirtualizingStackPanel.VirtualizationMode)}" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
Whether you use the Standard or Recycling virtualization mode is up to you. The Recycling mode tends to cause some odd artifacts on a ComboBox. For example: as you scroll, you may see more than one item highlighted with the 'selected' effect (but never more than one in view at a time).
However, there is a catch to making this work: you need to rename PART_ItemsPresenter to something else (I renamed it to ItemsPresenter). I have not been able to nail down why, but when you use the name PART_ItemsPresenter, your ItemsPanelTemplate doesn't get applied the virtualization is being negated somehow. I can see that the template is being applied, but there are clearly too many item containers being generated, which makes using a virtualizing panel rather pointless. Whatever the cause, simply giving your ItemsPresenter a different name appears to resolve the issue.
As #mm8 points out, you should also set MaxHeight="{TemplateBinding MaxDropDownHeight}" on DropDownBorder, or your popup will try to grow to accommodate all the items.
Lastly, I'm not sure what your intent was when you added the binding below, but you can't just add a named reference to the property path to set the binding source. Whatever your intent was, this binding is not going to satisfy it. I'd just get rid of it.
<ScrollViewer x:Name="PART_ContentHost"
Template="{Binding PART_ItemsPresenter.Template}"
... />

You need to do multiple things to support virtualization. Setting the ItemsPanelTemplate was part of the solution:
<Setter Property="VirtualizingPanel.IsVirtualizing" Value="True" />
<Setter Property="ScrollViewer.CanContentScroll" Value="True" />
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>

Setting the ItemsPanel to a VirtualizingStackPanel does work:
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
However, in order for the actual virtualization to work, you should also set the MaxHeight property of the DropDownBorder element in your template (or don't comment out the SystemDropShadowChrome element):
<Border x:Name="DropDownBorder" MaxHeight="{TemplateBinding MaxDropDownHeight}" ...

Related

Make listboxitem unselectable

I'm writing an small key application and I'm using a listbox to dynamically visualise a list from code. Now the issue is it needs to be used on windows 7 and I'm having multiple issues with the visualisation on that platform.
The code I have is:
<ListBox x:Name="listBoxSecrets" Margin="0,84,10,10" Background="{x:Null}"
HorizontalContentAlignment="Stretch" BorderBrush="{x:Null}" >
<ListBox.ItemTemplate>
<DataTemplate>
<!--<Border BorderBrush="#FFF3560D" CornerRadius="3" BorderThickness="3" Margin="0,0,0,10">-->
<Border BorderBrush="White" CornerRadius="10" BorderThickness="10" Margin="0,0,0,10">
<Grid Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="30" />
<ColumnDefinition Width="30" />
</Grid.ColumnDefinitions>
<StackPanel Uid="{Binding Path=ID}" Grid.Row="0" Grid.Column="0">
...
</StackPanel>
<Button ... Grid.Row="0" Grid.Column="2">
...
</Button>
<Button ... Grid.Row="0" Grid.Column="3" >
...
</Button>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
I tried the following style to no effect:
<Window.Resources>
<Style x:Key="{x:Type ListBox}" TargetType="{x:Type ListBox}">
<Setter Property="Focusable" Value="False" />
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
</Style>
</Window.Resources>
This is how the selection looks - I don't want it to be selectable. (I have the same issue with the buttons btw - same solution would be applicable?)
Before:
After click:
Also: Bonus points if you can help me figure out how to remove the "aliasing" line between the grid and the border around it.. ;-; Only happens on the win 7 mahcine, not my win 8 machine.
I guess you want to remove the selection state of ListBoxItem, if thats right, Please use the below style,
in this i have commented out the triggers responsible for selection and mouse over state.
You can assign this style in ItemContainerStyle of ListBox,,
<Style x:Key="FocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="Item.MouseOver.Background" Color="#1F26A0DA"/>
<SolidColorBrush x:Key="Item.MouseOver.Border" Color="#a826A0Da"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Background" Color="#3DDADADA"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Border" Color="#FFDADADA"/>
<SolidColorBrush x:Key="Item.SelectedActive.Background" Color="#3D26A0DA"/>
<SolidColorBrush x:Key="Item.SelectedActive.Border" Color="#FF26A0DA"/>
<Style x:Key="ListBoxItemStyle1" TargetType="{x:Type ListBoxItem}">
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Padding" Value="4,1"/>
<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<!--<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.MouseOver.Border}"/>
</MultiTrigger>-->
<!--<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="False"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Border}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Selector.IsSelectionActive" Value="True"/>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Border}"/>
</MultiTrigger>-->
<Trigger Property="IsEnabled" Value="False">
<Setter Property="TextElement.Foreground" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Why don't you remove the selection upon happening? Like this:
private void listBoxSecrets_SelectionChanged(object sender, EventArgs e)
{
listBoxSecrets.SelectedIndex = -1;
}
You then setup the listener like this:
<ListBox x:Name="listBoxSecrets" Margin="0,84,10,10" Background="{x:Null}"
HorizontalContentAlignment="Stretch" BorderBrush="{x:Null}" SelectionChanged="listBoxSecrets_SelectionChanged" >

WPF especific style on selected item according to the style of the item

I have a custom combobox where I apply a specific style on the first item. I want that when this item is selected, the style of displayed text in the combobox is the same. Currently, all selected items appear in the same style and I do not know how to make the ContentPresenter "get" the specified style in that item. This is the code:
<ComboBox HorizontalAlignment="Center" Margin="0,106,0,0" VerticalAlignment="Top" Width="200">
<ComboBoxItem Style="{StaticResource mySpecialStyle}">Select an option...</ComboBoxItem>
<ComboBoxItem>ComboBox Item #1</ComboBoxItem>
<ComboBoxItem>ComboBox Item #2</ComboBoxItem>
<ComboBoxItem>ComboBox Item #3</ComboBoxItem>
</ComboBox>
In fact, "mySpecialStyle" only changes color and fontstyle. But when the first item is selected, it appears like any other selected item. How can set this on the Contentpresenter?
Here is the complete code of the custom ComBoBox:
<Window.Resources>
<Style x:Key="mySpecialStyle" TargetType="{x:Type ComboBoxItem}">
<Setter Property="TextElement.FontStyle" Value="Italic"/>
</Style>
<ControlTemplate x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="20"/>
</Grid.ColumnDefinitions>
<Border
x:Name="Border"
Grid.ColumnSpan="3"
CornerRadius="3"
Background="#FFFAFAFA"
BorderBrush="#FF999999"
BorderThickness="1" />
<Border
x:Name="Background"
Grid.Column="0"
CornerRadius="3,0,0,3"
Margin="1"
Background="#FFFAFAFA"
BorderBrush="#FF999999"
BorderThickness="0" />
<Path
x:Name="ArrowDw"
Grid.Column="1"
Fill="#FF404040"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 0 0 L 4 4 L 8 0 Z"/>
<Path
x:Name="ArrowUp"
Grid.Column="1"
Fill="Transparent"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 0 2 L 4 -2 L 8 2 Z"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ToggleButton.IsMouseOver" Value="true">
<Setter TargetName="ArrowDw" Property="Fill" Value="#FF000000" />
</Trigger>
<Trigger Property="ToggleButton.IsChecked" Value="true">
<Setter TargetName="Background" Property="Background" Value="#FFFFFFFF" />
<Setter TargetName="Border" Property="BorderBrush" Value="#FF000000" />
<Setter TargetName="Border" Property="Background" Value="#FFFFFFFF" />
<Setter TargetName="ArrowDw" Property="Fill" Value="Transparent" />
<Setter TargetName="ArrowUp" Property="Fill" Value="#FF404040" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="ToggleButton.IsChecked" Value="True" />
<Condition Property="ToggleButton.IsMouseOver" Value="True" />
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter TargetName="ArrowUp" Property="Fill" Value="#FF000000" />
</MultiTrigger.Setters>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="#FFFFFF" />
<Setter TargetName="Border" Property="BorderBrush" Value="#CCCCCC" />
<Setter Property="Foreground" Value="#888888"/>
<Setter TargetName="ArrowDw" Property="Fill" Value="#999999" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="ComboBoxTextBox" TargetType="{x:Type TextBox}">
<Border x:Name="PART_ContentHost" Focusable="False" Background="{TemplateBinding Background}"/>
</ControlTemplate>
<Style x:Key="{x:Type ComboBox}" TargetType="{x:Type ComboBox}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="MinHeight" Value="27"/>
<Setter Property="ToolTip" Value="{Binding Path=SelectionBoxItem, RelativeSource={RelativeSource Self}}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid>
<ToggleButton
Name="ToggleButton"
Template="{StaticResource ComboBoxToggleButton}"
Grid.Column="2"
Focusable="false"
IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press">
</ToggleButton>
<ContentPresenter
Name="ContentSite"
IsHitTestVisible="False"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
????
Margin="8,3,28,3"
VerticalAlignment="Center"
HorizontalAlignment="Left" />
<TextBox x:Name="PART_EditableTextBox"
Style="{x:Null}"
Template="{StaticResource ComboBoxTextBox}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="8,3,28,3"
Focusable="True"
Background="Transparent"
Visibility="Hidden"
IsReadOnly="{TemplateBinding IsReadOnly}"/>
<Popup
Name="Popup"
Placement="Bottom"
IsOpen="{TemplateBinding IsDropDownOpen}"
AllowsTransparency="True"
Focusable="False"
PopupAnimation="Slide">
<Grid
Name="DropDown"
SnapsToDevicePixels="True"
MinWidth="{TemplateBinding ActualWidth}"
MaxHeight="{TemplateBinding MaxDropDownHeight}">
<Border
x:Name="DropDownBorder"
Background="#FFFFFF"
BorderThickness="1"
BorderBrush="#FF999999"/>
<ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True">
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained"/>
</ScrollViewer>
</Grid>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter TargetName="DropDownBorder" Property="MinHeight" Value="27"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="#888888"/>
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
<Trigger SourceName="Popup" Property="Popup.AllowsTransparency" Value="true">
<Setter TargetName="DropDownBorder" Property="CornerRadius" Value="4"/>
<Setter TargetName="DropDownBorder" Property="Margin" Value="0,2,0,0"/>
</Trigger>
<Trigger Property="IsEditable" Value="true">
<Setter Property="IsTabStop" Value="false"/>
<Setter TargetName="PART_EditableTextBox" Property="Visibility" Value="Visible"/>
<Setter TargetName="ContentSite" Property="Visibility" Value="Hidden"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
</Style.Triggers>
</Style>
</Window.Resources>
The placeholder "???" is where I need to set the style according to the style defined in the ComboBoxitem (in this case, "mySpecialStyle").
Thanks in advance!
The logic to solve here of course will base on something like a Trigger. However we can't set Style via Setter. So I thought of this hack (but safe enough). Firstly you need to bind the Style of the ContentPresenter to its Tag property (of course 2 way binding by default). Then you just need to change the Tag to a {StaticResource} with ResourceKey being the same as the resource you defined upwards on the tree. This resource should of course have TargetType of ContentPresenter:
<ContentPresenter
Name="ContentSite"
IsHitTestVisible="False"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
Style="{Binding Tag, RelativeSource={RelativeSource Self}}" />
<!-- the additional Trigger should be added to the ControlTemplate.Triggers -->
<Trigger Property="SelectedIndex" Value="0">
<Setter TargetName="ContentSite"
Property="Tag" Value="{StaticResource mySpecialStyle}"/>
</Trigger>

WPF - "g" is cut off sometimes

I have a combobox in which I display simple strings. At some point this looks like the following:
This is ok. now ein scroll down one step:
Now the "g" is cut off at the bottom. What I have tried so far: Set the height property explicit to Auto. Changing Padding and Margin. Nothing worked. Had some of you the same problem or any suggestions what I could try?
EDIT
Here the related XAML:
<Style x:Key="GraphicEditorInplaceLabelEditStyle">
<Setter Property="ComboBox.Focusable" Value="True"/>
<Setter Property="ComboBox.IsEditable" Value="True"/>
<Setter Property="ComboBox.IsReadOnly" Value="False"/>
<Setter Property="ComboBox.IsTextSearchEnabled" Value="True"/>
<!--Use Separators for empty entries-->
<Setter Property="ComboBox.ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ComboBoxItem}" BasedOn="{StaticResource {x:Type ComboBoxItem}}">
<Style.Triggers>
<DataTrigger Binding="{Binding}" Value="">
<Setter Property="IsEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBoxItem}">
<Separator HorizontalAlignment="Stretch"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Setter.Value>
</Setter>
<Setter Property="ComboBox.Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Border Validation.ValidationAdornerSiteFor="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType=ComboBox}}" Background="{StaticResource ComboBoxActiveBackgroundBrush}" BorderBrush="{StaticResource ComboBoxBorderBrush}" BorderThickness="1" CornerRadius="2">
<Grid>
<ContentPresenter Margin="3,3,3,3" Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" HorizontalAlignment="Left" IsHitTestVisible="False" Name="ContentSite" VerticalAlignment="Center" />
<TextBlock Style="{x:Null}" x:Name="PART_TextBackground" Margin="3,3,3,3" Background="Transparent" Focusable="False" FontStyle="Italic" Foreground="LightGray" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<TextBox Style="{x:Null}" x:Name="PART_EditableTextBox" Margin="3,3,3,3" Background="Transparent" Focusable="True" HorizontalAlignment="Left" IsReadOnly="{TemplateBinding IsReadOnly}" VerticalAlignment="Center" Visibility="Hidden">
<TextBox.Template>
<ControlTemplate TargetType="TextBox">
<Border x:Name="PART_ContentHost" Background="{TemplateBinding Background}" Focusable="False" />
</ControlTemplate>
</TextBox.Template>
</TextBox>
<Popup AllowsTransparency="True" Focusable="False" IsOpen="{TemplateBinding IsDropDownOpen}" Name="Popup" Placement="Bottom" PopupAnimation="Slide">
<Grid MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{TemplateBinding ActualWidth}" Name="DropDown" SnapsToDevicePixels="True">
<Border x:Name="DropDownBorder" Background="{StaticResource ComboBoxActiveBackgroundBrush}" BorderBrush="{StaticResource ComboBoxBorderBrush}" BorderThickness="1" />
<ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True" CanContentScroll="False">
<StackPanel KeyboardNavigation.DirectionalNavigation="Contained" IsItemsHost="True" />
</ScrollViewer>
</Grid>
</Popup>
</Grid>
</Border>
<ToggleButton Grid.Column="1" ClickMode="Press" Focusable="false" IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}" Name="ToggleButton" VerticalAlignment="Bottom">
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<Grid>
<Border x:Name="Border"
CornerRadius="2"
Background="{StaticResource ComboBoxActiveBackgroundBrush}"
BorderBrush="{StaticResource ComboBoxBorderBrush}"
BorderThickness="1"
Margin="0,0,0,0" />
<Rectangle x:Name="arrow"
Height="6"
Width="8"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="2"
Fill="{StaticResource ComboBoxArrowDownBrush}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ToggleButton.IsMouseOver"
Value="true">
<Setter TargetName="Border"
Property="Background"
Value="{StaticResource ComboBoxMouseOverBackgroundBrush}" />
<Setter TargetName="arrow"
Property="Fill"
Value="{StaticResource ComboBoxArrowDownMouseOverBrush}"></Setter>
</Trigger>
<Trigger Property="ToggleButton.IsChecked"
Value="true">
<Setter TargetName="Border"
Property="Background"
Value="{StaticResource ComboBoxPressedBackgroundBrush}" />
<Setter Property="Fill"
TargetName="arrow"
Value="{StaticResource ComboBoxArrowUpBrush}" />
</Trigger>
<Trigger Property="IsEnabled"
Value="false">
<Setter TargetName="Border"
Property="Background"
Value="{StaticResource ComboBoxInactiveBackgroundBrush}" />
<Setter Property="Foreground"
Value="{StaticResource FontInactiveBrush}" />
<Setter Property="Fill"
TargetName="arrow"
Value="{StaticResource ComboBoxArrowDownInactiveBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ToggleButton.Template>
<ToggleButton.RenderTransform>
<TranslateTransform X="-3" Y="0"></TranslateTransform>
</ToggleButton.RenderTransform>
</ToggleButton>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter Property="Visibility" TargetName="ToggleButton" Value="Collapsed" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{StaticResource FontInactiveBrush}" />
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false" />
</Trigger>
<Trigger Property="Popup.AllowsTransparency" SourceName="Popup" Value="true">
<Setter Property="CornerRadius" TargetName="DropDownBorder" Value="4" />
<Setter Property="Margin" TargetName="DropDownBorder" Value="0,2,0,0" />
</Trigger>
<Trigger Property="IsEditable" Value="true">
<Setter Property="IsTabStop" Value="false" />
<Setter Property="Visibility" TargetName="PART_EditableTextBox" Value="Visible" />
<Setter Property="Visibility" TargetName="ContentSite" Value="Hidden" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I tried to change the padding/margin of the ComboBoxItem as well as the item host stackpanel.

WPF - styling comboboxes

I'm trying to style Comboboxes in WPF so that they are white, and have the same border as TextBoxes. I have the following style so far, but don't know how to set the border:
<Style TargetType="ComboBox">
<Setter Property="Margin" Value="0,2,0,2" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Background" Value="White" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
???
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Use Expression Blend. If you want ComboBox to look similar to TextBox, have a look at TextBox template by right clicking TextBlock and selecting Edit Template > Edit a Copy and use that to modify your ComboBox template!
In between your controltemplate please implement something like this:
<Grid SnapsToDevicePixels="true">
<Border
x:Name="Bd"
Background="Transparent"
BorderBrush="#FF888888"
Padding="1"
CornerRadius="5" BorderThickness="2,2,2,2">
<Grid
Grid.IsSharedSizeScope="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto"
SharedSizeGroup="ComboBoxButton" />
</Grid.ColumnDefinitions>
<Border x:Name="SelectedItemBorder" Grid.ColumnSpan="2" />
<ContentPresenter
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Grid.Column="1"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
TextBlock.Foreground="White"
Height="Auto"
Margin="2,2,7,2"
VerticalAlignment="Center" />
<ToggleButton
Style="{StaticResource ComboBoxTransparentButtonStyle}"
Grid.ColumnSpan="3"
IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay,
RelativeSource={RelativeSource TemplatedParent}}"
Opacity="0.25" />
</Grid>
</Border>
<Popup Focusable="false" AllowsTransparency="true"
IsOpen="{Binding Path=IsDropDownOpen,
RelativeSource={RelativeSource TemplatedParent}}"
Placement="Bottom"
PopupAnimation="{DynamicResource
{x:Static SystemParameters.ComboBoxPopupAnimationKey}}"
x:Name="PART_Popup">
<Border CornerRadius="5" x:Name="DropDownBorder"
MaxHeight="{TemplateBinding MaxDropDownHeight}"
MinWidth="{TemplateBinding ActualWidth}"
Background="#FF7E7E7E"
BorderThickness="1">
<ScrollViewer Foreground="#FFFFFFFF">
<ItemsPresenter
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
KeyboardNavigation.DirectionalNavigation="Contained"
TextBlock.Foreground="White"
VerticalAlignment="Stretch" />
</ScrollViewer>
</Border>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" TargetName="Bd" Value="#FFFFFFFF"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" TargetName="Bd" Value="#FFD2ECCF"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelectionBoxHighlighted" Value="true" />
<Condition Property="IsDropDownOpen" Value="false" />
</MultiTrigger.Conditions>
<Setter Property="Foreground" Value="white" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</MultiTrigger>
<Trigger Property="IsSelectionBoxHighlighted" Value="true">
<Setter Property="Background" TargetName="SelectedItemBorder"
Value="Transparent" />
</Trigger>
<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
</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" TargetName="Bd"
Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false" />
</Trigger>
</ControlTemplate.Triggers>

ComboBox Style problems with DisplayMemberPath

I have a ComboBox and I have set the combo.ItemsSource property to a List object. The Book class contains two properties: "Abbreviation" and "Name".
I have set the ComboBox's DisplayMemberPath to "Abbreviation" but the following style that is set on the ComboBox does not display the Abbreviation property, but instead shows "Words.Book" which is the name of the class. The ComboBox drop-down displays a list correctly (Using the specified Abbreviation property). The problem is in the selected ComboBox item, the one displayed when the ComboBox' drop-down is closed.
I guess the problem is in ContentPresenter in DataTemplate but I was unable to find a successful solution. Currently the ContentPresenter's Content property is set to Content="{TemplateBinding Content} but I don't know if that's how it should be.
Any ideas how to show the property specified in DisplayMemberPath on the selected item?
Thank you
the code:
<ControlTemplate x:Key="ComboBoxToggleButton" TargetType="ToggleButton">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Border x:Name="Border" Grid.ColumnSpan="2" CornerRadius="2" BorderThickness="1" Background="{DynamicResource ribbonTitleFade}" />
<Border Grid.Column="0" CornerRadius="2,0,0,2" Margin="1,6,1,6" BorderBrush="{DynamicResource boSecE}" BorderThickness="0,0,1,0" />
<Path x:Name="Arrow" Grid.Column="1" Fill="White"
HorizontalAlignment="Center" VerticalAlignment="Center" Data="M 0 0 L 4 4 L 8 0 Z"
/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ToggleButton.IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background" Value="{DynamicResource ribbonTitleFade}" />
</Trigger>
<Trigger Property="ToggleButton.IsChecked" Value="true">
<Setter TargetName="Border" Property="Background" Value="{DynamicResource ribbonTitleFade}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="Black" />
<Setter TargetName="Border" Property="BorderBrush" Value="Black" />
<Setter Property="Foreground" Value="Gray"/>
<Setter TargetName="Arrow" Property="Fill" Value="Gray" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="comboBoxBorderTransparent" TargetType="Control">
<Setter Property="BorderBrush" Value="{DynamicResource boPrimC}" />
</Style>
<Style x:Key="comboItemStyle" TargetType="ComboBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBoxItem}">
<Border x:Name="backBorder" >
<StackPanel Orientation="Horizontal">
<Rectangle x:Name="rectA" Stroke="White" Width="4" Height="4" Fill="#80FFFFFF" Margin="5,0,0,0" HorizontalAlignment="Left" />
<TextBlock x:Name="text" Foreground="White" FontSize="10px">
<ContentPresenter Margin="8,1,0,1"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
/>
</TextBlock>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="ComboBoxItem.IsMouseOver" Value="true">
<Setter TargetName="rectA" Property="Stroke" Value="Black" />
<Setter TargetName="rectA" Property="Fill" Value="#80000000" />
<Setter TargetName="backBorder" Property="Background" Value="White"/>
<Setter TargetName="text" Property="Foreground" Value="{DynamicResource boPrimC}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate x:Key="comboSelectedItemTemplate">
<TextBlock Foreground="White" FontSize="10px">
<ContentPresenter Content="{TemplateBinding Content}" />
</TextBlock>
</DataTemplate>
<Style TargetType="ComboBox">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="MinWidth" Value="60"/>
<Setter Property="MinHeight" Value="20"/>
<Setter Property="ItemContainerStyle" Value="{DynamicResource comboItemStyle}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBox">
<Grid>
<ToggleButton
Name="ToggleButton" Grid.Column="2"
Template="{StaticResource ComboBoxToggleButton}"
Focusable="false"
IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press">
</ToggleButton>
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"
Name="ContentSite"
IsHitTestVisible="False"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{DynamicResource comboSelectedItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
Margin="3,3,23,3"
/>
<Popup Name="Popup" Placement="Bottom" IsOpen="{TemplateBinding IsDropDownOpen}"
AllowsTransparency="False" Focusable="False" PopupAnimation="Slide">
<Grid Name="DropDown" SnapsToDevicePixels="True"
MinWidth="{TemplateBinding ActualWidth}" MaxHeight="{TemplateBinding MaxDropDownHeight}">
<Border x:Name="DropDownBorder" Background="{DynamicResource ribbonTitleFade}"
BorderThickness="1" BorderBrush="{DynamicResource boPrimC}" />
<ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True">
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
</ScrollViewer>
</Grid>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter TargetName="DropDownBorder" Property="MinHeight" Value="95"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="Black"/>
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
<Trigger SourceName="Popup" Property="Popup.AllowsTransparency" Value="true">
<Setter TargetName="DropDownBorder" Property="CornerRadius" Value="2"/>
<Setter TargetName="DropDownBorder" Property="Margin" Value="0"/>
</Trigger>
<Trigger Property="IsEditable" Value="true">
<Setter Property="IsTabStop" Value="false"/>
<!--<Setter TargetName="ContentSite" Property="Visibility" Value="Hidden"/>-->
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
</Style.Triggers>
</Style>
I think you want something more like this in your ComboBox template:
<ContentPresenter Content="{TemplateBinding ComboBox.SelectionBoxItem}"
ContentTemplate="{TemplateBinding ComboBox.SelectionBoxItemTemplate}"
ContentStringFormat="{TemplateBinding ComboBox.SelectionBoxItemStringFormat}"
Margin="{TemplateBinding Control.Padding}"
HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
If your template doesn't explicitly handles the value of DisplayMemberPath, then the template won't use it to change the ComboBoxItem. You'll have to bind to the member directly in the ControlTemplate, or use a separate DataTemplate.
I found problem with the BureauBlack theme where it would just not apply the DisplayMemberPath, just showed the object name. I had a window with many (15+) ComboBoxes and all but 2 worked correctly using DisplayMemberPath.
I removed the following from the ContentPresenters and GridViewRowPresenters:
Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}"
in ListBoxItem, ComboBoxItem,ListViewItem. There may be more that need fixed, but this was all we found that we are using.
Using Microsoft.Net 4.0 Framework. I did not try on 4.5 or 3.5.

Resources