How can I pass a parameter to a DataTrigger? - wpf

I have a list of things that I'm displaying via ItemsControl where each Item is basically a card that can be clicked. Is there a way I can pass a parameter to a DataTrigger to show whether or not a card has been clicked and if it is clicked set the Background to another color?

[...] show whether or not a card has been clicked and if it is clicked [...]
You created a view of cards and want them to be selectable. An ItemsControl is does not support selection, but there is a control called Selector that derives from ItemsControl, which is the abstract base type for items controls that need selection. Its derivatives include ListBox and ListView, which come with selection and highlighting out-of-the-box. In other words, do not re-invent the wheel, there are already more suitable controls that meet your requirements.
Types derived from Selector contain dependency properties for SelectedIndex, SelectedItem or SelectedValue, which makes it easy for you to bind them and create triggers. There is also an attached property IsSelected for item containers, which is exactly what you need to change the Background or any other property depending on the clicked or selected item.
In the following I will show you how to customize the appearance of ListBox items. You can do the same with a ListView. You can extract the default style and template for a ListBoxItem using Blend or Visual Studio.
As you can see below there are a few brushes, a focus visual and the style with control template and triggers. Adapt this style to meet your desired design. Look for the triggers that bind the IsSelected property.
<SolidColorBrush x:Key="Item.MouseOver.Background" Color="#1F26A0DA"/>
<SolidColorBrush x:Key="Item.MouseOver.Border" Color="#a826A0Da"/>
<SolidColorBrush x:Key="Item.SelectedActive.Background" Color="#3D26A0DA"/>
<SolidColorBrush x:Key="Item.SelectedActive.Border" Color="#FF26A0DA"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Background" Color="#3DDADADA"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Border" Color="#FFDADADA"/>
<Style x:Key="FocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" StrokeDashArray="1 2" SnapsToDevicePixels="true" StrokeThickness="1" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ListBoxItemContainerStyle" 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" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" 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>
Move these resources to a resource dictionary that is in scope of the controls where you want to use them, or simply copy them to the application resources to make them globally available. In order to apply the style, you have two options.
Use the x:Key and reference them as ItemContainerStyle in each ListBox.
<ListBox ItemContainerStyle="{DynamicResource ListBoxItemContainerStyle}" .../>
Make the style implicit by removing the x:Key. Then it will be applied to all ListBoxItem in scope of the resource dictionary that contains it.
<Style TargetType="{x:Type ListBoxItem}">

Related

ListView Selected Item Style Override

I have a WPF ListView that I'm trying to apply a custom style too... primarily I want to make the background of the listview box transparent with no border... and I want to override the highlighted and selected item styles. The default highlight is a semi-transparent blue and the selected item is grey. I'd like to override these in order to customize them. I've followed several threads and tutorials... including this one but, my highlight and selected items remain in the default style. Here's what I have.
<UserControl.Resources>
<ResourceDictionary>
<Style x:Key="ListViewStyle" TargetType="ListView">
<Setter Property="Background" Value="Transparent"></Setter>
<Setter Property="BorderThickness" Value="0"></Setter>
<Setter Property="BorderBrush" Value="Transparent"></Setter>
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Red" />
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Green" />
</Style.Resources>
</Style>
</ResourceDictionary>
</UserControl.Resources>
....
<ListView Style="{StaticResource ListViewStyle}" ItemsSource="{Binding Path=Items}" ScrollViewer.VerticalScrollBarVisibility="Disabled" SelectedItem="{Binding Path=DataContext.Current" >
<ListView.ItemTemplate>
<DataTemplate DataType="local:Cases">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
<!-- Some UI Controls Here -->
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This approach of trying to override the system colours doesn't work on Windows 8 and later.
You need to modify the ControlTemplate of the ListViewItem container.
You can copy the default template into your XAML markup by right-clicking on the ListView in design mode in Visual Studio or in Blend and choose Edit Additional Templates->Edit Generated Item Container (ItemContainerStyle) and then edit it as per your requirements by for example changing the Color properties of the SolidColorBrushes:
<ListView ItemsSource="{Binding Path=Items}" SelectedItem="{Binding Path=DataContext.Current" >
<ListView.Resources>
<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="Red"/>
<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 TargetType="{x:Type ListViewItem}">
<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 ListViewItem}">
<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>
</ListView.Resources>
<ListView.ItemTemplate>
<DataTemplate DataType="local:Cases">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
<!-- Some UI Controls Here -->
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

In a WPF ListBox, how to "copy" the focused selection style for items over to the non-focused selection style?

