-2

This is my control:

enter image description here

I am getting this to rotate, with this code:

Global variable, bool IsTriangleAnimRunning = false; And the rest of the code:

public void AnimateTriangle()
        {
            var rotation = (int)((100 / 100d) * 45 * 1); // Max 45 degree rotation
            var duration = (int)(750 * (100 / 100d)); // Max 750ms rotation

            while (IsTriangleAnimRunning != false)
            {
                MyTriangle.Dispatcher.BeginInvoke(
               (Action)(() =>
               {
                   var anim = new DoubleAnimation
                   {
                       To = rotation,
                       Duration = new Duration(TimeSpan.FromMilliseconds(duration)),
                       AutoReverse = true
                   };

                   var rt = new RotateTransform();
                   MyTriangle.RenderTransform = rt;
                   rt.BeginAnimation(RotateTransform.AngleProperty, anim);

               }), DispatcherPriority.ContextIdle);

                Thread.Sleep(duration * 2);
            }
        }

The event handler for the button, that triggers the animation:

 public void HandleTriangleEvents(object sender, RoutedEventArgs a) 
        {
            Thread t_triagle = new Thread(new ThreadStart(this.AnimateTriangle));

            Button btn = (Button)sender;
            if (btn.Name == "btnStartTriangleAnim")
            {
                IsTriangleAnimRunning = true;
                btnStartTriangleAnim.IsEnabled = false;
                t_triagle.Start();
            }
            else
            {
                IsTriangleAnimRunning = false;
                btnStartTriangleAnim.IsEnabled = true;
                t_triagle.Abort();
            }
        }

It behaves in an unatural way, because, when I stop it, it resets to its regular position. I am assuming it, does this for some reason, that I cannot understand. Also, for some reason, this code does not get it to run constantly, but only once.

Desired functionality: If, I hit start button, run the thread and keep on rotating, while thread is running. If, I hit, stop, then stop in the current state of rotation. If I hit start, run the thread again and keep rotating back and forth.

--

Tested with Task Async, runs slower and doesn't repeat.

  private async Task AnimateTriangle()
        {
            double rotation = 45d;
            double duration = 100d;

            var anim = new DoubleAnimation
            {
                To = rotation,
                Duration = TimeSpan.FromMilliseconds(duration),
                AutoReverse = true,
                RepeatBehavior = RepeatBehavior.Forever
            };

            var transform = MyTriangle.RenderTransform as RotateTransform;

            await Task.Factory.StartNew(() =>
            {
                while (IsTriangleAnimRunning != false)
                {
                    MyTriangle.Dispatcher.Invoke(() =>
                    {
                        if (transform == null)
                        {
                            transform = new RotateTransform();
                            MyTriangle.RenderTransform = transform;
                        }

                        transform.BeginAnimation(RotateTransform.AngleProperty, anim);

                    }, DispatcherPriority.ContextIdle);        

                    if (IsTriangleAnimRunning == false)
                    {
                        MyTriangle.Dispatcher.Invoke(() =>
                        {
                            if (MyTriangle.RenderTransform is RotateTransform)
                            {
                                var angle = transform.Angle; // current animated value
                                transform.Angle = angle;
                                transform.BeginAnimation(RotateTransform.AngleProperty, null);
                            }
                        }, DispatcherPriority.ContextIdle);
                    }
                }
            });
        }
