DataTrigger in WinRT? - xaml

I was able to find EventTrigger in the WinRT reference, however, I wasn't able to find DataTrigger. I wasn't able to use it in an application either.
Can anyone confirm that DataTrigger is really missing in WinRT? Is EventTrigger the only trigger available in WinRT?

DataTrigger is not currently supported in WinRT XAML.
Addendum by Mike Brown
The DataTrigger API has been replaced with the VisualStateManager a similar API to Data Triggers was provided by the Blend SDK for Silverlight. Since the Attached Behavior Pattern works in WinRT, it is possible to do the same.

What about this project that seems implement triggers in WinRT : http://winrttriggers.codeplex.com/

I don't know when it changed but i have DataTriggerBehavior and GoToStateAction combining them should solve your problem...
namespace imports
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
ViewSateManager place on root element
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Common">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0" To="Online">
<Storyboard>
<ColorAnimation Duration="0" To="Lime" Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)" Storyboard.TargetName="Name" />
</Storyboard>
</VisualTransition>
<VisualTransition GeneratedDuration="0" To="Offline">
<Storyboard>
<ColorAnimation Duration="0" To="Red" Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)" Storyboard.TargetName="Name" />
</Storyboard>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="Online" />
<VisualState x:Name="Offline" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Interactivity:Interaction.Behaviors>
<Core:DataTriggerBehavior Binding="{Binding Active}" Value="True">
<Core:GoToStateAction StateName="Online" />
</Core:DataTriggerBehavior>
<Core:DataTriggerBehavior Binding="{Binding Active}" Value="False">
<Core:GoToStateAction StateName="Offline" />
</Core:DataTriggerBehavior>
</Interactivity:Interaction.Behaviors>

I implemented an alternate workaround that may work for you. Steps:
Create a UserControl (either from scratch or inheriting) so you can write some code-behind C# into the control.
Create a DependencyProperty in the codebehind for the databinding you want to trigger on.
Use the DependencyProperty's PropertyChangedCallback method to implement do what you need to do in code to the control.
Bind the DependencyProperty in XAML to the data you want to trigger on.
It's not as clean as a DataTrigger, but it's not too much worse and it works well (for me at least).
Declaration in XAML (DataContext is already set to a viewmodel object):
<local:PlayButton IsPlaying="{Binding IsPlaying}"/>
Example DependencyProperty that triggers storyboards to change state:
// Use this to implement storyboard changing in W8 since triggers are not supported
public static readonly DependencyProperty IsPlayingProperty = DependencyProperty.Register(
"IsPlaying",
typeof(bool),
typeof(PlayButton),
new PropertyMetadata(null,
new PropertyChangedCallback(OnIsPlayingChanged)
));
private static void OnIsPlayingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PlayButton pb = (PlayButton)d;
bool isPlaying = (bool)e.NewValue;
if (isPlaying == false)
pb.GotoPlay.Begin();
else
pb.GotoPause.Begin();
}
public bool IsPlaying
{
get { return (bool)GetValue(IsPlayingProperty); }
set { SetValue(IsPlayingProperty, value); }
}

you can use VisualState instead of object.Triggers in Windows 8 Here is the code
<ControlTemplate TargetType="Button">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<!--Take one half second to transition to the PointerOver state.-->
<VisualTransition To="PointerOver" GeneratedDuration="0:0:0.5"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="ButtonBrush"
Storyboard.TargetProperty="Color" To="Red" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.Background>
<SolidColorBrush x:Name="ButtonBrush" Color="Green"/>
</Grid.Background>
</Grid>
</ControlTemplate>

Related

UWP equivalent for WPF Style.Triggers