First of all, this is not a duplicate of Setting the Inactive Highlight Colour of a WPF ListBox to the Active Highlight Colour. An explanation for that is given below.
Setting:
I have a WPF ListBox in a UserControl that will later be put into an application that uses heavy theming. From the perspective of the UserControl, I don't know in advance what the theming will be like.
Desired behavior:
If the ListBox does not have focus at some point, I still want the selected ListBoxItems to have the same appearance as if the ListBox does have focus.
Additional information:
Note that just setting the colors to some system defaults will not work. Doing so would override the containing application's theming. (That's the reason why this question is not a duplicate of the linked question above.)
Is there a way to realize this, e.g. using XAML?
EDIT: After a bit of research, I think I want to create a copy of the "default" ListBoxItem style ("default" at least in terms of being the default at the level of the UserControl), where all Triggers with Property="Button.IsFocused" Value="False" will not be triggered and all Triggers with Property="Button.IsFocused" Value="True" will always be triggered.
Unfortunately I have no clue where to even start to perform research in how to accomplish this. So any hints towards places where I can start researching would be much appreciated as well.
Summary
It seems like you want to achieve setting the focused style equal to the non-focused style, without editing a theme and doing it in a theme independent way. As far as I know, this isn't possible, primarily because each theme can technically implement ListBoxItem focus behavior in different ways. In fact, I've seen a theme where your desired behavior was the behavior of the ListBoxItem!
How to Modify the Theme
Now if you're open to modifying each theme to suite your needs, read ahead.
If you're modifying the theme globally, you can edit the style for the ListBoxItem directly (after finding out where it exists). If you want the changes applied more specifically, then you'll end up copying the current ListBoxItem style (from whatever theme you're editing) and making changes to it.
A copy of the default ListBoxItem theme is as follows (I used Visual Studio to make the copy). The things you need to change are going to be slightly different for each theme, but the general idea is the same.
<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 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>
The key part is in the middle:
<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>
This is setting up two different styles for the selected item while focused and while unfocused.
To get your desired behavior, you have one of two options; you can either simply turn it into a simple trigger just on IsSelected, replacing the above chunk with:
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Background}"/>
<Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Border}"/>
</Trigger>
or you can change the Item.SelectedInactive.Background and Item.SelectedInactive.Border properties to match the active colors (this was above the ListBox style):
<SolidColorBrush x:Key="Item.SelectedInactive.Background" Color="#3D26A0DA"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Border" Color="#FF26A0DA"/>
<SolidColorBrush x:Key="Item.SelectedActive.Background" Color="#3D26A0DA"/>
<SolidColorBrush x:Key="Item.SelectedActive.Border" Color="#FF26A0DA"/>
Generally the first approach is preferred, as it's more clear what's going on.
Additional Constraints
Now, the above copy of the default theme's ListBoxItem will change it for all ListBoxItems. If you want to only change some, then you need to add a key to your "copied style", like so:
<Style x:Key="InactiveLikeActive" TargetType="{x:Type ListBoxItem}">
And then at some level above where you want the style applied (perhaps even just a single ListBox itself), add the following style definition:
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource InactiveLikeActive}" />
For example:
<ListBox>
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource InactiveLikeActive}" />
</ListBox.Resources>
<ListBoxItem>One</ListBoxItem>
<ListBoxItem>Two</ListBoxItem>
</ListBox>
Closing Thoughts
While WPF makes it possible to override almost all default appearances, it doesn't necessarily make it easy, or simple to do.
The shortest variant that has been achieved
<ListBox ItemsSource="{Binding ElectrEquipAll}"
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem" >
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="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>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#1F26A0DA"/>
<Setter Property="BorderBrush" Value="#a826A0Da"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#3D26A0DA"/>
<Setter Property="BorderBrush" Value="#FF26A0DA"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>

Use the same border as the TextBox for my control

