How can I target multiple UIElements with VisualStateManager? - silverlight

If I do something like this to change the opacity of an Ellipse:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStateGroup">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:1">
<VisualTransition.GeneratedEasingFunction>
<CircleEase EasingMode="EaseIn"/>
</VisualTransition.GeneratedEasingFunction>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="Lit"/>
<VisualState x:Name="Unlit">
<Storyboard>
<DoubleAnimation Duration="0" To="0.225"
Storyboard.TargetProperty="(UIElement.Opacity)"
Storyboard.TargetName="ellipse" d:IsOptimized="True"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Ellipse x:Name="ellipse" Width="100" Height="100" Fill="Azure"/>
VisualStateManager.GoToState(this, "Unlit", true);
It works just fine.
But let's say I have a handful of Ellipses and want to apply the same StoryBoard to them, how do I do that?
<Ellipse x:Name="ellipse1" Width="100" Height="100" Fill="Azure"/>
<Ellipse x:Name="ellipse2" Width="100" Height="100" Fill="Azure"/>
<Ellipse x:Name="ellipse3" Width="100" Height="100" Fill="Azure"/>
One way would be to define multiple DoubleAnimations to the same StoryBoard:
<Storyboard>
<DoubleAnimation Duration="0" To="0.225"
Storyboard.TargetProperty="(UIElement.Opacity)"
Storyboard.TargetName="ellipse1"
d:IsOptimized="True"/>
<DoubleAnimation Duration="0" To="0.225"
Storyboard.TargetProperty="(UIElement.Opacity)"
Storyboard.TargetName="ellipse2"
d:IsOptimized="True"/>
</Storyboard>
But this is somewhat cumbersome when I have a dynamic number of ellipses.
Is there any more elegant way?

