WPF add custom attribute for control style in ResourceDictionary - wpf

I am making a skin for WPF and store all the styles in ResourceDictionary. I wrote a button template which has many various colors, eg: PrimaryHover, PrimaryActive etc. If I want to add a new style with other colors, I'll need to copy and paste this code and replace colors in whole code. So as not to do this, I want to create custom properties which will contain colors and point them in the triggers. I created a class with properties but it's invisible inside the ResourceDictionary. How to do that?
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background" Value="{StaticResource Primary}" />
<Setter Property="Foreground" Value="{StaticResource White}" />
<Setter Property="BorderBrush" Value="{StaticResource PrimaryBorder}"/>
<Setter Property="BorderThickness" Value="0,1,0,0"/>
<Setter Property="Padding" Value="5,0,5,0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid SnapsToDevicePixels="True">
<Border x:Name="BkgBorder" CornerRadius="3" BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" Opacity="1">
</Border>
<ContentPresenter Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="True"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Background" Value="{StaticResource PrimaryFocused}" TargetName="BkgBorder"/>
<Setter Property="BorderBrush" Value="{StaticResource PrimaryBorderFocused}" TargetName="BkgBorder"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsEnabled" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" Value="{StaticResource PrimaryHover}" TargetName="BkgBorder"/>
<Setter Property="BorderBrush" Value="{StaticResource PrimaryBorderHover}" TargetName="BkgBorder"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsPressed" Value="True"/>
<Condition Property="IsEnabled" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" Value="{StaticResource PrimaryActive}" TargetName="BkgBorder"/>
<Setter Property="BorderBrush" Value="{StaticResource PrimaryBorderActive}" TargetName="BkgBorder"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{StaticResource White}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and class
class Buttons
{
public static SolidColorBrush GetHoverColor(DependencyObject obj)
{
return (SolidColorBrush)obj.GetValue(HoverColorProperty);
}
public static void SetHoverColor(DependencyObject obj, SolidColorBrush value)
{
obj.SetValue(HoverColorProperty, value);
}
public static readonly DependencyProperty HoverColorProperty =
DependencyProperty.RegisterAttached("HoverColor", typeof(SolidColorBrush), typeof(MainWindow), new PropertyMetadata(new SolidColorBrush(Colors.Green)));
}

I was able to copy your class into a test project exactly as it is and use it just fine.
Are you maybe neglecting to set up the XAML Namespace? My working example looks like this:
<Window x:Class="CSharpTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CSharpTest">
<Button local:Buttons.HoverColor="Yellow"/>
</Window>
As a seperate note, keep in mind that with the way your proeprty is currently declared, it can be applied to any kind of DependencyObject. If you want to limit so it is only valid on Buttons, then you should do this instead:
public static SolidColorBrush GetHoverColor(Button obj)
{
return (SolidColorBrush)obj.GetValue(HoverColorProperty);
}
public static void SetHoverColor(Button obj, SolidColorBrush value)
{
obj.SetValue(HoverColorProperty, value);
}

Related

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>

Change ItemContainerStyle of ListBox MVVM in WPF MVVM Pattern