I created a control that works in WPF and now trying to port to UWP.
The control exposes a boolean property and when set to true, I change the background color of a Path (Stroke) via a story board with a RepeatBehavior of "forever".
After reading numerous articles I understand UWP uses VisualStates, Interactivity, etc... but no triggers. I have tried reworking the code but not getting the background to change/animate.
Part of ControlTemplate in WPF
<Path Fill="Transparent"
Stroke="{TemplateBinding Outline}"
StrokeThickness="{TemplateBinding Thickness}"
StrokeDashCap="Flat"
x:Name="OuterRing">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure x:Name="OutlineFigurePart">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment x:Name="OutlineArcPart" IsLargeArc="True" SweepDirection="Clockwise"/>
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
<Path.Style>
<Style TargetType="Path">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=controls:MyControl}, Path=IsValueOutOfRange}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="Red"
AutoReverse="True"
Duration="0:0:0.8"
RepeatBehavior="Forever"
Storyboard.TargetProperty="(Path.Stroke).(SolidColorBrush.Color)"
Storyboard.TargetName="OuterRing"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation To="LightGray"
Storyboard.TargetProperty="(Path.Stroke).(SolidColorBrush.Color)"
Duration="0:0:0.8"
FillBehavior="Stop"
Storyboard.TargetName="OuterRing"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
Control used in the view (XAML)
<controls:MyControl Width="48"
Height="48"
Header="My Header"
IsValueOutOfRange="{x:Bind ValueOutOfRange" />
When ValueOutOfRange is set to 'True' from the ViewModel, the Path.Stroke color should animate on 'OuterRing'
When ValueOutOfRange is set to 'False' from the ViewModel, the Path.Stroke color should go back to normal.
Part of ControlTemplate in UWP
<Path Fill="Transparent"
Stroke="{TemplateBinding Outline}"
StrokeThickness="{TemplateBinding Thickness}"
StrokeDashCap="Flat"
x:Name="OuterRing">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure x:Name="OutlineFigurePart">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment x:Name="OutlineArcPart" IsLargeArc="True" SweepDirection="Clockwise"/>
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{TemplateBinding IsValueOutOfRange}" Value="True" ComparisonCondition="Equal">
<media:ControlStoryboardAction ControlStoryboardOption="Play">
<media:ControlStoryboardAction.Storyboard>
<Storyboard>
<ColorAnimation
To="Red"
Storyboard.TargetName="OuterRing"
Storyboard.TargetProperty="(Path.Stroke).(SolidColorBrush.Color)"
AutoReverse="True"
Duration="0:0:8"
RepeatBehavior="Forever" />
</Storyboard>
</media:ControlStoryboardAction.Storyboard>
</media:ControlStoryboardAction>
</core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</Path>
In UWP, the more common way is to use VisualStateManager to process:
According to your code, it can be rewritten like this:
code-behind
public bool IsValueOutOfRange
{
get { return (bool)GetValue(IsValueOutOfRangeProperty); }
set { SetValue(IsValueOutOfRangeProperty, value); }
}
// Using a DependencyProperty as the backing store for IsValueOutOfRange. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsValueOutOfRangeProperty =
DependencyProperty.Register("IsValueOutOfRange", typeof(bool), typeof(PathCustomControl), new PropertyMetadata(false,new PropertyChangedCallback(IsValueOutofRange_Changed)));
private static void IsValueOutofRange_Changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if(e.NewValue is bool isOut)
{
var instance = d as PathCustomControl;
if (isOut)
VisualStateManager.GoToState(instance, "Invalid", false);
else
VisualStateManager.GoToState(instance, "Normal", false);
}
}
Template
<ControlTemplate TargetType="local:PathCustomControl">
<Grid x:Name="rootGrid">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Common">
<VisualState x:Name="Invalid">
<Storyboard>
<ColorAnimation To="Red"
AutoReverse="True"
Duration="0:0:0.8"
RepeatBehavior="Forever"
Storyboard.TargetProperty="(Path.Stroke).(SolidColorBrush.Color)"
Storyboard.TargetName="OuterRing"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Normal">
<Storyboard>
<ColorAnimation To="LightGray"
Storyboard.TargetProperty="(Path.Stroke).(SolidColorBrush.Color)"
Duration="0:0:0.8"
FillBehavior="Stop"
Storyboard.TargetName="OuterRing"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Path Fill="Transparent"
StrokeThickness="{TemplateBinding Thickness}"
Stroke="{TemplateBinding Outline}"
StrokeDashCap="Flat"
x:Name="OuterRing">
<Path.Data>
<PathGeometry>
<PathGeometry.Figures>
<PathFigureCollection>
<PathFigure x:Name="OutlineFigurePart">
<PathFigure.Segments>
<PathSegmentCollection>
<ArcSegment x:Name="OutlineArcPart" IsLargeArc="True" SweepDirection="Clockwise"/>
</PathSegmentCollection>
</PathFigure.Segments>
</PathFigure>
</PathFigureCollection>
</PathGeometry.Figures>
</PathGeometry>
</Path.Data>
</Path>
</Grid>
</ControlTemplate>
When IsValueOutOfRange changes, it will switch the control to a different state, thus running different animations.
This is just an example, I use a button on the device to switch the state of the control, it works. But if you want to adapt your project, you need to meet the following two conditions:
Provide an initial Stroke value
The default IsValueOutOfRange is False when the control is first loaded, so the default color of your Path should be the same as the Normal state.

