Animating SolidColorBrush in Background - c#

I am trying to create a simple button style, that will change the opacity of the background from 0.0 to 1.0 on mouse over (and vice versa). I am creating a template for said button and I am binding all the properties in the template. It all works properly except the SolidColorBrush in background, that I can not bind to the template binding. I've seen some mentions of TemplateBinding not being the right one due to contexts, but I am not able to find another solution. I suspect, there might be a problem of Background being a Brush and I need just a Color component of that brush, but I am not able to obtain it.
The obvious override is to create two template styles with two different colors (which works), but I would like to avoid such hard-coding and copy-paste. What I would like to have is an option to specify Background property on Button, that would be used in SolidColorBrush and then the opacity would do the rest.
<Style TargetType="{x:Type Button}" x:Key="WindowButtonStyle">
<Setter Property="Width" Value="46" />
<Setter Property="Height" Value="32" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<Border.Background>
<SolidColorBrush x:Name="ButtonBackgroundBrush" Color="???" Opacity="0.0" />
</Border.Background>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}" />
</Border>
<ControlTemplate.Resources>
<Storyboard x:Key="MouseOverAnimation">
<DoubleAnimation Storyboard.TargetName="ButtonBackgroundBrush" Storyboard.TargetProperty="Opacity" To="1.0" Duration="0:0:0.15" />
</Storyboard>
<Storyboard x:Key="MouseOutAnimation">
<DoubleAnimation Storyboard.TargetName="ButtonBackgroundBrush" Storyboard.TargetProperty="Opacity" To="0.0" Duration="0:0:0.15" />
</Storyboard>
</ControlTemplate.Resources>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource MouseOverAnimation}" />
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource MouseOutAnimation}" />
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then, the button is used like this:
<Button x:Name="MinimizeButton" Style="{StaticResource WindowButtonStyle}" Click="MinimizeButton_Click" Background="Green">
<Image Source="../Resources/WindowButtons/Images/win-minimize.png" Width="12" Height="12"></Image>
</Button>
Added Background="Green" property setting to test it, but did not worked.

You could use a TemplateBinding to bind the Background property of the Border to the Background of the Button and then animate the Opacity property of the SolidColorBrush like this:
<Style TargetType="{x:Type Button}" x:Key="WindowButtonStyle">
<Setter Property="Width" Value="46" />
<Setter Property="Height" Value="32" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}" />
</Border>
<ControlTemplate.Resources>
<Storyboard x:Key="MouseOverAnimation">
<DoubleAnimation Storyboard.TargetName="border" Storyboard.TargetProperty="Background.(SolidColorBrush.Opacity)" To="1.0" Duration="0:0:0.15" />
</Storyboard>
<Storyboard x:Key="MouseOutAnimation">
<DoubleAnimation Storyboard.TargetName="border" Storyboard.TargetProperty="Background.(SolidColorBrush.Opacity)" To="0.0" Duration="0:0:0.15" />
</Storyboard>
</ControlTemplate.Resources>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource MouseOverAnimation}" />
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource MouseOutAnimation}" />
</Trigger.ExitActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Answering my own, as always - you figure out the solution just after you post on S/O. So I hope it will help someone:
...
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<Border.Background>
<!-- ReSharper disable once Xaml.BindingWithContextNotResolved -->
<SolidColorBrush x:Name="ButtonBackgroundBrush" Color="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Background.Color}" Opacity="0.0" />
</Border.Background>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}" />
</Border>
...
I've included the ReSharper disable as well, because the warning form ReSharper was what kept me from trying this one - and desperation forced me to try it anyway.

Related

Change particular button Background color on Mouse over in xaml wpf

