문제

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 Borderchanges 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..

도움이 되었습니까?

해결책 2

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.

다른 팁

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>
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top