GridView's ItemContainerStyle and selection states

I'm trying to change the appearance of gridview items when they are selected.
(Before, I used a trick with an IsSelected property in the ViewModel object bound to the containing grid and a bool-to-color converter, but I recognize that it is bad)
To do so, I do:
<GridView ItemContainerStyle="{StaticResource GridViewItemContainerStyle}" ...> ...
and
<Style x:Key="GridViewItemContainerStyle" TargetType="GridViewItem">
<Setter Property="Background" Value="Red" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GridViewItem">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.Background)" Storyboard.TargetName="itemGrid">
<DiscreteObjectKeyFrame KeyTime="0" Value="Black"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="UnselectedSwiping"/>
<VisualState x:Name="UnselectedPointerOver"/>
<VisualState x:Name="Selecting"/>
<VisualState x:Name="Selected">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Grid.Background)" Storyboard.TargetName="itemGrid">
<DiscreteObjectKeyFrame KeyTime="0" Value="White"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="SelectedSwiping"/>
<VisualState x:Name="Unselecting"/>
<VisualState x:Name="Unselected"/>
<VisualState x:Name="SelectedUnfocused"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid ... x:Name="itemGrid">
<!-- HERE MY DATA TEMPLATE -->
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
When I run the app, the items are Black (as in the "normal" state). But selecting them does not turn them into White. Where am I wrong?
Moreover, it there a way to set "ItemContainerStyle" without having it to "overwrite" the "ItemTemplate" ???
You DataTemplate should be inside the ItemTemplate property of the GridView element in your page's XAML. Make a separate XAML file (ResourceDictionary), for example CustomStyles.xaml. Reference it in App.xaml like this:
<Application.Resources>
<!-- Application-specific resources -->
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="PathToCustomStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
You can find on MSDN the default template for GridViewItem (http://msdn.microsoft.com/en-us/library/windows/apps/xaml/jj709915.aspx), under the Default style section (second, longer XAML).
Copy that and paste it into CustomStyles.xaml. Just give it some key like:
<Style TargetType="GridViewItem" x:Key="CustomGridViewItemStyleWithWhiteSelectionBackground">...
As you can see, Selected visual state changes the opacity of three targets, SelectionBackground, SelectedBorder and SelectedCheckMark. So, these elements are not visible in Normal state because their opacity is zero. Find those three elements down below, and change their properties if needed. For the background change the Fill property of the SelectionBackground rectangle:
<Rectangle x:Name="SelectionBackground"
Margin="4"
Fill="White"
Opacity="0" />
Now, when the selection occurs, this element's opacity will be changed to 1 and since you set it's Fill to be white, the background of the selected item will be white. And don't forget to reference this style in the definition of the GridView:
<GridView ItemContainerStyle="{StaticResource CustomGridViewItemStyleWithWhiteSelectionBackground}" ...>
<GridView.ItemTemplate>
<DataTemplate>
...define your template here...
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Edit:
This is the expanded style XAML, probably more suitable for some more complicated style changes. If you want to change only the background, you should take the first style from that MSDN link above under the Default style section, and just edit this (and give it some style key, so you don't overwrite the default one):
SelectedBackground="{ThemeResource ListViewItemSelectedBackgroundThemeBrush}"

Windows Store Apps: change the background of GridViewItem on PointerOver

I have a gridview thats' bound to the following model
class Item
{
string Title;
string ImagePath
string ImagePathPressed;
}
where ImagePath & ImagePathPressed are paths to images within the app.
now I want my grid View Item to change it's background when the mouse is over from the value in ImagePath to that in ImagePathPressed
how to achive this ?
it would be better if you make these variables as properties and also implement INotifyPropertyChanged on your class. And on your mouseOver event of gridView change the ImagePath to that of ImagePathPressed it will reflect the change in ImagePath.i think on your mouseover event you can get the on which item is your mouse pointer reside.
Following this link to get guideline to implement Style for GridViewItem
http://msdn.microsoft.com/en-us/library/windows/apps/jj709915.aspx
You should implement your class members as Bindable Properties then implement PointerOver state as guideline in above link.
I suggest that you should create two images (one for normal state and other one for hover state)
For example:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GridViewItem">
<Border x:Name="OuterContainer">
<Grid>
<Image x:Name="NormalImage" Source="{Binding ImagePath}"/>
<Image x:Name="PressImage" Source="{Binding ImagePathPressed}" Opacity="0"/>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="PointerOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="PressImage"
Storyboard.TargetProperty="Opacity"
Duration="0"
To="1" />
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">...
OK, I got it
I implemented a control template like this:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GridViewItem">
<Border x:Name="OuterContainer" Tag={Binding}>
<Border.Resources>
<!-- Define brush resources for both states-->
<ImageBrush x:Key="MouseOverBrush" ImageSource="{Binding Tag.ImagePathPressed, ElementName=OuterContainer}" Stretch="None" />
<ImageBrush x:Key="DefaultBrush" ImageSource="{Binding Tag.ImagePath, ElementName=OuterContainer}" Stretch="None" />
</Border.Resources>
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="ReorderHintContent" >
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource MouseOverBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
.
.
.
<Grid x:Name="ReorderHintContent" Tag="{Binding}" DataContext="{Binding}" >
<Grid.Background>
<!-- Default background-->
<ImageBrush x:Name="BGBrush" ImageSource="{Binding Tag.ImagePath, ElementName=ReorderHintContent}" Stretch="None" Opacity="0" />
</Grid.Background>
I had to set the Tag for both the border and the Grid in order to have access to the properties of the model

Where are the possible VisualStates for Windows 8 Metro controls documented?

When writing a custom ControlTemplate (XAML) for a Win 8 Metro control we need to use the VisualStateManager to update the control according to VisualState transitions. I see the below sample all over MSDN, but I can't find where the VisualStateGroup "CommonStates" is documented and what other VisualStates are defined other than "PointerOver" and "Normal"? Do you have to go dig in the SDK to find the default ControlTemplate for a button? If so, where?
<ControlTemplate TargetType="Button">
<Grid >
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<!--Take one half second to transition to the PointerOver state.-->
<VisualTransition To="PointerOver"
GeneratedDuration="0:0:0.5"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal" />
<!--Change the SolidColorBrush, ButtonBrush, to red when the
Pointer is over the button.-->
<VisualState x:Name="PointerOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="ButtonBrush"
Storyboard.TargetProperty="Color" To="Red" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.Background>
<SolidColorBrush x:Name="ButtonBrush" Color="Green"/>
</Grid.Background>
</Grid>
</ControlTemplate>
You can go to the design view of your xaml file and with the Button control selected - right click/Edit Template/Edit Current - will get you the default template extracted. Normally controls should be annotated with attributes that indicate which visual states should be used in the template like below, but I can't see them when I just navigate to definition of a control like Button.
[TemplateVisualState(GroupName="CommonStates", Name="Normal")]
[TemplateVisualState(GroupName="CommonStates", Name="PointerOver")]

Create ControlTemplate with Attached Property

I am essentially trying to create a Button with an image background and border that changes color based off if it is hovered, clicked or default state. I will several of these types of buttons, and I want to have a ControlTemplate defined that I can reuse and change the ImageSource. Here is what I have so far, but for some reason the TemplateBinding doesn't seem to work. The buttons have no background image set.
Template:
<ControlTemplate x:Name="SkillIconTemplate" TargetType="Button">
<Border CornerRadius="10" BorderThickness="2" Margin="5">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition To="MouseOver" GeneratedDuration="0:0:0.1"/>
<VisualTransition To="Pressed" GeneratedDuration="0:0:0.1"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="BorderBrush"
Storyboard.TargetProperty="Color"
To="Yellow" />
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ColorAnimation Storyboard.TargetName="BorderBrush"
Storyboard.TargetProperty="Color"
To="Black"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border.BorderBrush>
<SolidColorBrush x:Name="BorderBrush" Color="White"/>
</Border.BorderBrush>
<Border.Background>
<ImageBrush ImageSource="{TemplateBinding local:SkillIcon.IconImageSource}"/>
</Border.Background>
</Border>
</ControlTemplate>
Buttons:
<Button x:Name="skillIcon0" Width="75" Height="75" ClickMode="Press" local:SkillIcon.IconImageSource="ms-appx:/data/images/icons/skill_icon_0.png" Template="{StaticResource SkillIconTemplate}"/>
<Button x:Name="skillIcon1" Width="75" Height="75" ClickMode="Press" local:SkillIcon.IconImageSource="ms-appx:/data/images/icons/skill_icon_1.png" Template="{StaticResource SkillIconTemplate}"/>
…
Property Code:
public abstract class SkillIcon : DependencyObject
{
public static readonly DependencyProperty IconImageSourceProperty = DependencyProperty.RegisterAttached(
"IconImageSource",
typeof(ImageSource),
typeof(SkillIcon),
new PropertyMetadata(GetIconImage(0))
);
public static ImageSource GetIconImageSource(DependencyObject obj)
{
return (ImageSource)obj.GetValue(IconImageSourceProperty);
}
public static void SetIconImageSource(DependencyObject obj, ImageSource value)
{
obj.SetValue(IconImageSourceProperty, value);
}
/// <summary>
/// Gets the image source for the button
/// </summary>
public static ImageSource GetIconImage(int index)
{
Uri source = new Uri(string.Format("ms-appx:/data/images/icons/skill_icon_{0}.png", index), UriKind.RelativeOrAbsolute);
return new BitmapImage() { UriSource = source };
}
}
Usage in code:
SkillIcon.SetIconImageSource(skillIcon0, SkillIcon.GetIconImage(0));
I ended up binding the background of the Button to the background of the border:
<ControlTemplate x:Name="SkillIconTemplate" TargetType="Button">
<Border CornerRadius="10" BorderThickness="2" Margin="5" Background="{TemplateBinding Background}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition To="MouseOver" GeneratedDuration="0:0:0.05"/>
<VisualTransition To="Pressed" GeneratedDuration="0:0:0.05"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="BorderBrush"
Storyboard.TargetProperty="Color"
To="Yellow" />
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ColorAnimation Storyboard.TargetName="BorderBrush"
Storyboard.TargetProperty="Color"
To="Black"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border.BorderBrush>
<SolidColorBrush x:Name="BorderBrush" Color="White"/>
</Border.BorderBrush>
</Border>
</ControlTemplate>