I am struggling with the issue for last three days.
I have to change particular Button background color on mouse over and Button Content should be delete, But i got issue that all button changed if i am hovering of button.
I put my design In resource dictionary
Below is my design code
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>`<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BaseShape" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ButtonHoverBackgroundBrush}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BaseShape" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ButtonHoverBorderBrush}" />
</ObjectAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="ButtonHighlight" Storyboard.TargetProperty="Opacity">
<SplineDoubleKeyFrame KeyTime="0" Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState> </VisualStateGroup></VisualStateManager.VisualStateGroups>'
<Border x:Name="BaseShape" CornerRadius="10" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" />
<Rectangle x:Name="ButtonHighlight" Margin="1" RadiusX="9" RadiusY="9" Stroke="{StaticResource ButtonHoverHighlightBorderBrush}" StrokeThickness="1" Grid.ColumnSpan="2" Opacity="0" />
<ContentPresenter x:Name="contentPresenter" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" />
<Rectangle x:Name="FocusedVisualElement" Stroke="{StaticResource ButtonFocusedBorderBrush}" StrokeThickness="1" RadiusX="10" RadiusY="10" Opacity="0" />
<Rectangle x:Name="DisabledVisualElement" Fill="{StaticResource DisabledBackgroundBrush}" IsHitTestVisible="false" RadiusX="10" RadiusY="10" Opacity="0" />
<Button Content="delete" Name="xyz" Width="70"Margin="0,65,59,4" HorizontalAlignment="Right">
</Button>`
and
Xaml
File with design is
and i need fix only design side not in code behind
I'm assuming by "Button background color on mouse over and Button Content should be delete" you mean that you want to change the colour and hide the content while your mouse is over the button. If so, why don't you do something like this
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Blue"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}" BorderBrush="Black" BorderThickness="1">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="SkyBlue"/>
<Setter Property="Foreground" Value="SkyBlue"/>
</Trigger>
</Style.Triggers>
</Style>
While it doesn't delete the content it sets the foreground and background to the same colour so it is hidden from the user.
Edited to address your comments
You seem to be trying to use a DataTrigger; as this answer suggests Triggers are ideal for making changes on things like OnMouseOver and DataTriggers are ideal for making changes on data changes. So unless I'm missing something, you should be using a Trigger not a DataTrigger.
An obvious solution to your style-variation problem is to create a default style with most of your styling in and then a second style that overrides the changing elements. In this case your second style would give it the "hide my text on mouse over" behaviour.
Your default style could be something like this
<Style x:Key="ButtonDefaultStyle" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Blue"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}" BorderBrush="Black" BorderThickness="1">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="White"/>
<Setter Property="Foreground" Value="Blue"/>
</Trigger>
</Style.Triggers>
</Style>
And your second style could be something like this
<Style x:Key="ButtonHideTextOnMouseOverStyle" BasedOn="{StaticResource ButtonDefaultStyle}" TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
Then you'd use them like this
<Button Content="First Button" Style="{StaticResource ButtonDefaultStyle}"/>
<Button Content="Second Button" Style="{StaticResource ButtonDefaultStyle}"/>
<Button Content="Delete" Style="{StaticResource ButtonHideTextOnMouseOverStyle}"/>
In the ButtonDefaultStyle cases the white text on a blue background is changed to blue text on a white background on mouse over. In the ButtonHideTextOnMouseOverStyle case the white text on a blue background is changed to white text on a white background on mouse over.
The full example is here
<Window x:Class="Button_Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Button_Test"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style x:Key="ButtonDefaultStyle" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Blue"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}" BorderBrush="Black" BorderThickness="1">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="White"/>
<Setter Property="Foreground" Value="Blue"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="ButtonHideTextOnMouseOverStyle" BasedOn="{StaticResource ButtonDefaultStyle}" TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<TextBlock Text="Buttons"/>
<Button Content="First Button" Style="{StaticResource ButtonDefaultStyle}"/>
<Button Content="Second Button" Style="{StaticResource ButtonDefaultStyle}"/>
<Button Content="Delete" Style="{StaticResource ButtonHideTextOnMouseOverStyle}"/>
</StackPanel>
</Window>

Animate button background color in XAML

I new to WPF and XAML, so I have ResourceDictionary (one button for now):
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="ButtonProduct" TargetType="Button">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Name="Border"
CornerRadius="0"
BorderThickness="0"
Focusable="False"
BorderBrush="Transparent" Background="White">
<ContentPresenter Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" RecognizesAccessKey="True"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="#52b0ca"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
On hover the color of the button changes, but how can I make change in fade in and out, for smooth transition of the color?
You can use EventTrigger to start ColorAnimation on MouseEnter and MouseLeave:
<ControlTemplate TargetType="{x:Type Button}">
<Border Name="Border" CornerRadius="0" BorderThickness="0" Focusable="False" BorderBrush="Transparent" Background="White">
<ContentPresenter Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" RecognizesAccessKey="True"/>
</Border>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard>
<ColorAnimation From="White" To="#52b0ca" Duration="0:0:1" Storyboard.TargetName="Border" Storyboard.TargetProperty="Background.Color"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard>
<ColorAnimation From="#52b0ca" To="White" Duration="0:0:1" Storyboard.TargetName="Border" Storyboard.TargetProperty="Background.Color"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>