I have a custom WpfToolkit DataGrid (.net 3.5) that is always in edit mode and I would like it to have the same border as the standard TextBox.
I tried to:
bind the BorderBrush of my control to the BorderBrush of a TextBox (it seems the BorderBrush of the TextBox is null).
read the BorderBrush of a TextBox at runtime (it was always null).
I also tried to manually set a BorderBrush, but the TextBox has a
different border brush depending on the Windows theme.
A normal TextBox border looks like this:
My control looks like this, but should have the same border as the text box:
EDIT:
my DataGrid cell style:
<Style TargetType="{x:Type Controls:DataGridCell}" x:Key="DefaultExcelCell">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="TextBlock.TextAlignment" Value="Right" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Transparent" />
<!-- The text color of a selected cell (Black = same as not selected cell) -->
<Setter Property="Foreground" Value="Black" />
</Trigger>
</Style.Triggers>
</Style>
Look a the default TextBox style, you can figure out what is going on there. And i don't see why the BorderBrush of a TextBox at runtime is always null, it shouldn't be if the TextBox does not have another style applied.
<LinearGradientBrush x:Key="TextBoxBorder" EndPoint="0,20" MappingMode="Absolute" StartPoint="0,0">
<GradientStop Color="#ABADB3" Offset="0.05"/>
<GradientStop Color="#E2E3EA" Offset="0.07"/>
<GradientStop Color="#E3E9EF" Offset="1"/>
</LinearGradientBrush>
<Style x:Key="TextBoxStyle1" BasedOn="{x:Null}" TargetType="{x:Type TextBox}">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
<Setter Property="BorderBrush" Value="{StaticResource TextBoxBorder}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="AllowDrop" Value="true"/>
<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}">
<Themes:ListBoxChrome x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderFocused="{TemplateBinding IsKeyboardFocusWithin}" SnapsToDevicePixels="true">
<ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Themes:ListBoxChrome>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsInactiveSelectionHighlightEnabled" Value="true"/>
<Condition Property="IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="SelectionBrush" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}"/>
</MultiTrigger>
</Style.Triggers>
</Style>

Adding heading to existing control

I want to create a custom control type that behaves exactly like a ListBox, except that it displays with a heading above it.
I think what I need to do is inherit from ListBox and use code like the following:
var originalTree = Template.VisualTree;
var panel = new FrameworkElementFactory(typeof(StackPanel));
var heading = new FrameworkElementFactory(typeof(TextBlock));
heading.SetValue(TextBlock.TextProperty, "Heading");
panel.AppendChild(heading);
panel.AppendChild(originalTree);
Template.VisualTree = panel;
Except wherever I tried to place it, it didn't work, because Template.VisualTree was null. What am I doing wrong?
As far as i know templates can be defined in various ways, if the VisualTree is null, it has been generated 'by reference', in that case it has been set with Frameworktemplate.Template.
(Editing that is not intended, all members are internal or private)
I would use a UserControl if you are going to take the whole root anyway.
Edit: Copying and editing the default template should be fine as well, here is the default style:
<SolidColorBrush x:Key="ListBorder" Color="#828790"/>
<Style TargetType="{x:Type ListBox}">
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
<Setter Property="BorderBrush" Value="{StaticResource ListBorder}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="ScrollViewer.PanningMode" Value="Both"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="1" SnapsToDevicePixels="true">
<ScrollViewer Focusable="false" Padding="{TemplateBinding Padding}">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<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>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
(You could bind the header TextBox.Text to the ListBox.Tag then you do not need to subclass it)
Default templates and styles are on MSDN.
This article http://www.wpftutorial.net/CustomVsUserControl.html implies that a UserControl is a conglomerate of several controls - in your case a Label or TextBlock plus a ListBox? So is a user control a possible solution?

DataTemplate, Style, Triggers

I have a <ListBox> with custom <ListBox.ItemTemplate> and <DataTemplate> in it:
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="2" CornerRadius="5">
<Image Source="{Binding Picture}" />
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Now when I'm choosing the ListBoxItem it gets ugly with blue colored row selection. I'd like to change it. I want to color only border's background and nothing else. Also I want to change MouseOver behavior. I've tried trough triggers, but ContentPresenter doesn't have Background property.
UPD:
Well, I've managed to change the background on MouseEnter and MouseLeave:
<EventTrigger RoutedEvent="Border.MouseEnter">
<BeginStoryboard>
<Storyboard >
<ColorAnimation Storyboard.TargetProperty="Background.Color"
To="LightBlue" Duration="0:0:0.03"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
But still can't change the Background when item's selected. I'm trying through:
<Trigger Property="ListBoxItem.IsSelected" Value="True">
<Setter Property="Background" Value="Red" />
</Trigger>
Doesn't work
The coloring you're looking for is in two Triggers inside the template for ListBoxItem, not the ItemTemplate. To change this you need to edit the ItemContainerStyle for the ListBox. This is the default that can be used as a starting point:
<ListBox>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="{Binding Path=HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment" Value="{Binding Path=VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Padding" Value="2,0,0,0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="Bd" SnapsToDevicePixels="true" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="Selector.IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
You can do this by using triggers. I have in one of my projects something like this:
<Trigger Property="IsSelected" Value="true">
<Setter Property="Panel.ZIndex" Value="1"/>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource TabItemSelectedBackground}"/>
</Trigger>
Though it doesn't change Border color, it shows how to change the Background. So, maybe try to set this to null. This triggers are part of a custom Style in which a tracking is achieved using IsMouseOver property.
HTH

Resources