C# - Animate a Winforms Control

Viewed 87

I'm trying to develop some sort of "animation" for a control in winforms that will run in a new thread than the main one.

So the code I used for the animation is the one I leave you below (a label control that scrolls up pixel by pixel every few seconds until it reaches 0 pixels):

private void LabelAnimation(int amount)
{
    this.Invoke((MethodInvoker)delegate
    {
        int currentX = Label.Location.X;

        Label.Text = amount.ToString();

        for (int h = 1; h < 7; h++)
        {
            int subtractHeight = h;
            int currentY = Label.Location.Y;

            Label.Location = new Point(currentX, (currentY - subtractHeight));

            Thread.Sleep(200);
        }
    });
}

And the method in which the new thread is created:

private void ExecuteAnimation()
{
    Thread t = new Thread(() => LabelAnimation(100));
    t.Start();
}

The problem is that in itself it works but on a graphic level it sucks, I mean, instead of moving the entire control, the text string remained in the same position while the rectangle of the label moved in the indicated direction, covering its own string.

3 Answers

Use System.Timers.Timer.
It`s recommended to use a timer instead of sleeping the thread.
Here is one way to achieve this:

private System.Timers.Timer timer;
private int amount = 100;
private void ExecuteAnimation()
{
     new Thread(() =>
    {
        // Set DoubleBuffered to true for smoother animation 
        this.DoubleBuffered = true;
        if (timer == null)
        {
            timer = new System.Timers.Timer();
            timer.Interval = 100;
            timer.Elapsed += timer_tick;
        }

        timer.Start();
    }).Start();           
}

private void timer_tick(object sender, EventArgs e)
{
    this.Invoke(new Action(() =>
    {
        int currentX = label1.Location.X;
        label1.Text = amount.ToString();

        if (label1.Location.Y > 0)
        {
            label1.Location = new Point(currentX, label1.Location.Y - 1);
        }
    }));
}

OUTPUT:

enter image description here

If it could be useful to someone else, I finally solved the problem by mixing the @Jonathan Applebaum code and mine, like so:

private void LabelAnimation(int amount, int moveUp)
{
    int currentX = 0;

    this.Invoke(new Action(() =>
    {
        currentX = Label.Location.X;
        Label.Text = amount.ToString();
    }));

    for (int h = 1; h < moveUp; h++)
    {
        this.Invoke(new Action(() =>
        {
            int currentY = Label.Location.Y;
            Label.Location = new Point(currentX, (currentY - h));
        }));

        Thread.Sleep(150);
    }
}

The thread execution always remains the same:

private void ExecuteAnimation()
{
    Thread t = new Thread(() => LabelAnimation(100, 7));
    t.Start();
}

In this way I make only the gets or sets related to the Label control run on the main thread while everything else, including the Thread.Sleep(150), in the secondary thread so as to avoid the Form getting stuck. In this way everything runs smoothly for me.

Related