Set button as selected state and change style

This is my actual button style:
<Style x:Key="CategoryButtonStyle" TargetType="{x:Type Button}">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid x:Name="grid">
<Path x:Name="TabPath" StrokeThickness="2"
Margin="{Binding ElementName=buttonContent, Converter={x:Static c:ContentToMarginConverter.Value}}"
Stroke="{StaticResource BorderBrush1}"
Fill="{StaticResource TabItemPathBrush}">
<Path.Data>
<PathGeometry>
<PathFigure IsClosed="False" StartPoint="1,0"
Segments="{Binding ElementName=buttonContent, Converter={x:Static c:ContentToPathConverter.Value}}">
</PathFigure>
</PathGeometry>
</Path.Data>
<Path.LayoutTransform>
<!-- For some reason -->
<ScaleTransform ScaleY="-1"/>
</Path.LayoutTransform>
</Path>
<Rectangle x:Name="TabItemTopBorder" Height="2" Visibility="Visible"
VerticalAlignment="Bottom" Fill="{StaticResource BorderBrush1}"
Margin="{Binding ElementName=TabPath, Path=Margin}" />
<ContentPresenter x:Name="buttonContent" Margin="10,2,10,2" VerticalAlignment="Center"
TextElement.Foreground="{StaticResource ForegroundBrush}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Fill" TargetName="TabPath">
<Setter.Value>
<SolidColorBrush Color="#FFe4f6fa"/>
</Setter.Value>
</Setter>
<Setter Property="BitmapEffect">
<Setter.Value>
<DropShadowBitmapEffect Direction="302" Opacity="0.4"
ShadowDepth="2" Softness="0.5"/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" TargetName="grid" Value="0.25"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and I need add code that I could set button to selected state which looks like when button is pressed. I was thinking about using VisualStateManager but I am not sure if it's good way and how can I do this. I've started with something like this:
<VisualStateManager.VisualStateGroups>
<VisualState x:Name="Normal" />
<VisualState x:Name="Selected">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TabPath"
Storyboard.TargetProperty="Fill">
<DiscreteObjectKeyFrame KeyTime="0" Value="#FFe4f6fa" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateManager.VisualStateGroups>
But It's not working. I just don't know what to use in storyboard.
Edit - Almost working:
<VisualState x:Name="Selected">
<Storyboard>
<ColorAnimation Storyboard.TargetName="TabPath" Storyboard.TargetProperty="Fill.(LinearGradientBrush.GradientStops)[0].(GradientStop.Color)" To="#FFe4f6fa" Duration="0:0:0" />
<ColorAnimation Storyboard.TargetName="TabPath" Storyboard.TargetProperty="Fill.(LinearGradientBrush.GradientStops)[1].(GradientStop.Color)" To="#FFa9cde7" Duration="0:0:0" />
</Storyboard>
</VisualState>
Forget to add my brushes:
<LinearGradientBrush x:Key="TabItemSelectedPathBrush" StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#FFe4f6fa" Offset="0"/>
<GradientStop Color="#FFa9cde7" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="TabItemPathBrush" StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#FFa9cde7" Offset="0"/>
<GradientStop Color="#FF3164a5" Offset="1"/>
</LinearGradientBrush>
Use ToggleButton that uses the same style and template.
In your style (the one in your original question) change TargetType on style and control template to ButtonBase.
In control template triggers add this trigger:
<Trigger Property="ToggleButton.IsChecked" Value="True">
<Setter Property="Fill" TargetName="TabPath">
<Setter.Value>
<SolidColorBrush Color="#FFe4f6fa"/>
</Setter.Value>
</Setter>
<Setter Property="BitmapEffect">
<Setter.Value>
<DropShadowBitmapEffect Direction="302" Opacity="0.4" ShadowDepth="2" Softness="0.5"/>
</Setter.Value>
</Setter>
</Trigger>
Now you can use this style for buttons, toggle buttons, radio buttons and check boxes.
VisualStates should be declared on the root element of the ControlTemplate. With the following code,
<Grid>
<Grid.Resources>
<SolidColorBrush x:Key="FillBrush" Color="Magenta" />
<Color x:Key="StateBrush" >#a3bfb1</Color>
<Style x:Key="CategoryButtonStyle" TargetType="{x:Type Button}">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="Green" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" x:Name="root">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Selected">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TabPath"
Storyboard.TargetProperty="Fill.(SolidColorBrush.Color)">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource StateBrush}">
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid x:Name="grid">
<Path x:Name="TabPath" StrokeThickness="1"
Stroke="Blue"
Fill="{StaticResource FillBrush}" Canvas.Left="16.75"
Canvas.Top="14"
Width="50"
Height="40"
Data="F1 M 26.75,24L 16.75,34L 23.5,34L 34,24L 23.5,14L 16.75,14L 26.75,24 Z ">
<Path.LayoutTransform>
<!-- For some reason -->
<ScaleTransform ScaleY="-1"/>
</Path.LayoutTransform>
</Path>
<ContentPresenter x:Name="buttonContent" Margin="10,2,10,2" VerticalAlignment="Center"
TextElement.Foreground="{TemplateBinding Foreground}"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Button x:Name="test" Style="{StaticResource CategoryButtonStyle}" Content="Test" VerticalAlignment="Center" Width="200px" BorderBrush="#888" BorderThickness="1px"/>
</Grid>
this code will change the state as intended.
VisualStateManager.GoToState(test, "TestState", true);
Make changes to the template as required, but make sure you put the VisualState declaration in the Root element of the ControlTemplate (Border in this example)
EDIT:
I've updated the Selected VisualState's transition declaration with correct property path. Let me know if it solves your problem.
Personally I would use Blend for such task, it's really the companion to Visual Studio for developing WPF applications.
In a few clicks you would get to it, nothing prevents you to either use it or just copy the XAML it generated to your project.
Editing the button style
Triggers
Here are all your button states
Blend is like bread for butter, WPF is tastier with it, unless you prefer plain butter :-)
Note that if you are on VS2012 and working on a WPF project, Microsoft Blend + SketchFlow Preview for Visual Studio 2012 is the version that will allow you to edit WPF projects. The version bundled with VS2012 is only for Windows Store apps.