I'm not sure about elegant, but one way could be to animate an intermediate property, and bind the real target properties to that:
<Grid x:Name="animationTarget" Visibility="Collapsed" />
<Ellipse x:Name="ellipse1" Width="100" Height="100" Fill="Azure"
Opacity={Binding Opacity, ElementName=animationTarget}/>
<Ellipse x:Name="ellipse2" Width="100" Height="100" Fill="Azure"
Opacity={Binding Opacity, ElementName=animationTarget}/>
and
<Storyboard>
<DoubleAnimation Duration="0" To="0.225"
Storyboard.TargetProperty="(UIElement.Opacity)"
Storyboard.TargetName="animationTarget" />
</Storyboard>
(If using an invisible extra element as an intermediate binding target doesn't seem nice it could instead be on some attached property on whatever container your ellipses are in)

Related

WPF Change Path Data in storyboard

So I have a path setup like this
<Grid x:Name="arrowPanel">
<Path x:Name="arrow" Data="M0,4 H8 M4,4 V8Z" Stroke="Black" StrokeThickness="2" Height="8" RenderTransformOrigin="0.5,0.5" Stretch="None" Width="8">
<!--Not sure what to do here -->
</Path>
</Grid>
I have a storyboard setup like this
<VisualStateGroup x:Name="ExpandStateGroup">
<VisualState x:Name="Expanded">
<Storyboard>
<!-- Something to change the data to this 'M0,5 H10'(A minus sign)-->
</Storyboard>
</VisualState>
<VisualState x:Name="Collapsed">
<Storyboard>
<!-- Something to change the data back to it's original 'M0,5 H10 M5,5 V10Z' (A plus sign)-->
</Storyboard>
</VisualState>
</VisualStateGroup>
I do not know how to alter the data aspect of the path to make these transformations.
You could use an ObjectAnimationUsingKeyFrames to "animate" the Data property of the Path:
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0"
Storyboard.TargetName="arrow" Storyboard.TargetProperty="Data">
<DiscreteObjectKeyFrame>
<DiscreteObjectKeyFrame.Value>
<Geometry>M0,5 H10</Geometry>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
In order to get the results I was looking for I created two paths that occupied the same area. One with the plus sign and the other with the minus sign (The plus sign without the vertical path).
Object:
<Grid>
<Grid x:Name="plusSignPanel">
<!-- Minus -->
<Path Data="M0,4 H8" Stroke="Red" StrokeThickness="2" Height="8" RenderTransformOrigin="0.5,0.5" Stretch="None" Width="8"/>
<!-- Plus -->
<Path x:Name="plusSign" Data="M0,4 H8 M4,4 V8Z" Stroke="Black" StrokeThickness="2" Height="8" RenderTransformOrigin="0.5,0.5" Stretch="None" Width="8"/>
</Grid>
</Grid>
And then all I did was change the color from black to transparent using the coloranimation on the plus sign for expanded state and vice versa with the collapsed state:
Animation:
<VisualStateGroup x:Name="ExpandStateGroup">
<VisualState x:Name="Expanded">
<Storyboard>
<ColorAnimation Duration="0:0:0.1" Storyboard.TargetName="plusSign" Storyboard.TargetProperty="(Path.Stroke).(SolidColorBrush.Color)" To="Transparent"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Collapsed">
<Storyboard>
<ColorAnimation Duration="0:0:0.1" Storyboard.TargetName="plusSign" Storyboard.TargetProperty="(Path.Stroke).(SolidColorBrush.Color)" To="Black"/>
</Storyboard>
</VisualState>
</VisualStateGroup>

Change text when VisualState is set to "PointerOver",

I am working on ControlTemplate for a Button.
This is my current code which changes button's color.
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1.Styles">
<ControlTemplate x:Key="ButtonBrushAnimation" TargetType="Button">
<Grid >
<TextBlock x:Name="textBlock" Width="80" Height="30" Text="AAA" />
<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="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>
</ResourceDictionary>
I want to change text property of textBlock control on PointerOver event.
How can I do it?
Thank you!
Add StringAnimationUsingKeyFrames to your Storyboard like this:
<Storyboard>
<ColorAnimation Storyboard.TargetName="ButtonBrush" Storyboard.TargetProperty="Color" To="Red" />
<StringAnimationUsingKeyFrames Storyboard.TargetName="textBlock" Storyboard.TargetProperty="Text">
<DiscreteStringKeyFrame Value="BBB" KeyTime="0:0:0" />
</StringAnimationUsingKeyFrames>
</Storyboard>
You can set a KeyTime property value depending on when exactly you need to change the text.

Is there a way to change the selected row background color of Silverlight DataGrid?

I haven't found anything that works.
This is my code:
<data:DataGrid x:Name="dgSample"
Grid.Row="2"
CanUserSortColumns="True"
CanUserResizeColumns="False"
HorizontalScrollBarVisibility="Hidden"
IsReadOnly="True"
AutoGenerateColumns="False"
HorizontalAlignment="{Binding Path=App.TextAlignment,
Source={StaticResource LocalizedStrings}}"
Foreground="Black"
FontWeight="Normal"
FlowDirection="{Binding Path=App.FlowDirection,
Source={StaticResource LocalizedStrings}}"
AreRowDetailsFrozen="True"
LoadingRow="dgSample_LoadingRow">
You need to edit the Template, here's an example of a RowStyle you can use on a DataGrid. Tweak the colours and mouseovers to your requirements.
Use it like this ...
<sdk:DataGrid RowStyle="{StaticResource BetterHighlightedDataGridRowStyle}" />
Define it like this ...
<Style x:Key="BetterHighlightedDataGridRowStyle" TargetType="sdk:DataGridRow">
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="sdk:DataGridRow">
<sdk:DataGridFrozenGrid x:Name="Root">
<sdk:DataGridFrozenGrid.Resources>
<Storyboard x:Key="DetailsVisibleTransition">
<DoubleAnimation Duration="00:00:0.1" Storyboard.TargetProperty="ContentHeight" Storyboard.TargetName="DetailsPresenter"/>
</Storyboard>
</sdk:DataGridFrozenGrid.Resources>
<sdk:DataGridFrozenGrid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</sdk:DataGridFrozenGrid.ColumnDefinitions>
<sdk:DataGridFrozenGrid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</sdk:DataGridFrozenGrid.RowDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="NormalAlternatingRow">
<Storyboard>
<DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BackgroundRectangle"/>
</Storyboard>
</VisualState>
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation Duration="0" To=".5" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BackgroundRectangle"/>
</Storyboard>
</VisualState>
<VisualState x:Name="NormalSelected">
<Storyboard>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BackgroundRectangle"/>
<ColorAnimation Duration="0" To="#DDE9EE33" Storyboard.TargetProperty="(Fill).Color" Storyboard.TargetName="BackgroundRectangle"/>
</Storyboard>
</VisualState>
<VisualState x:Name="MouseOverSelected">
<Storyboard>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BackgroundRectangle"/>
<ColorAnimation Duration="0" To="#E1C8C864" Storyboard.TargetProperty="(Fill).Color" Storyboard.TargetName="BackgroundRectangle"/>
</Storyboard>
</VisualState>
<VisualState x:Name="UnfocusedSelected">
<Storyboard>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="BackgroundRectangle"/>
<ColorAnimation Duration="0" To="#DDE9EE33" Storyboard.TargetProperty="(Fill).Color" Storyboard.TargetName="BackgroundRectangle"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="ValidationStates">
<VisualState x:Name="Valid"/>
<VisualState x:Name="Invalid">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="Visibility" Storyboard.TargetName="BackgroundRectangle">
<DiscreteObjectKeyFrame KeyTime="0" Value="Collapsed"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="InvalidVisualElement"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle x:Name="BackgroundRectangle" Grid.ColumnSpan="2" Fill="#FFBADDE9" Opacity="0" Grid.RowSpan="2"/>
<Rectangle x:Name="InvalidVisualElement" Grid.ColumnSpan="2" Fill="#FFF7D8DB" Opacity="0" Grid.RowSpan="2"/>
<sdk:DataGridRowHeader x:Name="RowHeader" sdk:DataGridFrozenGrid.IsFrozen="True" Grid.RowSpan="3"/>
<sdk:DataGridCellsPresenter x:Name="CellsPresenter" Grid.Column="1" sdk:DataGridFrozenGrid.IsFrozen="True"/>
<sdk:DataGridDetailsPresenter x:Name="DetailsPresenter" Grid.Column="1" Grid.Row="1"/>
<Rectangle x:Name="BottomGridLine" Grid.ColumnSpan="2" Fill="Black" HorizontalAlignment="Stretch" Height="2" Grid.Row="2"/>
</sdk:DataGridFrozenGrid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Template the content control for multiple types - radio button and tabitem

