Thursday, 21 November 2013

Binding visual state manager to a view model

Using an attached property:

using System;
using System.Windows;
using System.Windows.Controls;

namespace StateHelperSL
{
public class VisualStateHelper : DependencyObject
{
public static readonly DependencyProperty VisualStatePropertyProperty = DependencyProperty.RegisterAttached(
"VisualStateProperty",
typeof(string),
typeof(VisualStateHelper),
new PropertyMetadata(VisualStatePropertyChangedCallback));

private static void VisualStatePropertyChangedCallback(DependencyObject s, DependencyPropertyChangedEventArgs e)
{
var ctrl = s as Control;
if (ctrl == null)
throw new InvalidOperationException("This attached property only supports types derived from Control.");

VisualStateManager.GoToState(ctrl, e.NewValue.ToString(), true);
}

public static string GetVisualStateProperty(DependencyObject obj)
{
return (string)obj.GetValue(VisualStatePropertyProperty);
}

public static void SetVisualStateProperty(DependencyObject obj, string value)
{
obj.SetValue(VisualStatePropertyProperty, value);
}
}
}
You can bind your view:
<UserControl x:Class="StateHelperSL.MainPage"
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"
xmlns:local="clr-namespace:StateHelperSL"
local:VisualStateHelper.VisualStateProperty="{Binding State, Mode=OneWay}"
mc:Ignorable="d">

<Grid x:Name="LayoutRoot">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VisualStateGroup">
<VisualState x:Name="Normal"/>
<VisualState x:Name="Gone">
<Storyboard>
<DoubleAnimation To="1000"
Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)"
Storyboard.TargetName="AnimatableButton"
d:IsOptimized="True"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<StackPanel Margin="50">
<Button x:Name="AnimatableButton" Click="ButtonBase_OnClick" Height="30" Content="Click me">
<Button.RenderTransform>
<CompositeTransform/>
</Button.RenderTransform>
</Button>

</StackPanel>
</Grid>
</UserControl>
View code behind:
using System.Windows;

namespace StateHelperSL
{
public partial class MainPage
{
private readonly ViewModel vm;

public MainPage()
{
this.InitializeComponent();
this.vm = new ViewModel();
this.DataContext = this.vm;
}

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
vm.State = "Gone";
}
}
}

To your view model:


namespace StateHelperSL
{
public class ViewModel : PropertyChangedBase
{
private string state;

public ViewModel()
{
State = "Normal";
}

public string State
{
get { return this.state; }
set
{
if (state == value) return;
this.state = value;
NotifyOfPropertyChange("State");
}
}
}
}

1 comment:

  1. Bit of a typo here (at least for a Win 8.1 app)

    The VisualStatePropertyProperty declaration should read

    public static readonly DependencyProperty VisualStatePropertyProperty = DependencyProperty.RegisterAttached(
    "VisualStateProperty", typeof(string), typeof(VisualStateHelper), new PropertyMetadata(null, VisualStatePropertyChangedCallback));

    (note the added "null" on the new PropertyMetadata declaration)

    Thanks!

    ReplyDelete