Why does the designer slowing when two custom cont

2019-07-25 10:21发布

问题:

I have a custom control that is used for long time processes. This control has spinning circles around a point. To do this, I am using timer that is working on design time and run-time. When one control is added to form there is no problem. But two of them are added to form, the designer slows down so much. Why does this problem occurs and how can I fix it?

My code from this project:

 public class SpinningCircles : Control
{
    bool fullTransparency = true;
    int increment = 1;
    int radius = 4;
    int n = 8;
    int next = 0;
    int k = 0;
    Timer timer;
    public SpinningCircles()
    {
        timer = new Timer();
        timer.Tick += timer_Tick;
        timer.Enabled = true;
        SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint | ControlStyles.SupportsTransparentBackColor, true);
        BackColor = Color.Transparent;
    }
    void timer_Tick(object sender, EventArgs e)
    {
        Invalidate();
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        if (fullTransparency)
        {
            Transparencer.MakeTransparent(this, e.Graphics);
        }
        e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
        int length = Math.Min(Width, Height);
        PointF center = new PointF(length / 2, length / 2);
        int bigRadius = length / 2 - radius - (n - 1) * increment;
        float unitAngle = 360 / n;
        next++;
        next = next >= n ? 0 : next;
        int a = 0;
        for (int i = next; i < next + n; i++)
        {
            int factor = i % n;
            float c1X = center.X + (float)(bigRadius * Math.Cos(unitAngle * factor * Math.PI / 180));
            float c1Y = center.Y + (float)(bigRadius * Math.Sin(unitAngle * factor * Math.PI / 180));
            int currRad = radius + a * increment;
            PointF c1 = new PointF(c1X - currRad, c1Y - currRad);
            e.Graphics.FillEllipse(Brushes.Black, c1.X, c1.Y, 2 * currRad, 2 * currRad);
            using (Pen pen = new Pen(Color.White, 2))
                e.Graphics.DrawEllipse(pen, c1.X, c1.Y, 2 * currRad, 2 * currRad);
            a++;
        }
    }
    protected override void OnVisibleChanged(EventArgs e)
    {
        timer.Enabled = Visible;
        base.OnVisibleChanged(e);
    }
    public bool FullTransparent
    {
        get
        {
            return fullTransparency;
        }
        set
        {
            fullTransparency = value;
        }
    }
}
public class Transparencer
{
    public static void MakeTransparent(Control cont, Graphics g)
    {
        if (cont.Parent != null)
        {
            Bitmap behind = new Bitmap(cont.Parent.Width, cont.Parent.Height);
            foreach (Control c in cont.Parent.Controls)
                if (c.Bounds.IntersectsWith(c.Bounds) & c != cont)
                    c.DrawToBitmap(behind, c.Bounds);
            g.DrawImage(behind, -cont.Left, -cont.Top);
            behind.Dispose();
        }
    }
}

回答1:

Why does this problem occurs and how can I fix it?

The problem has nothing in common with design time and timers, but the incorrect implementation of your MakeTransparent method.

First, there is an obvious bug in the condition

c.Bounds.IntersectsWith(c.Bounds)

The affect of this bug is the it calls c.DrawToBitmap for each control other than the caller. But DrawToBitmap triggers OnPaint, so when the other control is also SpinningCircles, it does the same, so it hits the current caller and you end up with an infinite OnPaint cycle.

Fixing the condition with the intended

c.Bounds.IntersectsWith(cont.Bounds)

will fix the issue as soon as the two custom controls do not overlap.

The whole implementation is incorrect. You shouldn't be doing it at the first place, but once you did it, it should at least call DrawToBitmap only for controls that intersect with the caller and has lower ZOrder. Something like this:

public static void MakeTransparent(Control control, Graphics g)
{
    var parent = control.Parent;
    if (parent == null) return;
    var bounds = control.Bounds;
    var siblings = parent.Controls;
    int index = siblings.IndexOf(control);
    Bitmap behind = null;
    for (int i = siblings.Count - 1; i > index; i--)
    {
        var c = siblings[i];
        if (!c.Bounds.IntersectsWith(bounds)) continue;
        if (behind == null)
            behind = new Bitmap(control.Parent.ClientSize.Width, control.Parent.ClientSize.Height);
        c.DrawToBitmap(behind, c.Bounds);
    }
    if (behind == null) return;
    g.DrawImage(behind, control.ClientRectangle, bounds, GraphicsUnit.Pixel);
    behind.Dispose();
}