winforms 在UserControl中设置计时器的间隔会导致整个项目和Visual Studio冻结

rryofs0p  于 2022-11-17  发布在  其他
关注(0)|答案(1)|浏览(143)

原始代码来源于以下答案:
How to animate dots in UserControl Paint event?

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

我希望当我在form 1设计器中拖动控件(例如m_DotColor)时显示interval属性。
这一行会在DotsTimer_Tick事件中产生问题:

dotsTimer.Interval = TimerInterval;

但是当我现在将控件拖到窗体设计器中时,整个项目冻结关闭,VisualStudio重新开始并再次加载项目。
PropertyGrid的屏幕快照,不包含tick事件中的间隔部分。
我从Tick事件中删除了这条线。在属性中,点颜色和点活动颜色列在属性中;我想以同样的方式更改间隔值。

form 1设计器上控件的屏幕快照:

现在我可以在运行程序之前改变DotActiveColorDotColor的颜色了!同样的,我想在运行程序之前改变计时器的速度。

92dk7w1h

92dk7w1h1#

如果要在设计器中查看动画的效果,可以添加一个允许在设计时启动/停止计时器的公共属性。
请注意,必须将属性的支持字段初始化为设置为DefaultValue的值,如下所示:

private int m_Interval = 200;

DefaultValue属性不设置Field,如果属性值与设置为 default 的值匹配,它将阻止属性值的序列化。
我添加了一个可以在PropertyGrid中设置的AnimationEnabled公共属性,以便根据需要启动和停止动画。

不要在UserControl的构造函数中启动计时器。如果希望在首次创建UserControl时(将其放在窗体上时)看到动画,可以使用OnHandleCreated()重写。即,在UC具有句柄之前不要启动计时器。

此外,System.Windows.Forms.Timer有一个official的最大分辨率(最小Interval55ms,虽然它可以工作在35ms。在55ms它已经是一个相当快的动画无论如何。

public partial class LoadingLabel : UserControl
{
    // [...]
    private Timer dotsTimer = null;
    private int m_Interval = 200;
    // [...]

    public LoadingLabel() {
        InitializeComponent();

        components = new Container();
        dotsTimer = new Timer(components) { Interval = m_Interval };
        dotsTimer.Tick += DotsTimer_Tick;

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

    [DefaultValue(false)]
    public bool AnimationEnabled { 
        get => dotsTimer.Enabled;
        set { 
            if (value) Start(); else Stop(); 
        }
    }

    [DefaultValue(200)]
    public int TimerInterval {
        get => m_Interval;
        set {
            value = Math.Max(55, Math.Min(value, 500));
            if (m_Interval != value) {
                m_Interval = value;
                dotsTimer.Interval = m_Interval;
            }
        }
    }

    [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();
        // Start the Timer here - eventually - and change the default value of 
        // AnimationEnabled to true
        // Start();
    }

    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;
    }
}

这是它在设计阶段的外观:
启动/停止计时器并更改间隔

相关问题