Плавная смена прозрачности кнопки в Windows Forms

Пишу класс анимаций. Начал писать код на смену прозрачности у кнопки, но столкнулся с проблемой. Хотел сделать плавность у прозрачности и поплыл в потоках. Фактический всё работает, но если я сначала наведу, потом уберу курсор, а потом снова наведу начинаются подёргивания прозрачности. Подскажите как это правильно сделать. Вот пример кода кнопки и класса:

protected override void OnMouseEnter(EventArgs e)
{

    base.OnMouseEnter(e);

    eventAnimation.ChangeTransparancy(this, 150);
    MouseEntered = false;
    Invalidate(); 
}
protected override void OnMouseLeave(EventArgs e)
{
    base.OnMouseLeave(e);

    eventAnimation.ChangeTransparancy(this, 255);
    MouseEntered = false;
    Invalidate();
}

Класс анимаций:

public class Animation
{

    public void ChangeTransparancy(Button button, int transparancyCount)
    {
        Thread thread = new Thread(() => ThreadTransparancyAction(button, transparancyCount));
        thread.Start();            
    }
    
    public void ThreadTransparancyAction(Button button, int transparancyCount)
    {
        while (button.BackColor.A != transparancyCount)
        {

            Thread.Sleep(100);
            LerpTransparancy(button, transparancyCount);
        }
    }

    private void LerpTransparancy(Button button, int transparancy)
    {

        Color buttonColor = button.BackColor;

        int NewTransparancyValue = (button.BackColor.A + transparancy) / 2;

        if(NewTransparancyValue == transparancy - 1 && transparancy == 255)
        {
            button.BackColor = Color.FromArgb(255, buttonColor.R, buttonColor.G, buttonColor.B);
        }
        else
        {
            button.BackColor = Color.FromArgb(NewTransparancyValue, buttonColor.R, buttonColor.G, buttonColor.B);
        }

    }
}

Ответы (1 шт):

Автор решения: MSDN.WhiteKnight

Ошибка в том, что позволяется одновременная работа скольких угодно потоков анимации, которые, конфликтуя друг с другом, беспорядочно меняют значения прозрачности. Нужно, чтобы позволялась одновременная работа только одного потока. Строго говоря, тут вообще не нужны потоки, периодический запуск метода в WinForms уже реализован в виде стандартного класса Timer.

Например, создадим такой класс для анимации прозрачности таймером:

public sealed class ButtonAnimation:IDisposable
{
    System.Windows.Forms.Timer _t;
    Button _button;
    int _transparancyCount;

    public ButtonAnimation(Button b, int transparancyCount)
    {
        _button = b;
        _transparancyCount = transparancyCount;
        _t = new System.Windows.Forms.Timer();
        _t.Interval = 100;
        _t.Tick += T_Tick;
        _t.Enabled = true;
    }

    private void T_Tick(object sender, EventArgs e)
    {
        if (_button.BackColor.A == _transparancyCount)
        {
            ((System.Windows.Forms.Timer)sender).Enabled = false;
            return;
        }

        LerpTransparancy(_button, _transparancyCount);
    }

    private void LerpTransparancy(Button button, int transparancy)
    {
        Color buttonColor = button.BackColor;

        int NewTransparancyValue = (button.BackColor.A + transparancy) / 2;

        if (NewTransparancyValue == transparancy - 1 && transparancy == 255)
        {
            button.BackColor = Color.FromArgb(255, buttonColor.R, buttonColor.G, buttonColor.B);
        }
        else
        {
            button.BackColor = Color.FromArgb(NewTransparancyValue, buttonColor.R, buttonColor.G, buttonColor.B);
        }
    }

    public void Dispose()
    {
        if (_t != null)
        {
            _t.Enabled = false;
            _t.Dispose();
            _t = null;
        }
    }
}

Тогда использование будет выглядеть так:

ButtonAnimation anim;

public void ChangeTransparancy(Button button, int transparancyCount)
{
    if (anim != null)
    {
         //если анимация уже идет, прекратим её
         anim.Dispose();
         anim = null;
    }

    anim = new ButtonAnimation(button, transparancyCount);
}
→ Ссылка