Hello,
I'm new to WPF animation (although an old hand at WPF in general), and am getting very confused, as there seem to be a lot of different ways of doing things, and a lot of poor explanations of how to do them! I want to do something apparently simple, but am struggling.
First, I'm not sure of the best way to approach animations. Ideally, I would like to do as much as I can in XAML, and make it as generic as possible, as then I can add the XAML as a resource in App.xaml, and reuse the animations wherever I like. All of the samples I've seen seem to be tied too closely to the task in hand.
Second problem is that I see storyboards, visual states, triggers, behaviours and more, and am lost as to which I should be using.
I tried a very simple sample to get the feel of what's going on. I have a window that has a listbox, containing two Thing objects (couldn't think of a better name!), each of which has a bool property which indicates if the stackpanel below the listbox should be visible or not. There is a purple rectangle below all that to fill up the rest of the window. Here is the XAML...
<Window x:Class="VisualStateManagerTest.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"><Window.Resources><Style x:Key="VisibilityChangedStyle" TargetType="{x:Type FrameworkElement}"><Style.Triggers><Trigger Property="Visibility" Value="Visible"><Trigger.EnterActions><BeginStoryboard><Storyboard><DoubleAnimation Storyboard.TargetProperty="Height" From="0" To="30" Duration="0:0:0.3" /></Storyboard></BeginStoryboard></Trigger.EnterActions></Trigger><Trigger Property="Visibility" Value="Collapsed"><Trigger.EnterActions><BeginStoryboard><Storyboard><DoubleAnimation Storyboard.TargetProperty="Height" From="30" To="0" Duration="0:0:0.3" /></Storyboard></BeginStoryboard></Trigger.EnterActions><Trigger.ExitActions><BeginStoryboard><Storyboard><DoubleAnimation Storyboard.TargetProperty="Height" From="0" To="30" Duration="0:0:0.3" /></Storyboard></BeginStoryboard></Trigger.ExitActions></Trigger></Style.Triggers></Style></Window.Resources><Grid><Grid.RowDefinitions><RowDefinition Height="Auto" /><RowDefinition Height="Auto" /><RowDefinition Height="*" /></Grid.RowDefinitions><ListBox Name="_listBox" DisplayMemberPath="Name" /><StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="3" Visibility="{Binding ElementName=_listBox, Path=SelectedItem.Visible, Converter={StaticResource BoolToVisibilityVC}, ConverterParameter=True}" Style="{StaticResource VisibilityChangedStyle}" Grid.Row="1"><Button Content="Save" Margin="3" /><Button Content="Apply" Margin="3" /><Button Content="Cancel" Margin="3" /></StackPanel><Rectangle Grid.Row="2" Fill="BlueViolet" Margin="3" /></Grid></Window>
The code-behind for this just contains a constructor that creates the Thing objects...
public MainWindow() { InitializeComponent(); _listBox.ItemsSource = new List<Thing> { new Thing {Name = "Visible thing", Visible = true}, new Thing {Name = "Non-visible thing", Visible = false}, }; _listBox.SelectedIndex = 0; }
The BoolToVisibilityVC value converter just converts the bool value from the Thing object to a Visibility...
public object Convert(object ValueFromDataBinding, Type TargetType, object ValueConverterParameter, CultureInfo Culture) { if (ValueConverterParameter != null) { bool boolFromDataBinding; bool boolParameter; if (bool.TryParse(ValueFromDataBinding.ToString(), out boolFromDataBinding) && bool.TryParse(ValueConverterParameter.ToString(), out boolParameter)) { string res = (boolFromDataBinding == boolParameter ? "Visible" : "Collapsed"); Debug.WriteLine(DateTime.Now.ToLongTimeString() + ": boolFromDataBinding: " + boolFromDataBinding + ", boolParameter: " + boolParameter + ", res: " + res); return res; } } return null; }
This partially works, in that when you click on the visible thing, the stackpanel animates into view very nicely. However, when you click the non-visible thing, it just disappears. My guess is that the Visibility is being set before the animation runs, so you don't see it. I've searched around for hours, but found out how to fix this.
One big problem is that I don't really understand what's going on here. I think I have the basic idea of a style that includes triggers, and those triggers are fired when some change happens (in my case, when the Visibility property changes), but I'm not sure what the EnterActions and ExitActions do. I tried fiddling around with these, but got very confused. I haven't found a clear explanation of their purpose anywhere.
Another problem with this is that it requires me to hard-code the target height, which apart from being a pain (I'm used to letting WPF decide how high these things should be), it also means I can't reuse the animation as a resource. I would like to say"Animate it to the height it would be when visible" so I could reuse it anywhere.
So, how would I get this to work, but be reusable? I've spent an amazing amount of time on this, and can't believe it's so hard to find a clear explanation of which approach to use, and how to do it. I would be really grateful if anyone can help me out.
Thanks for any help you can give.
FREE custom controls for Lightswitch! A collection of useful controls for Lightswitch developers. Download from the Visual Studio Gallery.
If you're really bored, you could read about my experiments with .NET and some of Microsoft's newer technologies athttp://dotnetwhatnot.pixata.co.uk/