Overview
As we know Windows 8 Metro apps (or whatever they are called now) don't support trigger or behaviours like Silverlight and WPF apps do, even though they are XAML family. This means that once you have crafted an animation in XAML, you can't actually trigger it without code behind. This article demonstrates how to use an attached property which is toggled by a bool dependency property and controls a pair of animations.Usage
I have a common settings page which is opened via the charm bar which I was originally showing and hiding using a bool property in the view model and a visibility converter. I wanted to have an animation so that the control would slide in and out but wanted to control it through the view model.Dependency Property
This dependency property is fairly straight forward, it has 3 dependency properties, the first binds to a boolean property and the other two bind to a pair of opposing animations to control the slide in and out:
/// <summary>
/// This attached property
binds to a bool and a pair of StoryboardAnimations
/// When the bool is true,
the true animation is started
/// When the bool is false,
the fals animation is started
/// </summary>
public class BoolBeginStoryBoardTrigger
{
#region Bool
Property
public static readonly DependencyProperty BoolProperty =
DependencyProperty.RegisterAttached("Bool",
typeof(bool), typeof(BoolBeginStoryBoardTrigger), new PropertyMetadata(false,
BoolPropertyChanged));
public static void SetBool(DependencyObject attached, bool value)
{
attached.SetValue(BoolProperty, value);
}
public static bool GetBool(DependencyObject attached)
{
return (bool)attached.GetValue(BoolProperty);
}
private static void BoolPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
if (GetBool(d))
GetTrueStoryboard(d).Begin();
else
GetFalseStoryboard(d).Begin();
}
#endregion
#region
TrueStoryboard Property
public static readonly DependencyProperty TrueStoryboardProperty =
DependencyProperty.RegisterAttached("TrueStoryboard",
typeof(Storyboard), typeof(BoolBeginStoryBoardTrigger),
null);
public static void SetTrueStoryboard(DependencyObject attached,
Storyboard value)
{
attached.SetValue(TrueStoryboardProperty, value);
}
public static
Storyboard GetTrueStoryboard(DependencyObject attached)
{
return
(Storyboard)attached.GetValue(TrueStoryboardProperty);
}
#endregion
#region FalseStoryboard
Property
public static readonly DependencyProperty FalseStoryboardProperty =
DependencyProperty.RegisterAttached("FalseStoryboard",
typeof(Storyboard), typeof(BoolBeginStoryBoardTrigger),
null);
public static void SetFalseStoryboard(DependencyObject attached,
Storyboard value)
{
attached.SetValue(FalseStoryboardProperty, value);
}
public static
Storyboard GetFalseStoryboard(DependencyObject attached)
{
return
(Storyboard)attached.GetValue(FalseStoryboardProperty);
}
#endregion
}
The XAML
The attached property can be put anywhere after where the animations are defined. The 3 properties are set like this:
<Grid Style="{StaticResource LayoutRootStyle}"
cmd:BoolBeginStoryBoardTrigger.Bool="{Binding IsSettingsVisible}"
cmd:BoolBeginStoryBoardTrigger.FalseStoryboard="{StaticResource SettingsSlideIn}"
cmd:BoolBeginStoryBoardTrigger.TrueStoryboard="{StaticResource SettingsSlideOut}">
If you don't want to bind it to a View Model, it can easily bound to a toggle button in the view like this:
If you don't want to bind it to a View Model, it can easily bound to a toggle button in the view like this:
<Grid x:Name="settingsPanel"
local:BoolBeginStoryBoardTrigger.Bool="{Binding ElementName=toggleButton, Path=IsChecked}"
local:BoolBeginStoryBoardTrigger.FalseStoryboard="{StaticResource SettingsSlideIn}"
local:BoolBeginStoryBoardTrigger.TrueStoryboard="{StaticResource SettingsSlideOut}"
Complete Settings Panel XAML
This is bound to the Settings view model which controls the IsSettingsVisible property.
<UserControl
x:Class="WebberCross.Win8.Demo.View.SettingsPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WebberCross.Win8.Demo.Settings"
xmlns:cmd="using:WebberCross.Win8.Demo.Commanding"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Width="346"
HorizontalAlignment="Right"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="346"
x:Name="ThisControl"
Visibility="Collapsed"
DataContext="{Binding Settings, Source={StaticResource Locator}}">
<UserControl.RenderTransform>
<TranslateTransform x:Name="settingsTranslateX" X="346" />
</UserControl.RenderTransform>
<UserControl.Resources>
<Storyboard x:Key="SettingsSlideOut">
<DoubleAnimationUsingKeyFrames
Storyboard.TargetProperty="X"
Storyboard.TargetName="settingsTranslateX">
<EasingDoubleKeyFrame KeyTime="0:0:0" Value="346" />
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0" >
<EasingDoubleKeyFrame.EasingFunction>
<PowerEase EasingMode="EaseOut"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="Visibility"
Storyboard.TargetName="ThisControl">
<DiscreteObjectKeyFrame KeyTime="0:0:0" Value="Visible"
/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="SettingsSlideIn">
<DoubleAnimationUsingKeyFrames
Storyboard.TargetProperty="X"
Storyboard.TargetName="settingsTranslateX">
<EasingDoubleKeyFrame KeyTime="0:0:0" Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="346" >
<EasingDoubleKeyFrame.EasingFunction>
<PowerEase EasingMode="EaseIn"/>
</EasingDoubleKeyFrame.EasingFunction>
</EasingDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</UserControl.Resources>
<Grid Style="{StaticResource LayoutRootStyle}"
cmd:BoolBeginStoryBoardTrigger.Bool="{Binding IsSettingsVisible}"
cmd:BoolBeginStoryBoardTrigger.FalseStoryboard="{StaticResource SettingsSlideIn}"
cmd:BoolBeginStoryBoardTrigger.TrueStoryboard="{StaticResource SettingsSlideOut}">
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Back button and page title -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ToggleButton Style="{StaticResource
BackToggleButtonStyle}"
IsChecked="{Binding IsSettingsVisible, Mode=TwoWay}"/>
<TextBlock x:Name="pageTitle" Text="{Binding SettingsTitle}" Style="{StaticResource PageSubheaderTextStyle}" Grid.Column="1"/>
</Grid>
<Grid Grid.Row="1" Margin="40,0,20,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="20" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding PushNotificationsText, Mode=OneWay}" VerticalAlignment="Center"
Style="{StaticResource
TitleTextStyle}" />
<ToggleSwitch Grid.Column="1" IsOn="{Binding IsPushEnabled, Mode=TwoWay}" />
<TextBlock Grid.Row="2" Text="{Binding FontSizeText, Mode=OneWay}" Style="{StaticResource TitleTextStyle}" />
<ComboBox Grid.Row="2" Grid.Column="1" ItemsSource="{Binding FontSizes}" DisplayMemberPath="Key"
SelectedItem="{Binding SelectedFontSize, Mode=TwoWay}"
Grid.ColumnSpan="2" >
</ComboBox>
</Grid>
</Grid>
</UserControl>
http://geoffwebbercross.blogspot.co.uk/2012/08/windows-8-mvvm-settings-service.html
Settings Service
The next article discusses how to implement the settings service.http://geoffwebbercross.blogspot.co.uk/2012/08/windows-8-mvvm-settings-service.html
Very Nice...Love it
ReplyDeleteWorked very well..
ReplyDeleteThanks for the help..