why this Storyboard in DataTrigger introduce the runtime exception?

William Liu 266 Reputation points
2023-05-29T00:59:20.6633333+00:00

I am trying to complish a Custom Control which has a DataTrigger with animation in my project, but I always hit a InvalidOperationException failure. So I abstract the logic into this repo . I found the same animation works well in the EventTrigger, but will throw runtime exception in DataTrigger which says "Cannot freeze this Storyboard timeline tree for use across threads."

Could you help me to work it out? All I want to complish is that the enduser can set the Color through dependency property, which will be the target value for ColorAnimation when the data value condition is triggered.

.NET
.NET
Microsoft Technologies based on the .NET software framework.
3,369 questions
Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,671 questions
0 comments No comments
{count} votes

Accepted answer
  1. Hui Liu-MSFT 38,251 Reputation points Microsoft Vendor
    2023-05-29T09:23:14.8+00:00

    Hi,@William Liu. Welcome Microsoft Q&A.

    It's because you're using a Binding in your Storyboard. It can't be done because WPF attempts to freeze all the resources leveraged by a template for efficiency and when you use a Binding on a Freezable, in this case the Storyboard, it prevents it from being able to be frozen.

    You could try to refer to the following code.

    
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Viewbox Grid.Column="0" Width="100">
                <CheckBox x:Name="CheckBox" />
            </Viewbox>
            <bindingTest:MyBorder x:Name="MyBorder" Grid.Column="1" Background="Transparent" IsChecked="{Binding ElementName=CheckBox, Path=IsChecked}"  OnBrush="Red">
               
            </bindingTest:MyBorder>
    
        </Grid>
    
    
    
    

    MyBorder:

    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Threading;
    
    namespace BindingTest;
    
    public class MyBorder : Border
    {
        public bool IsChecked
        {
            get => (bool)GetValue(IsCheckedProperty);
            set => SetValue(IsCheckedProperty, value);
        }
    
        public static readonly DependencyProperty IsCheckedProperty =
            DependencyProperty.Register(nameof(IsChecked), typeof(bool), typeof(MyBorder), new PropertyMetadata(false, OnIsCheckedChanged));
    
        public Color OnBrush
        {
            get => (Color)GetValue(OnBrushProperty);
            set => SetValue(OnBrushProperty, value);
        }
    
        public static readonly DependencyProperty OnBrushProperty =
            DependencyProperty.Register(nameof(OnBrush), typeof(Color), typeof(MyBorder), new PropertyMetadata(Colors.LightGreen, OnBrushChanged));
    
        private static void OnIsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var myBorder = (MyBorder)d;
    
            if (myBorder.IsChecked)
            {
                var colorAnimation = new ColorAnimation
                {
                    To = myBorder.OnBrush,
                    Duration = new Duration(TimeSpan.FromSeconds(3))
                };
    
                var storyboard = new Storyboard();
                storyboard.Children.Add(colorAnimation);
    
                Storyboard.SetTarget(colorAnimation, myBorder);
                Storyboard.SetTargetProperty(colorAnimation, new PropertyPath("(Background).(SolidColorBrush.Color)"));
    
                storyboard.Begin();
            }
            else
            {
                var colorAnimation = new ColorAnimation
                {
                    To = Colors.Transparent,
                    Duration = new Duration(TimeSpan.FromSeconds(1))
                };
    
                var storyboard = new Storyboard();
                storyboard.Children.Add(colorAnimation);
    
                Storyboard.SetTarget(colorAnimation, myBorder);
                Storyboard.SetTargetProperty(colorAnimation, new PropertyPath("(Background).(SolidColorBrush.Color)"));
    
                storyboard.Begin();
            }
        }
    
    
        private DispatcherTimer timer;
    
       
    
        private static void OnBrushChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var myBorder = (MyBorder)d;
            if (myBorder.IsChecked)
            {
                myBorder.AnimateColorChange((Color)e.OldValue, (Color)e.NewValue);
            }
            if (!myBorder.IsChecked)
            {
                myBorder.AnimateColorChange((Color)e.OldValue, (Color)Colors.Transparent);
            }
        }
    
        private void AnimateColorChange(Color fromColor, Color toColor)
        {
    
            if (timer != null)
                timer.Stop();
    
            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds(1);
            timer.Tick += (sender, e) =>
            {
                timer.Stop();
    
                var storyboard = new Storyboard();
                var colorAnimation = new ColorAnimation(fromColor, toColor, new Duration(TimeSpan.FromSeconds(3)));
    
                Storyboard.SetTarget(colorAnimation, this);
                Storyboard.SetTargetProperty(colorAnimation, new PropertyPath("(Background).(SolidColorBrush.Color)"));
    
                storyboard.Children.Add(colorAnimation);
                storyboard.Begin();
            };
    
            timer.Start();
        }
    }
    
    
    
    
    

    The result:

    If the response is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


0 additional answers

Sort by: Most helpful