So, what I set out to do is template my radio buttons and tab items to look the same so that they look the same but can act differently. SoIi created a Control template and targeted it to the Content Control type and used it as a static resource to template the two types.
Everything works except i cant figure out how to set the header to display as the label. The 'Content' Property picks up the radio button label text, but when I try binding to the 'Header' property I fail at getting the label text.
Here is the content control Control Template Xaml:
<!-- Tab Selector Style -->
<ControlTemplate x:Key="TabSelectorStyle" TargetType="ContentControl">
<Grid x:Name="Root" HorizontalAlignment="Stretch">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates" >
<VisualState x:Name="Normal" >
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Box" Storyboard.TargetProperty="Fill">
<ObjectAnimationUsingKeyFrames.KeyFrames>
<DiscreteObjectKeyFrame KeyTime="0:0:0.1">
<DiscreteObjectKeyFrame.Value>
<SolidColorBrush Color="#FF323232"/>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames.KeyFrames>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="Box" Storyboard.TargetProperty="StrokeThickness" To="1" Duration="0:0:0.1" />
</Storyboard>
</VisualState>
<VisualState x:Name="MouseOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Box" Storyboard.TargetProperty="Fill">
<ObjectAnimationUsingKeyFrames.KeyFrames>
<DiscreteObjectKeyFrame KeyTime="0:0:0.3">
<DiscreteObjectKeyFrame.Value>
<SolidColorBrush Color="#FF326699"/>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames.KeyFrames>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Storyboard.TargetName="Box" Storyboard.TargetProperty="StrokeThickness" To="1.5" Duration="0:0:0.3" />
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates" >
<VisualState x:Name="Selected" >
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Check" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.3" />
</Storyboard>
</VisualState>
<VisualState x:Name="Unselected" >
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Check" Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.3" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch" Margin="{Binding Path=Margin}">
<Grid Margin="5,2,5,2" HorizontalAlignment="Center">
<Rectangle x:Name="Box"
Width="10" Height="10"
Fill="#FF323232" Stroke="#FFFFFFFF" StrokeThickness="1"
VerticalAlignment="Center" HorizontalAlignment="Center"/>
<Rectangle x:Name="Check"
Width="8" Height="8"
Fill="#FFFFFFFF" Opacity="0"
VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
<TextBlock x:Name="RadioButtionTitle" Foreground="#FFFFFFFF" Text="{TemplateBinding Content}" HorizontalAlignment="Stretch" Width="Auto" Margin="5,2,5,2"/>
<!-- F I X E D H E R E -->
<TextBlock x:Name="TabitemTitle" Foreground="#FFFFFFFF" Text="{Binding Header, RelativeSource={RelativeSource FindAncestor, AncestorType=presentcontrols:TabItem}}" HorizontalAlignment="Stretch" Width="Auto" Margin="5,2,5,2"/>
<!-- =================== -->
</StackPanel>
</Grid>
</ControlTemplate>

How can I add the validation style to all my custom controls and still be DRY?

