Плавная смена прозрачности кнопки в 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 шт):
Ошибка в том, что позволяется одновременная работа скольких угодно потоков анимации, которые, конфликтуя друг с другом, беспорядочно меняют значения прозрачности. Нужно, чтобы позволялась одновременная работа только одного потока. Строго говоря, тут вообще не нужны потоки, периодический запуск метода в 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);
}