Reputation: 8502
How can I create 2 visual states for a WPF Border
control : one that flashes the Background color between Transparent
and Red
; and one normal that sets the Border Background color back to Transparent
and stops flashing?
Note: The WPF Border
control is used inside the ContentTemplate
of another control.
I also require them to be triggered when some property say IsEnabled
of the Border
changes from False
to True
and vice versa; and the IsEnabled property is bound to a ViewModel property. When we click on the Border-the Flashing should stop and the background should revert to normal..
Upvotes: 1
Views: 2232
Reputation: 4919
You can define VisualStates with the VisualStateManager. To get the behaviour you want on the Border the following should be a good starting point:
The xaml:
<Border Name="TheBorder" BorderThickness="5"
Margin="30" Padding="20"
wpfApplication1:StateManager.VisualState="{Binding ElementName=TheBorder,
Path=IsEnabled, Mode=TwoWay,
Converter={StaticResource EnabledToVisualStateConverter}}">
<Border.Background>
<SolidColorBrush x:Name="BackgroundBrush" Color="Transparent"/>
</Border.Background>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="Common">
<VisualState x:Name="Normal"/>
<VisualState x:Name="Flash">
<Storyboard>
<ColorAnimation Storyboard.TargetName="BackgroundBrush"
Storyboard.TargetProperty="Color" To="Red"
RepeatBehavior="Forever"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Border>
The converter:
public class EnabledToVisualStateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var isEnabled = (bool) value;
if (isEnabled)
return "Flash";
return "Normal";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
The StateManager class used to change the VisualState:
public class StateManager
{
private static string _valueToApplyOnInitialization;
public static readonly DependencyProperty VisualStateProperty =
DependencyProperty.RegisterAttached("VisualState", typeof (string), typeof (StateManager),
new PropertyMetadata(VisualStateChangeCallback));
public static string GetVisualState(DependencyObject obj)
{
return (string)obj.GetValue(VisualStateProperty);
}
public static void SetVisualState(DependencyObject obj, string value)
{
obj.SetValue(VisualStateProperty, value);
}
public static void VisualStateChangeCallback(object sender, DependencyPropertyChangedEventArgs args)
{
var element = sender as FrameworkElement;
if (element == null)
return;
if (!element.IsInitialized)
{
_valueToApplyOnInitialization = (String) args.NewValue;
element.Initialized += OnElementInitialized;
}
else
VisualStateManager.GoToElementState(element, (string)args.NewValue, true);
}
private static void OnElementInitialized(object sender, EventArgs e)
{
var element = sender as FrameworkElement;
if (element == null)
return;
VisualStateManager.GoToElementState(element, _valueToApplyOnInitialization, true);
element.Initialized -= OnElementInitialized;
}
}
If you want to use a property from your ViewModel rather than the IsEnabled property on your Border, then just replace the Binding to 'TheBorder' with your ViewModel property.
Upvotes: 3
Reputation: 4919
If you want to perform the animation purely in xaml then you could use Triggers instead of the VisualStateManager. The following should give you the behaviour you're after:
<Border Name="TheBorder" BorderThickness="5"
Margin="30" Padding="20" >
<Border.Background>
<SolidColorBrush x:Name="BackgroundBrush" Color="Transparent" />
</Border.Background>
<Border.Style>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Trigger.EnterActions>
<BeginStoryboard Name="FlashStoryboard">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background"
Duration="0:0:1" RepeatBehavior="Forever">
<ObjectAnimationUsingKeyFrames.KeyFrames>
<DiscreteObjectKeyFrame KeyTime="0:0:0.5">
<DiscreteObjectKeyFrame.Value>
<SolidColorBrush Color="Red"/>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames.KeyFrames>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<StopStoryboard BeginStoryboardName="FlashStoryboard"></StopStoryboard>
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
Upvotes: 4