I'm currently building some custom controls in Silverlight. I want these controls to respond to validation errors. What I'm trying to do is to get that red border around my control, just like the default Silverlight controls.
What I understand is that I need to add this to my template:
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ValidationStates">
<VisualState x:Name="Valid"/>
<VisualState x:Name="InvalidUnfocused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationErrorElement" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="InvalidFocused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationErrorElement" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="validationTooltip" Storyboard.TargetProperty="IsOpen">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<sys:Boolean>True</sys:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="ValidationErrorElement" BorderThickness="1" CornerRadius="1" BorderBrush="#FFDB000C" Visibility="Collapsed">
<ToolTipService.ToolTip>
<ToolTip x:Name="validationTooltip" Template="{StaticResource ValidationToolTipTemplate}" Placement="Right"
PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}"
DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<ToolTip.Triggers>
<EventTrigger RoutedEvent="Canvas.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="validationTooltip" Storyboard.TargetProperty="IsHitTestVisible">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<sys:Boolean>true</sys:Boolean>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</ToolTip.Triggers>
</ToolTip>
</ToolTipService.ToolTip>
<Grid Width="12" Height="12" HorizontalAlignment="Right" Margin="1,-4,-4,0" VerticalAlignment="Top" Background="Transparent">
<Path Margin="1,3,0,0" Data="M 1,0 L6,0 A 2,2 90 0 1 8,2 L8,7 z" Fill="#FFDC000C"/>
<Path Margin="1,3,0,0" Data="M 0,0 L2,0 L 8,6 L8,8" Fill="#ffffff"/>
</Grid>
</Border>
and
<ControlTemplate x:Key="ValidationToolTipTemplate">
<Grid x:Name="Root" Margin="5,0" RenderTransformOrigin="0,0" Opacity="0">
<Grid.RenderTransform>
<TranslateTransform x:Name="xform" X="-25"/>
</Grid.RenderTransform>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="OpenStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0"/>
<VisualTransition To="Open" GeneratedDuration="0:0:0.2">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="xform" Storyboard.TargetProperty="X" To="0" Duration="0:0:0.2">
<DoubleAnimation.EasingFunction>
<BackEase Amplitude=".3" EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.2"/>
</Storyboard>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="Closed">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="0" Duration="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Open">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="xform" Storyboard.TargetProperty="X" To="0" Duration="0"/>
<DoubleAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border Margin="4,4,-4,-4" Background="#052A2E31" CornerRadius="5"/>
<Border Margin="3,3,-3,-3" Background="#152A2E31" CornerRadius="4"/>
<Border Margin="2,2,-2,-2" Background="#252A2E31" CornerRadius="3"/>
<Border Margin="1,1,-1,-1" Background="#352A2E31" CornerRadius="2"/>
<Border Background="#FFDC000C" CornerRadius="2"/>
<Border CornerRadius="2">
<TextBlock
UseLayoutRounding="false"
Foreground="White" Margin="8,4,8,4" MaxWidth="250" TextWrapping="Wrap" Text="{Binding (Validation.Errors)[0].ErrorContent}"/>
</Border>
</Grid>
</ControlTemplate>
And I need to add this for every control that I create (with the exception of the ValidationToolTipTemplate-ControlTemplate).
How can I add the validation style to all my controls and still be DRY?
If that is not possible, does this mean that when the style of the validation template changes, I have to copy and paste the templates of all the controls in the known universe in a file called 'generic.xaml' and change it accordingly?
I've worked with Microsoft APIs long enough to know that (2) is probably the way to go, but I want to make sure first.
With the way that Silveright/WPF work there are some gotchas in these sorts of situations. You DO end up repeating XAML as ControlTemplates are all or nothing. You either end up replacing the Style's template or not. You can't pick and choose what to override like you do with a class.
I will try to answer your specific questions then try to provide a couple of broad approaches that should help to eliminate redundancy in XAML files.
How can you add the validation style to all controls?
First, take that border and make it a StaticResoruce by giving in a Key attribute. Then put it in a shared resource dictionary. When you need the style again you can use it in your templates like this:
<ContentControl x:Name="ValidationErrorElement" Content={StaticResource MyBorderResource}" />
You can't do much more than that for encapsulating the template. As far as the Validation VisualStates, those are as compact as possible already. What you can do however, is combine multiple templates into one larger template. I like to do this with ToggleButtons, Buttons and RadioButtons that look like buttons. See point #2 below.
General Rules of Thumb
Encapsulate commonly used XAML fragments into templated Controls and Control templates. For instance, let's say you have a border within a border that you're using in a few different places. That could be described as a ControlTemplate resource and loaded into a ContentControl.
If you have several styles or control templates that are largely the same (Button,ToggleButton,RadioButton come to mind), then you can apply the style to a common ancestor ButtonBase, in this case. Your new button ControlTemplate would contain all the VisualStates and graphic elements needed by Button,ToggleButton and RadioButton and be applied to the type ButtonBase. As noted above, this approach works well for very similar looking controls but would be a poor choice for controls that aren't that similar.

Resources