如何在 UserControl Paint 事件中对点进行动画处理?

jho*_*ast 2 .net c# graphics user-controls winforms

在绘画事件中,因为我希望能够控制点大小颜色和更多属性。

using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;

public partial class LoadingLabel : UserControl
{
    public LoadingLabel()
    {
        InitializeComponent();
    }

    private void LoadingLabel_Paint(object sender, PaintEventArgs e)
    {
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        e.Graphics.FillEllipse(Brushes.Red, 1, 1, 20, 20);
        Thread.Sleep(1);
        e.Graphics.FillEllipse(Brushes.Red, 1, 1, 0, 0);
        Thread.Sleep(1);
    }
}
Run Code Online (Sandbox Code Playgroud)

我首先尝试制作一个简单的点,该点在一段时间后消失,然后再次显示,但它不起作用,我看到一个红色的静止点(点)。

稍后当它起作用时,我想制作 3 个点的动画,就像加载动画一样。

这是我尝试过的:

using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;

public partial class LoadingLabel : UserControl
{
    private bool animate = false;

    public LoadingLabel()
    {
        InitializeComponent();
        timer1.Enabled = true;
    }

    private void LoadingLabel_Paint(object sender, PaintEventArgs e)
    {
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        if (animate == false)
        {
            e.Graphics.FillEllipse(Brushes.Red, 1, 1, 20, 20);
        }
        else
        {
            e.Graphics.FillEllipse(Brushes.Red, 5, 1, 20, 20);
        }
    }

    int count = 0;
    private void timer1_Tick(object sender, EventArgs e)
    {
        count++;

        if(count == 10 && animate == false)
        {
            animate = true;
        }

        if(count == 20 && animate)
        {
            animate = false;
            count = 0;
        }
        this.Invalidate();
    }
}
Run Code Online (Sandbox Code Playgroud)

结果是第一个点绘制,然后是第二个点绘制,但第一个点消失了:

看起来该点向右移动,然后又回到左侧。

点移动

但我想要3点的加载效果。并且不移动点。

这适用于 3 点,但对于 3 点来说看起来太复杂了。如果我想要 100 分呢?

也许我应该在绘画事件中使用循环?

using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;

public partial class LoadingLabel : UserControl
{
    private int numofpoints = 0;

    public LoadingLabel()
    {
        InitializeComponent();
        timer1.Enabled = true;
    }

    private void LoadingLabel_Paint(object sender, PaintEventArgs e)
    {
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        if(numofpoints == 0)
        {
            e.Graphics.FillEllipse(Brushes.Red, 1, 1, 20, 20);
        }
        if(numofpoints == 1)
        {
            e.Graphics.FillEllipse(Brushes.Red, 5, 1, 20, 20);
        }
        if(numofpoints == 2)
        {
            e.Graphics.FillEllipse(Brushes.Red, 10, 1, 20, 20);
        }
    }

    int count = 0;
    private void timer1_Tick(object sender, EventArgs e)
    {
        count++;

        if(count == 10)
        {
            numofpoints = 0;
        }

        if(count == 20)
        {
            numofpoints = 1;
        }

        if(count == 30)
        {
            numofpoints = 2;
            count = 0;
        }
        this.Invalidate();
    }
}
Run Code Online (Sandbox Code Playgroud)

我尝试过的另一个更新:

 using System.ComponentModel;
 using System.Drawing;
 using System.Drawing.Drawing2D;
    
 public partial class LoadingLabel : UserControl
 {
     private List<PointF> points = new List<PointF>();

     public LoadingLabel()
     {
         InitializeComponent();
         points.Add(new PointF(0, 0));
         timer1.Enabled = true;
     }

     private void LoadingLabel_Paint(object sender, PaintEventArgs e)
     {
         e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            
         for (int i = 0; i < points.Count; i++)
         {
             e.Graphics.FillEllipse(Brushes.Red, points[i].X, points[i].Y, 20, 20);
         }
     }

     int count = 0;
     private void timer1_Tick(object sender, EventArgs e)
     {
         count++;

         if (count < 3)
         {
             points.Add(new PointF(count * 20, 0));
             //points = new List<PointF>();
         }
         //this.Invalidate();
     }
 }
Run Code Online (Sandbox Code Playgroud)