I have a ListBox which has an itemcontainerstyle, it has background color(lets say Green) too in this style, I want to change the background color from MVVM.
The color will change on some specific condition,otherwise the default color(ie Green) should apply.
<ListBox x:Name="lst1" ItemsSource="{Binding DataSource}" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Disabled" Style="{DynamicResource StepListBox}" ItemContainerStyle="{DynamicResource ListBoxItemStyle}" Margin="-10,0,0,0">
//Listbox Items
</ListBox>
and this is my ListboxItemStyle,its defined in a separate xaml file
<Style x:Key="ListboxItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Margin" Value="20,0,0,10"/>
<Setter Property="Background" Value="{DynamicResource Green}"/>
<Setter Property="BorderBrush" Value="{DynamicResource TertiaryMediumStroke}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="{DynamicResource SecondaryDark}"/>
<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="10,10,0,10"/>
<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">
<Grid Margin="0">
<Rectangle x:Name="BgColor" Fill="{DynamicResource TertiaryMediumStroke}" Margin="-10,-10,0,-10" Opacity="0.1"/>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<!--#FFD1EFD6-->
<Setter Property="Background" TargetName="Bd" Value="#FFD7F0DB" />
<Setter Property="Opacity" TargetName="BgColor" Value="0" />
<Setter Property="Foreground" Value="{DynamicResource ForeLight}"/>
</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="Opacity" TargetName="BgColor" Value="0" />
<Setter Property="Foreground" Value="{DynamicResource ForeDisable}"/>
<Setter Property="Background" TargetName="Bd" Value="{StaticResource TertiaryMedium}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
How can I change this in my ViewModel?
When using MVVM, the view models should really know nothing about the views. However, if you really insist on changing UI element colours from the view model, then you can follow this approach. The idea is to have one or more properties with primitive types, such as bool, int, or even enum, that are data bound in the UI. Using DataTriggers, your UI can 'listen' for changes in these properties and update colours accordingly. Take this example:
If you have just one or two colours to update, you can use a bool property and a some DataTriggers:
<Style>
<Setter Property="Background" Value="White" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsElementRed}" Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
If you have a range of colours to update, you could use a custom enum:
<Style>
<Setter Property="Background" Value="White" />
<Style.Triggers>
<DataTrigger Binding="{Binding EnumInstance}" Value="SomeValue">
<Setter Property="Background" Value="Red" />
</DataTrigger>
...
<DataTrigger Binding="{Binding EnumInstance}" Value="SomeOtherValue">
<Setter Property="Background" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
Alternatively, you could also use an IValueConverter to convert between the enum instance and the various required colours.
You'll only need a DataTrigger in your Style on a boolean in your ViewModel layer:
<Style.Triggers>
<DataTrigger Binding="{Binding MyBooleanProperty, Mode=OneWay}" Value="True">
<Setter Property="Background" Value="WhatEverColorYouWant"/>
</DataTrigger>
</Style.Triggers>
<Setter Property="Background" Value="Green"/>
And in your ViewModel:
public bool MyBool {
get { return _firstCondition && _secondCondition && _thirdCondition; }
}
And when one of the condition changes, you'll just want to raise a NotifyPropertyChanged on MyBool

Remove Listview highlight

