I read mostly all of this article, but I just can't find out how I can change e.g. the entrance theme transition of the MenuFlyout, like it appears in the calender app. There is something like a horizontal turn instead of the default Animation of the MenuFlyout.
<MenuFlyout>
<MenuFlyout.MenuFlyoutPresenterStyle>
<Style...../>
</MenuFlyout.MenuFlyoutPresenterStyle>
<MenuFlyoutItem Text="Test"/>
</MenuFlyout>
C#:
MenuFlyout mf = (MenuFlyout)this.Resources["AddButtonFlyout"];
mf.Placement = FlyoutPlacementMode.Bottom;
mf.ShowAt(this.CommandBar);
The MenuFlyout has a standard Style which is set for TargetType="MenuFlyoutPresenter" and can be found in ..\Program Files (x86)\Windows Phone Kits\8.1\Include\abi\Xaml\Design\generic.xaml (I will not copy/paste here because it's quite long). This Style defines a ControlTemplate which you can modify to set how the MenuFlyout behaves when it changes to BottomPortrait VisualState.
From what I can see in the Calendar app, the MenuFlyout kind of flips when you open it. In predefined Style it first displays the top border and then draws the rest from top to bottom.
So, first of all you need to copy the whole Style to your resources. Then you need to find the BottomPortrait VisualState and clear everything from the Storyboard to be able to define your own from scratch.
I'll use a PlaneProjection class - it give that sort of a 3D effect which is what you're looking for. I added it to the CenterBorder Border element and set the default value to -90. I set it to -90 because that means it's perpendicular to the screen and the MenuFlyout is therefore not visible when first shown.
// ... rest of the code
<Border x:Name="CenterBorder" FlowDirection="LeftToRight" BorderBrush="{TemplateBinding Background}">
<Border.Projection>
<PlaneProjection RotationX="-90"/>
</Border.Projection>
// ... rest of the code
The next (and final) step is to define the new Storyboard in BottomPortrait VisualState as mentioned earlier - and it's really simple:
// ... rest of the code
<VisualState x:Name="BottomPortrait">
<Storyboard>
<DoubleAnimation Duration="0:0:0.18"
To="0"
Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)"
Storyboard.TargetName="CenterBorder" />
</Storyboard>
</VisualState>
// ... rest of the code
It just animates the Border from -90 to 0 degrees in very short period of time, which makes it go from invisible to visible with a nice flip animation, which is what you're looking for.
The Style (with irrelevant parts omitted for brevity - you should still have them!):
<Style TargetType="MenuFlyoutPresenter">
<!-- OTHER PROPERTY SETTERS -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="MenuFlyoutPresenter">
<Border x:Name="OuterBorder" FlowDirection="LeftToRight" BorderBrush="{TemplateBinding BorderBrush}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="PlacementStates">
<VisualState x:Name="None" />
<VisualState x:Name="TopPortrait">
<!-- TOP PORTRAIT STORYBOARD -->
</VisualState>
<VisualState x:Name="BottomPortrait">
<Storyboard>
<DoubleAnimation Duration="0:0:0.18"
To="0"
Storyboard.TargetProperty="(UIElement.Projection).(PlaneProjection.RotationX)"
Storyboard.TargetName="CenterBorder" />
</Storyboard>
</VisualState>
<VisualState x:Name="LeftLandscape">
<!-- LEFT LANDSCAPE STORYBOARD -->
</VisualState>
<VisualState x:Name="RightLandscape">
<!-- RIGHT LANDSCAPE STORYBOARD -->
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border.RenderTransform>
<ScaleTransform x:Name="OuterScaleTransform" />
</Border.RenderTransform>
<Border x:Name="CenterBorder" FlowDirection="LeftToRight" BorderBrush="{TemplateBinding Background}">
<Border.Projection>
<PlaneProjection RotationX="-90"/>
</Border.Projection>
<StackPanel x:Name="InnerBorder" FlowDirection="{TemplateBinding FlowDirection}" Background="{TemplateBinding Background}">
<StackPanel.RenderTransform>
<ScaleTransform x:Name="InnerScaleTransform" />
</StackPanel.RenderTransform>
<ItemsPresenter x:Name="ItemsPresenter" Margin="{TemplateBinding Padding}" FlowDirection="{TemplateBinding FlowDirection}" />
</StackPanel>
</Border>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
EDIT:
Showing the MenuFlyout is best done on a frame.
MenuFlyout mf = (MenuFlyout)this.Resources["AddButtonFlyout"];
mf.Placement = FlyoutPlacementMode.Bottom;
Frame fr = Window.Current.Content as Frame;
mf.ShowAt(fr);
Related
I have a control template (which is the header component of a DevExpress NavBarGroup - in order to change the expand/collapse icon DevExpress requires to override the whole control template) with a button inside, which also has its own control template, which then contains a content control pointing to another control template with paths inside. Kinda like this in pseudo-Xaml:
<NavBarGroupHeader>
<ExplorerBarExpandButton>
<Path>
<SolidColorBrush/>
</Path>
</ExplorerBarExpandButton>
</NavBarGroupHeader>
Now, there are various things happening. The outer most template of the NavBarGroupHeader contains the problematic animation logic: As soon as the header gets hovered the Path's fill color is supposed to change. There are actually two buttons inside depending on the state of the NavBarGroupHeader using the same logic and looks, which is why I outsourced it into a template. The NavBarGroupHeader can be used with an ExplorerBarView or a NavPaneView, which then activates either of the buttons inside (the ExplorerBarExpandButton is used in this case and the NavPaneExpandButton gets ignored - but it's still there and viable).
<ControlTemplate TargetType="{x:Type dxn:NavBarGroupHeader}>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinition>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Common">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<!-- these two lines throw the exception -->
<ColorAnimation Storyboard.Target="{Binding ElementName=ArrowTop}" Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)" Duration="0" To="#201F35"/>
<ColorAnimation Storyboard.Target="{Binding ElementName=ArrowBottom}" Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)" Duration="0" To="#201F35"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroup>
<!-- This shows a text within the header -->
<dxn:ImageAndTextContentPresenter/>
<dxn:ExplorerBarExpandButton x:Name="ExplorerBarExpandButton"
Grid.Column="1"
Template="{StaticResource GroupBoxExpandButtonTemplate}"/>
<dxn:NavPaneExpandButton x:Name="NavPaneExpandButton"
Grid.Column="1"
Template="{StaticResource GroupBoxExpandButtonTemplate}"/>
</Grid>
</ControlTemplate>
The ExplorerBarExpandButton contains two icons, looking the same, but one is flipped upside down with a LayoutTransform, which is why I outsorced the path into its own template. It also contains the logic of showing and hiding either of the buttons depending on whether the NavBarGroup is expanded or collapsed, which changes if you click the header (this is predetermined by DevExpress and works fine).
<ControlTemplate x:Key="GroupBoxExpandButtonTemplate">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
</VisualStateGroup>
<VisualStateGroup x:Name="ExpandStates">
<VisualState x:Name="Expanded"/>
<VisualState x:Name="Collapsed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphClose" Storyboard.TargetProperty="(Control.Visibility)" dxcn:ValueSetter.Visibility="Collapsed"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphOpen" Storyboard.TargetProperty="(Control.Visibility)" dxcn:ValueSetter.Visibility="Visible"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentControl x:Name="GlyphClose"
Template="{StaticResource CollapseExpandIconTemplate}"/>
<ContentControl x:Name="GlyphOpen"
Template="{StaticResource CollapseExpandIconTemplate}">
<ContentControl.LayoutTransform>
<RotateTransform Angle="180"/>
</ContentControl.LayoutTransform>
</ContentControl>
</Grid>
</ControlTemplate>
The path is actually a StackPanel with two Paths inside (two arrows pointing in the same direction). Therefore I actually wanted to use a DynamicResource of a color but after hours of trying and searching I stumbled across this post explaining that it is not possible to access a resource inside the targeted element from within a storyboard. So, now I'm left with this:
<ControlTemplate x:Key="CollapseExpandIconTemplate">
<StackPanel>
<Path x:Name="ArrowTop" Stretch="Uniform" Fill="#80838F" Data="M 0,50 25,50 50,30 75,50 100,50 100,40 50,0 0,40 Z"/>
<Path x:Name="ArrowBottom" Stretch="Uniform" Fill="#80838F" Data="M 0,50 25,50 50,30 75,50 100,50 100,40 50,0 0,40 Z"/>
</StackPanel>
</ControlTemplate>
In multiple other posts (actually in everyone concerning this error) I've read that the declaration of a SolidColorBrush in the path is missing but adding it doesn't change anything. I still get the same error with this:
</Path ...>
<Path.Fill>
<SolidColorBrush Color="#80838F"/>
</Path.Fill>
</Path>
I also tried using dynamic resources everywhere but it showed the same exception. I tried using DoubleAnimation for the different color parts but that throws another exception (with and without brackets around the R):
Cannot resolve all property references in the property path '(0).(1).(R)'. Verify that applicable objects support the properties.
I tried to change the path because I wasn't sure whether it was actually correct but nothing worked.
Now I'm out of ideas. How can I get this storyboard to work?
EDIT:
I've tried another thing, reducing the Xaml to the simplest basics, eliminating all duplicate elements and merging all the templates together without using any resources, changing background color from the outermost element and then step by step to the innermost until I've reached the path's template, then moving the templates back into resources.
This is the Xaml outcome:
<ControlTemplate TargetType="{x:Type dxn:NavBarGroupHeader}>
<Grid x:Name="HeaderRoot">
<Grid.Background>
<SolidColorBrush Color="White"/>
</Grid.Background>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinition>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="Common">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.Target="{Binding ElementName=HeaderRoot}" Storyboard.TargetProperty="(Grid.Background).(SolidColorBrush.Color)" Duration="0" To="#201F35"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroup>
<!-- This shows a text within the header -->
<dxn:ImageAndTextContentPresenter/>
<dxn:ExplorerBarExpandButton x:Name="ExplorerBarExpandButton"
Grid.Column="1">
<dxn:ExplorerBarExpandButton.Template>
<ControlTemplate x:Key="GroupBoxExpandButtonTemplate">
<Grid x:Name="ExpandButtonRoot">
<Grid.Background>
<SolidColorBrush Color="White"/>
</Grid.Background>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
</VisualStateGroup>
<VisualStateGroup x:Name="ExpandStates">
<VisualState x:Name="Expanded"/>
<VisualState x:Name="Collapsed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphClose" Storyboard.TargetProperty="(Control.Visibility)" dxcn:ValueSetter.Visibility="Collapsed"/>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphOpen" Storyboard.TargetProperty="(Control.Visibility)" dxcn:ValueSetter.Visibility="Visible"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentControl x:Name="GlyphClose">
<ContentControl.Template>
<ControlTemplate>
<StackPanel x:Name="PathRoot">
<Path x:Name="ArrowBottom" Stretch="Uniform" Fill="#80838F" Data="M 0,50 25,50 50,30 75,50 100,50 100,40 50,0 0,40 Z"/>
</StackPanel>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
</Grid>
</ControlTemplate>
</dxn:ExplorerBarExpandButton.Template>
</dxn:ExplorerBarExpandButton>
</Grid>
</ControlTemplate>
In this case there is no exception thrown and the first part worked fine. The whole header background changes when I hover over it. Then I've changed the line to:
<ColorAnimation Storyboard.Target="{Binding ElementName=ExpandButtonRoot}" Storyboard.TargetProperty="(Grid.Background).(SolidColorBrush.Color)" Duration="0" To="#201F35"/>
And oddly enough, still the whole header background gets changed upon hovering over it instead of just the button's background. I don't know what's going on here.
I'm trying to animate a button so the image enlarges when the user touches it and goes back to normal when he releases It but somehow the XAML Defined StoryBoard throws a nullpointer when called in C#. My code snippets are down here, I've got no clue at all what creates this problem.
XAML:
<UserControl x:Class="Mavie_Base.GUIElements.MenuButton"
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"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="200" d:DesignWidth="140">
<UserControl.Resources>
<Storyboard x:Name="Enlarge">
<DoubleAnimation Duration="0:0:1" From="60" To="66" Storyboard.TargetProperty="Height" Storyboard.TargetName="Rectangle"/>
</Storyboard>
<Storyboard x:Name="Normalize">
<DoubleAnimation Duration="0:0:1" From="66" To="60" Storyboard.TargetProperty="Height" Storyboard.TargetName="Rectangle"/>
</Storyboard>
</UserControl.Resources>
....
</UserControl>
C#:
private void IconContainer_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
Enlarge.Begin();
}
private void IconContainer_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
Normalize.Begin();
}
+1 #thumbmunkeys
Though if it were me, it sounds like something you might want to add other stuff to later for interaction so I'd just let functionality already built in to handle that and then have room to add to it later if you want to add other stuff to its functionality. I'd also think (not positive, would have to check) if you go tinkering with the height property it may offset other artifacts layed out on the screen as it adjusts causing for a jumpy UI that wouldn't be very pleasing to the user.
Again, if it were me, I would probably just do something like this instead. First, instead of fiddling with the Height/Width properties that might push other parts of the screen around, hit the ScaleTransform.ScaleY (and ScaleX respectively, you'll want to play with the Values I'm sure to get the sizes you want.) for your size changes.
I'd also probably go ahead and make it into a "lookless" and stripped down Button for other options later and to handle my events like Pressed. Something like;
<Style x:Key="GrowOnPressedThingy" TargetType="Button">
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid x:Name="Container"
RenderTransformOrigin="0.5,0.5">
<Grid.RenderTransform>
<TransformGroup>
<ScaleTransform />
<SkewTransform />
<RotateTransform />
<TranslateTransform />
</TransformGroup>
</Grid.RenderTransform>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<!-- Just left for reference -->
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver"/>
<VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Container"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
<EasingDoubleKeyFrame KeyTime="0:0:0.01" Value="1.05" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Container"
Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
<EasingDoubleKeyFrame KeyTime="0:0:0.01" Value="1.05" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled"/>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused"/>
<VisualState x:Name="Unfocused" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentControl x:Name="contentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
IsTabStop="False"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This way you could just do a quick... ;
<Button Content="whatever/image/path/blah.png"
Style="{StaticResource GrowOnPressedThingy}"/>
...wherever you need it, and you've got it setup to go easily add other neat UI tricks you might think of in the future. Just a shot in the dark, but maybe worth considering.
Anyway, hope this helps. Cheers
Try casting the targetproperty:
Storyboard.TargetProperty="(FrameworkElement.Height)"
I have a custom button-style with a ColorAnimation.
This works fine, but when pressed multiple times repeatedly, it stays stuck on the target color.
<Style TargetType="Button" x:Key="mainButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
<ContentPresenter Content="{TemplateBinding ContentControl.Content}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Duration="0:0:0.10"
Storyboard.TargetProperty="(Foreground).(SolidColorBrush.Color)"
To="Red"
AutoReverse="True"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
How can I resolve this ?
Update
Yeh if you cannot afford to remove the Storyboard in Trigger.ExitActions then you do indeed have to address the From issue for intermediate starting Storyboard's yourself.
However specifying a hard-coded From isn't the only solution. You can let the animation reset itself to the underlying base color when it's starting up.
The benefit of this is by not specifying a From you got one less thing to keep track of with future updates.
<Storyboard AutoReverse="True">
<!-- By not specifying a To or From we pretty much reset the property to un-animated state(Exactly what the hard-coded from does) -->
<ColorAnimation Duration="0:0:0"
Storyboard.TargetProperty="(Foreground).(SolidColorBrush.Color)" />
<!-- This part is same as original time to kick into new Foreground as desired -->
<ColorAnimation Duration="0:0:1.5"
Storyboard.TargetProperty="(Foreground).(SolidColorBrush.Color)"
To="Red" />
</Storyboard>
You have not set the From property on your ColorAnimation. So when you press the button in the middle of its animation, the Storyboard takes the current Foreground color value as its From, and this is the color that the animation reverses back to.
Now when you repeatedly press the button, the From color moves closer and closer to red, giving the impression that the color is stuck on red.
Update:
This answer only points out the problem. Refer to Viv's answer for an elegant solution
I've ported a silverlight TemplatedControl to WPF, and while it behaves mostly the same, the animations no longer work.
When I call VisualStateManager.GoToState() it returns false. In an attempt to force this manually, I followed someone elses recommendation and looked up the the visual state group by name, and force the storyboard to run:
foreach (VisualStateGroup vsg in VisualStateManager.GetVisualStateGroups(part_LayoutRoot))
{
VisualState vs = vsg.States.Cast<VisualState>().FirstOrDefault(o => o.Name == visualState);
if (vs == null)
throw new ApplicationException("No visual state found with name: " + visualState);
vs.Storyboard.Begin();
break;
}
However this throws the exceptionNo applicable name scope exists to resolve the name 'PART_MyPart'
when I call Storyboard.Begin().
Upon further investigation, the VisualStateManager, visual states and storyboards all return null when NameScope.GetNameScope() is called on them, so I attempted to set them manually in code as well:
var nameScope = NameScope.GetNameScope(vs.Storyboard);
if (nameScope == null)
{
nameScope = NameScope.GetNameScope(part_LayoutRoot);
NameScope.SetNameScope(vsg, nameScope);
NameScope.SetNameScope(vs, nameScope);
NameScope.SetNameScope(vs.Storyboard, nameScope);
}
However, the exception continues to get raised, and I can't for the life of me think why. This works exactly as expected in silverlight.
Can anyone shed any light on why there would be different behavior between silverlight and WPF with regards to NameScope?
Thanks
Retrieve a named element within your control template and call storyboard.Begin(_retrievedElement) instead of just storyboard.Begin() to solve the "No applicable name scope" problem. The name scope of the passed framework element will be applied to the storyboard in that case. See http://msdn.microsoft.com/en-us/library/system.windows.media.animation.storyboard.begin.aspx for further information on the different overloads of this method.
Unfortunately, I cannot reconstruct why the Visual States Manager does not work for your control.
Update: I might have figured out why your control templates work in Silverlight, but not in WPF. Details in the next answer.
I also might have a clue why your ported control template does not work: in WPF, it is not possible to use bindings in storyboards within visual states. Consider the following template:
<ControlTemplate x:Key="ButtonTemplate" TargetType="Button">
<Border x:Name="ButtonBorder" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="5" Background="White" BorderBrush="#FFB0B0B0">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.3"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<ColorAnimation Storyboard.TargetName="rectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
To="{Binding (Border.BorderBrush).(SolidColorBrush.Color), ElementName=ButtonBorder}"
Duration="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed"/>
<VisualState x:Name="Disabled"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Rectangle x:Name="rectangle" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="#00000000"/>
<ContentPresenter x:Name="ContentPresenter" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
</Border>
</ControlTemplate>
The important part is the Visual State "MouseOver" where an element binding is used: this code will execute in Silverlight perfectly well while in WPF the data binding engine will tell you that it cannot find the element with the name "ButtonBorder" (look at the Output window to trace data binding errors). This has to do with the namespace problem I mentioned in my earlier answer.
A workaround for this problem would be like this:
<ControlTemplate x:Key="ButtonTemplate" TargetType="Button">
<Border x:Name="ButtonBorder" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="5" Background="White" BorderBrush="#FFB0B0B0">
<Border.Resources>
<Storyboard x:Key="MouseOverStoryboard">
<ColorAnimation Storyboard.TargetName="rectangle" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
To="{Binding (Border.BorderBrush).(SolidColorBrush.Color), ElementName=ButtonBorder}"
Duration="0"/>
</Storyboard>
</Border.Resources>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.3"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver" Storyboard="{StaticResource MouseOverStoryboard}">
</VisualState>
<VisualState x:Name="Pressed"/>
<VisualState x:Name="Disabled"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Rectangle x:Name="rectangle" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="#00000000"/>
<ContentPresenter x:Name="ContentPresenter" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
</Border>
</ControlTemplate>
Now, the storyboard is added to the Resource property of the root template element and thus the name scope seems to fit. In the visual state, just use the Static Resource Markup Extension to reference the storyboard. Note that this XAML will not run in Silverlight, so you cannot share the file in both projects. Also, Blend is not able to process this markup so this would have to be written by hand.
Hope this helps.
I've created a WPF Storyboard animation on an image in Expression Blend 4. On hover, the image gradually blurs. Is there any way I can have the Storyboard be undone or reversed when the mouse leaves the image? I could make it trigger Storyboard.Remove() but that wouldn't actually play through the Storyboard backwards.
Is there any way I can accomplish that within Expression Blend 4?
Since you are using Blend, you should take advantage of Blend's support for the VisualStateManager. All you have to do is describe what the object looks like in its various states, like MouseOver and Normal and how long the transitions between various states are, and the visual state manager works out how to transition between states.
An image doesn't have any visual states but you can edit a Button template and make its content an image and then edit the states for the button. I've done this and cleaned up the XAML to demonstrate the technique:
<Grid>
<Grid.Resources>
<Style x:Key="ButtonStyle1" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Image x:Name="image" Height="100" Width="Auto" Source="http://thecybershadow.net/misc/stackoverflow.png" Margin="0,0,-25,0">
<Image.Effect>
<DropShadowEffect ShadowDepth="0"/>
</Image.Effect>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0:0:0.5"/>
</VisualStateGroup.Transitions>
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Effect).(DropShadowEffect.ShadowDepth)" Storyboard.TargetName="image">
<EasingDoubleKeyFrame KeyTime="0" Value="15"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed"/>
<VisualState x:Name="Disabled"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Image>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Button Style="{StaticResource ButtonStyle1}"/>
</Grid>
Note that Blend does all this for you but understanding the XAML will help. Here's a Blend-oriented tutorial:
MORE ARTICLES ON VISUAL STATE MANAGER
Maybe the solution is on this Answer:
How to animate ListBox Items on MouseEnter and MouseLeave events using C#/WPF?