如果我在勾选事件中创建实例,它将不会绘制任何内容。如果我使用无效线,它会使点像闪烁一样。我想要的是创建一个加载效果动画。

现在代码的结果仍然是 3 点,我想像链接中那样对它们进行动画处理。

点

像这样的东西:

动画点

Jim*_*imi 6

由于,根据您发布的图像,您想要为一系列点设置动画,其中只有活动的点会改变颜色,因此您的用户控件可以定义允许指定点的数量、点的颜色和点的颜色的属性。活动点。

计时器可用于更改当前活动的点,因此绘制过程知道何时更改其中一个点的颜色。

当指定的点数发生变化时,UserControl 会自动调整大小。
此外,当首次创建 UserControl 时,它会设置其MinimumSize,因此点始终可见。

您可以扩展此模板,添加更多功能。


请注意 UserControl 构造函数中的这些行:

components = new Container();
dotsTimer = new Timer(components) { ... };
Run Code Online (Sandbox Code Playgroud)

这指示定时器组件将自身添加到父容器的组件中,因此当父容器被释放时,定时器也被释放并且其事件处理程序被删除。

设置DoubleBuffered = true;可避免绘制点时闪烁。


调用Start()启动动画的方法和Stop()停止动画的方法。

using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;

public partial class LoadingLabel : UserControl {

    private int m_NumberOfDots = 5;
    private Color m_DotColor = Color.Cyan;
    private Color m_DotActiveColor = Color.Blue;
    private float dotSize = 20.0f;
    private float dotSpacing = 20.0f;
    private int currentDot = 0;
    private Timer dotsTimer = null;

    public LoadingLabel()
    {
        InitializeComponent();
        components = new Container();
        dotsTimer = new Timer(components) { Interval = 200 };
        dotsTimer.Tick += DotsTimer_Tick;

        DoubleBuffered = true;
        Padding = new Padding(5);
    }

    [DefaultValue(5)]
    public int NumberOfDots {
        get => m_NumberOfDots;
        set {
            value = Math.Max(3, Math.Min(value, 7));
            if (m_NumberOfDots != value) {
                m_NumberOfDots = value;

                bool running = dotsTimer.Enabled;
                Stop();
                SetMinSize();
                if (running) Start();
            }
        }
    }

    [DefaultValue(typeof(Color), "Cyan")]
    public Color DotColor { 
        get => m_DotColor;
        set {
            m_DotColor = value;
            Invalidate();
        } 
    }

    [DefaultValue(typeof(Color), "Blue")]
    public Color DotActiveColor { 
        get => m_DotActiveColor;
        set {
            m_DotActiveColor = value;
            Invalidate();
        } 
    }

    protected override void OnPaint(PaintEventArgs e) {
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        for (int dot = 0; dot < m_NumberOfDots; dot++) {
            var color = dot == currentDot ? DotActiveColor : DotColor;
            var pos = Padding.Left + (dotSize + dotSpacing) * dot;
            using (var brush = new SolidBrush(color)) {
                e.Graphics.FillEllipse(brush, pos, Padding.Top, dotSize, dotSize);
            }
        }
        base.OnPaint(e);
    }

    protected override void OnHandleCreated(EventArgs e) {
        base.OnHandleCreated(e);
        SetMinSize();
    }

    protected override void OnHandleDestroyed(EventArgs e) {
        Stop();
        base.OnHandleDestroyed(e);
    }

    private void DotsTimer_Tick(object sender, EventArgs e) {
        currentDot += 1;
        currentDot %= m_NumberOfDots;
        Invalidate();
    }

    public void Start() => dotsTimer.Start();

    public void Stop() {
        dotsTimer.Stop();
        currentDot = 0;
        Invalidate();
    }

    private void SetMinSize() {
        var width = Padding.Left + Padding.Right + 
            (dotSize * m_NumberOfDots) + (dotSpacing * (m_NumberOfDots - 1)) + 1;
        var height = Padding.Top + Padding.Bottom + dotSize + 1;
        MinimumSize = new Size((int)width, (int)height);
        Size = MinimumSize;
    }
}
Run Code Online (Sandbox Code Playgroud)

它是这样工作的:

动画点


根据需要,这是用于选择颜色的自定义 ComboBox 控件的PasteBin 。