I am using List View with WPF, i want to remove highlight color on Mouse Over, i am implementing this code.
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Transparent" />
</Trigger>
Although it is removing highlight color but not completely, there's a little gray rectangle box on top of the List View Item, How can i completely remove this highlight?
Below is the image attached as well.
http://i.stack.imgur.com/8uvsi.png
Altamash, to my understanding, the reason why the white line is there is because wpf is using the default Windows Aero theme which includes that color scheme. In order to modify it, you can write your own control for your ListViewItem
Before
After
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Style.Resources>
<SolidColorBrush x:Key="ListItemHoverFill" Color="LightBlue"/>
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border CornerRadius="0" SnapsToDevicePixels="True"
BorderThickness="0,-1,0,1"
BorderBrush="#dcdbd5"
Background="{TemplateBinding Background}">
<Border Name="InnerBorder" CornerRadius="0" BorderThickness="0">
<Grid>
<Rectangle Name="UpperHighlight" Visibility="Collapsed" Fill="Transparent" />
<GridViewRowPresenter Grid.RowSpan="0"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Grid>
</Border>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{StaticResource ListItemHoverFill}" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter TargetName="UpperHighlight" Property="Visibility" Value="Visible" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
Hope this helps ;)
Maybe setting the style for the item container
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Control.Focusable" Value="False"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
or you can use Multi Triggers
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="false"/>
<Condition Property="IsMouseOver" Value="true"/>
</MultiTrigger.Conditions>
My solution was in setting classical theme:
public static void SetTheme(string themeName, string themeColor)
{
const BindingFlags staticNonPublic = BindingFlags.Static | BindingFlags.NonPublic;
var presentationFrameworkAsm = Assembly.GetAssembly(typeof(Window));
var themeWrapper = presentationFrameworkAsm.GetType("MS.Win32.UxThemeWrapper");
var isActiveField = themeWrapper.GetField("_isActive", staticNonPublic);
var themeColorField = themeWrapper.GetField("_themeColor", staticNonPublic);
var themeNameField = themeWrapper.GetField("_themeName", staticNonPublic);
// Set this to true so WPF doesn't default to classic.
isActiveField.SetValue(null, true);
themeColorField.SetValue(null, themeColor);
themeNameField.SetValue(null, themeName);
}
static App()
{
try
{
SetTheme("Classic", "NormalColor");
Source:
http://northhorizon.net/2010/how-to-actually-change-the-system-theme-in-wpf/

wpf custom control using blend

I'm trying to build a custom ToggleIconButton with image = toggle button with image.
But some features as Command not really working.
I created the following custom class:
public class ToggleIconButton : ToggleButton
{
static ToggleIconButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ToggleIconButton), new FrameworkPropertyMetadata(typeof(ToggleIconButton)));
}
public static readonly DependencyProperty IconProperty =
DependencyProperty.Register("Icon", typeof(string), typeof(ToggleIconButton), new PropertyMetadata(default(string)));
public string Icon
{
get { return (string)GetValue(IconProperty); }
set { SetValue(IconProperty, value); }
}
}
and using Blend, I created the following style:
ResourceDictionary1.xaml
<SolidColorBrush x:Key="ToolBarButtonHoverBorder" Color="#3399FF"/>
<SolidColorBrush x:Key="ToolBarButtonChecked" Color="#E6F0FA"/>
<SolidColorBrush x:Key="ToolBarButtonHover" Color="#C2E0FF"/>
<SolidColorBrush x:Key="ToolBarButtonPressedBorder" Color="#3399FF"/>
<SolidColorBrush x:Key="ToolBarButtonPressed" Color="#99CCFF"/>
<Style x:Key="ToggleButtonStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<Image Width="22" Height="22" HorizontalAlignment="Center" VerticalAlignment="Center" Source="{Binding Icon, Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls:ToggleIconButton}}}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter Property="BorderBrush" Value="{StaticResource ToolBarButtonHoverBorder}"/>
<Setter Property="Background" Value="{StaticResource ToolBarButtonChecked}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" Value="{StaticResource ToolBarButtonHoverBorder}"/>
<Setter Property="Background" Value="{StaticResource ToolBarButtonHover}"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter Property="BorderBrush" Value="{StaticResource ToolBarButtonHoverBorder}"/>
<Setter Property="Background" Value="{StaticResource ToolBarButtonHover}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="true"/>
<Condition Property="IsChecked" Value="true"/>
</MultiTrigger.Conditions>
<Setter Property="BorderBrush" Value="{StaticResource ToolBarButtonPressedBorder}"/>
<Setter Property="Background" Value="{StaticResource ToolBarButtonPressed}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsKeyboardFocused" Value="true"/>
<Condition Property="IsChecked" Value="true"/>
</MultiTrigger.Conditions>
<Setter Property="BorderBrush" Value="{StaticResource ToolBarButtonPressedBorder}"/>
<Setter Property="Background" Value="{StaticResource ToolBarButtonPressed}"/>
</MultiTrigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="BorderBrush" Value="{StaticResource ToolBarButtonPressedBorder}"/>
<Setter Property="Background" Value="{StaticResource ToolBarButtonPressed}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ToggleIconButtonStyle" TargetType="{x:Type controls:ToggleIconButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:ToggleIconButton}">
<Grid>
<ToggleButton Content="ToggleButton" Style="{DynamicResource ToggleButtonStyle}"/>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Content="Click!" Style="{DynamicResource ContentPresenterStyle}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ContentPresenterStyle" TargetType="{x:Type ContentPresenter}"/>
and apply the custom control:
<ToolBar VerticalAlignment="Top">
<Controls:ToggleIconButton
Content="ToggleIconButton"
Height="24"
Style="{DynamicResource ToggleIconButtonStyle}"
VerticalAlignment="Center"
Width="24"
Icon="/Images/musicNote48.png"
Command="{Binding AddCommand}"
/>
The control shown good, with the icon, but Command is not invoked. If i click on the ContentPresenter content it is... my wish is to remove the ContentPresenter with the label "Click!" and use only the icon...

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?

Resources