John Smith
  • 465
  • 4
  • 15
  • 38
  • Why does it seem like everyone is using sleep-loops when there is a perfectly good timer, heck there are [many of them](https://stackoverflow.com/questions/10317088/why-there-are-5-versions-of-timer-classes-in-net)! And it would make the code both simpler and more efficient, not to mention remove the need for `Thread.Abort` that should be definitely be avoided. – JonasH Nov 15 '21 at 22:51
  • 1
    Jonas, I am using Threads for this. I will not use a Timer. Forget about it. – John Smith Nov 15 '21 at 22:56
  • Does this answer your question? [WPF Xaml Rotation Animation Keeping Final State](https://stackoverflow.com/questions/29653508/wpf-xaml-rotation-animation-keeping-final-state) – Jesse Good Nov 16 '21 at 00:17
  • @JesseGood I will check. But, I highly doubt, this needs to run by a **Thread** ... – John Smith Nov 16 '21 at 06:29
  • @JesseGood, no it doesn't answer my question, again this needs to be **Thread** based. – John Smith Nov 16 '21 at 06:31
  • 1
    Do not use a thread. Start an animation in the UI thread. Let it run forever, i.e with `RepeatBehavior = RepeatBehavior.Forever` and stop it by assigning `rt.BeginAnimation(RotateTransform.AngleProperty, null);` Do not assign a new RotateTransform to the RenderTransform each time you start the animation. Reuse the existing one. – Clemens Nov 16 '21 at 07:21
  • @Clemens, I want to run this with a Thread, plain and simple English. – John Smith Nov 16 '21 at 07:26
  • That makes no sense, sorry. Absolutely not. – Clemens Nov 16 '21 at 07:26
  • @Clemens, why doesn't this make any sense? – John Smith Nov 16 '21 at 07:27
  • You insist on not using the correct solution, but are not presenting any arguments why you need to use a thread. At the same time you are using Thread.Abort, a method so bad it was [removed from the language](https://learn.microsoft.com/en-us/dotnet/api/system.threading.thread.abort?view=net-5.0) – JonasH Nov 16 '21 at 07:34
  • Be aware that explicitly using threads is considered obsolete. For asynchronous operations, use Tasks. – Clemens Nov 16 '21 at 07:44

1 Answers1

1

You do not need to start a thread.

Start an animation that runs forever, until you reset it by setting a null animation.

Before you reset it, explicitly set the value of the target property to the current animated value.

Reuse the existing RotateTransform instead of re-assigning one each time you start the animation.

private void StartButtonClick(object sender, RoutedEventArgs e)
{
    double rotation = 45d;
    double duration = 750d;

    var anim = new DoubleAnimation
    {
        To = rotation,
        Duration = TimeSpan.FromMilliseconds(duration),
        AutoReverse = true,
        RepeatBehavior = RepeatBehavior.Forever
    };

    var transform = MyTriangle.RenderTransform as RotateTransform;

    if (transform == null)
    {
        transform = new RotateTransform();
        MyTriangle.RenderTransform = transform;
    }

    transform.Angle = 0d;
    transform.BeginAnimation(RotateTransform.AngleProperty, anim);
}

private void StopButtonClick(object sender, RoutedEventArgs e)
{
    if (MyTriangle.RenderTransform is RotateTransform transform)
    {
        var angle = transform.Angle; // current animated value
        transform.Angle = angle;
        transform.BeginAnimation(RotateTransform.AngleProperty, null);
    }
}
Clemens
  • 123,504
  • 12
  • 155
  • 268
  • Clemens, why can't this be achieved with Threads? – John Smith Nov 16 '21 at 08:40
  • You would have to synchronize your thread with the running animation, which is hard to achieve. And totally unnecessary. Forget about threads, seriously. They are no longer required for anything you would typically do in a .NET desktop application. – Clemens Nov 16 '21 at 08:55
  • Be aware that WPF animations are supposed to be started and stopped from the UI thread. They are designed like that, and using them from a background thread is not intended. – Clemens Nov 16 '21 at 09:01
  • Clemens, where you getting this information from? – John Smith Nov 16 '21 at 09:06
  • From reading books and online documentation, e.g. [Threading Model](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/threading-model?view=netframeworkdesktop-4.8). Have you tried the code in my answer to see that no background thread is necessary? – Clemens Nov 16 '21 at 09:10
  • Clemens, I believe your code sample works. But, I am interested in separate threads. One thread that can run something, and another that runs something else. Like a Multi-threaded environment. – John Smith Nov 16 '21 at 09:15
  • Sure, but WPF is not the right platform for these trials. Most classes - i.e. all that are derived from DispatcherObject - do even have thread affinity, which means you are not allowed to access instances of these classes from a thread other than the one in which they were created. – Clemens Nov 16 '21 at 09:18
  • Clemens, what about **Task** ? Do these **async** work like traditional *threads*? – John Smith Nov 16 '21 at 09:19
  • Tasks may wrap threads, yes. They are an abstraction however, which free you from the need to think about multithreading at all. – Clemens Nov 16 '21 at 09:28
  • Clemens, Tasks make it slow, very slow. – John Smith Nov 16 '21 at 10:25
  • Not sure what exactly you mean with "*make it slow*" but you would obviously also not do anything with Tasks to start and stop a WPF animation. An animation runs asynchronously by itself. You just do not need a thread or Task at all. – Clemens Nov 16 '21 at 10:42
  • Clemens, so what about doing it with a thread then? And keeping it running. – John Smith Nov 16 '21 at 10:44
  • I don't understand why you insist on using threads. The example here clearly shows how to start an animation that runs until it is stopped, without a background thread. Stop thinking about it. If you want to learn multithreading, do some backend processing, but not UI. – Clemens Nov 16 '21 at 10:50
  • Clemens, what about this: https://stackoverflow.com/questions/28279094/wpf-create-a-floating-animated-clickable-control-image-or ? – John Smith Nov 16 '21 at 10:53
  • That does not make sense. An animation has a Completed event to get notified when it is finished. You do however not need that. – Clemens Nov 16 '21 at 11:04
  • Clemens, I sorted it out using Task async. – John Smith Nov 16 '21 at 11:55