Understanding WPF Control Styles and Templates

I am relatively new to .NET/C#/WPF/XAML. I have noticed sometimes controls seem to be "missing" the simplest properties to affect style. As a result, the class Style must be used to modify the the look-and-feel of a control. This is OK, but I find myself hunting on Google/StackOverflow to find the name of a particular attribute to modify.
I am searching for a canonical and complete source of default Control Styles and Templates for ALL .NET Framework WPF controls.
Before you answer too quickly, please keep reading to understand my confusion.
On this Microsoft doc page DataGrid Styles and Templates, I see the template for DataGridCell:
<!--Style and template for the DataGridCell.-->
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Border x:Name="border"
BorderBrush="Transparent"
BorderThickness="1"
Background="Transparent"
SnapsToDevicePixels="True">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Unfocused" />
<VisualState x:Name="Focused" />
</VisualStateGroup>
<VisualStateGroup x:Name="CurrentStates">
<VisualState x:Name="Regular" />
<VisualState x:Name="Current">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="border"
Storyboard.TargetProperty="(Border.BorderBrush).
(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="0"
Value="{StaticResource DatagridCurrentCellBorderColor}" />
</ColorAnimationUsingKeyFrames
>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This page from MSDN forums, shows the following Style/Triggers/Setters.
<SolidColorBrush x:Key="{x:Static DataGrid.FocusBorderBrushKey}" Color="#FF000000"/>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
<Setter Property="BorderBrush" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
</Trigger>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="BorderBrush" Value="{DynamicResource {x:Static DataGrid.FocusBorderBrushKey}}"/>
</Trigger>
</Style.Triggers>
</Style>
Why don't I see properties Background, Foreground, BorderBrush in the first template?
I have a feeling the VisualState stuff is a reference, but I cannot find the source.
Well, the ContentPresenter is a control that shows your content. The VisualState stuff is just holding the references of all the VisualState the control DataGridCell can come across. It puts some animation on the Property Border which have wrapped the ContentPresenter.
You can redefine the the whole Style, I mean put some other control to redefine the content, and use TemplateBinding to get the external dependency property. Like I mean you can even use TemplateBinding Background on the border with VisualState.
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Border x:Name="border"
BorderBrush=BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"
SnapsToDevicePixels="True">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Unfocused" />
<VisualState x:Name="Focused" />
</VisualStateGroup>
<VisualStateGroup x:Name="CurrentStates">
<VisualState x:Name="Regular" />
<VisualState x:Name="Current">
<Storyboard>
<ColorAnimationUsingKeyFrames Storyboard.TargetName="border"
Storyboard.TargetProperty="(Border.BorderBrush).
(SolidColorBrush.Color)">
<EasingColorKeyFrame KeyTime="0"
Value="{StaticResource DatagridCurrentCellBorderColor}" />
</ColorAnimationUsingKeyFrames
>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
Thats perfectly makes sense. But if you do not specify them for a Style, it means it will fallback to the default Color and background etc that has been defined for the control.
This is a property of every dependencyProperty to fallback to the default value when new value does not override it.
You can read my WPF tutorials to get more concepts like this :
http://www.abhisheksur.com/2010/12/wpf-tutorial.html
Here you can download the default style dictionaries for the WPF controls Default WPF Themes.
It can be very helpfull to know how the default style works if you have to restyle a control.

WPF style/control template reuse

I'm new to WPF, and I would like to know how to reuse some annoying xaml I have to avoid duplicating.
<Button Cursor="Hand" HorizontalAlignment="Left" Margin="0,0,0,0" x:Name="MyButton" Style="{StaticResource ButtonTemplate}" Width="286" Content="hi!" Focusable="False" IsTabStop="False"/>
<Button Cursor="Hand" HorizontalAlignment="Left" Margin="0,0,0,0" x:Name="MyButton2" Style="{StaticResource ButtonTemplate}" Width="286" Content="hi 2!" Focusable="False" IsTabStop="False"/>
I'd really like to use something like this template:
<Style TargetType="{x:Type Button}" x:Key="ButtonTemplate">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid x:Name="btGrid">
<Path Cursor="Hand" HorizontalAlignment="Left" Stretch="Fill" Stroke="{x:Null}" Opacity="0" x:Name="path"/>
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" RecognizesAccessKey="True" Visibility="Hidden" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top"/>
</Grid>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="Button.PreviewMouseLeftButtonDown">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard SlipBehavior="Slip" BeginTime="00:00:00">
<MediaTimeline Source="{Binding StringFormat={}, Path=Name}" Storyboard.TargetName="{Binding StringFormat={}_wma, Path=Name}"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="{Binding StringFormat=key{}, Path=Name}" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>
Visible
</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Button.PreviewMouseLeftButtonUp">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="{Binding StringFormat=key{}, Path=Name}" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>
Hidden
</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
<Trigger Property="IsFocused" Value="True"/>
<Trigger Property="IsDefaulted" Value="True"/>
<Trigger Property="IsMouseOver" Value="True"/>
<Trigger Property="IsPressed" Value="True"/>
<Trigger Property="IsEnabled" Value="False"/>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And I'd like the {Binding StringFormat={}, Path=Name} to point button's name, e.g. "MyButton", "MyButton2", etc.
When I run this code I get the error "Cannot freeze this Storyboard timeline tree for use across threads." :/ I understand this is because I use binding in a storyboard, correct? I don't know what to do to make this work.
Also, I'd like to make the ToggleVisibility of the image a template as well, that accepts once "Visible" and once "Hidden" values.
Thanks in advance!
You could always define properties other than Template in your style too.
<Style TargetType="{x:Type Button}"
x:Key="ButtonTemplate">
<Setter Property="Cursor" Value="Hand" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Margin" Value="0,0,0,0" />
<Setter Property="Width" Value="286" />
<Setter Property="Focusable" Value="False" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
...
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Which makes your code look like
<Button x:Name="MyButton" Style="{StaticResource ButtonTemplate}" Content="hi!" />
<Button x:Name="MyButton2" Style="{StaticResource ButtonTemplate}" Content="hi 2!" />
Yeah creating a style with target type to as button would do the trick.
Tip:It is always a good practice to write all the styling informations such as border, background, templates, etc., under the resource section of your code and apply them on the controls. It'll give good readability.
HTH :)

Categories