为什么当两个自定义控件添加了计时器时设计器会变慢?
Why does the designer slowing when two custom controls has timer added?
我有一个用于长时间处理的自定义控件。此控件围绕一个点旋转圆圈。为此,我使用了在设计时间和 运行 时间工作的计时器。当一个控件添加到表单时没有问题。但是加了两个成型,设计师就慢了那么多。为什么会出现此问题,我该如何解决?
我在这个项目中的代码:
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();
}
}
}
Why does this problem occurs and how can I fix it?
该问题与设计时间和计时器没有任何共同之处,而是您的 MakeTransparent
方法的错误实现。
首先,条件有一个明显的bug
c.Bounds.IntersectsWith(c.Bounds)
此错误的影响是它为 每个 控件调用 c.DrawToBitmap
而不是调用者。但是 DrawToBitmap
会触发 OnPaint
,所以当另一个控件也是 SpinningCircles
时,它也会做同样的事情,所以它会命中当前的调用者,你最终会陷入无限的 OnPaint
循环.
根据预期修复条件
c.Bounds.IntersectsWith(cont.Bounds)
只要两个自定义控件不重叠,就会解决此问题。
整个实现是不正确的。你一开始不应该这样做,但一旦你这样做了,它至少应该只为与调用者 相交并且具有较低 ZOrder 的控件调用 DrawToBitmap
.像这样:
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();
}
我有一个用于长时间处理的自定义控件。此控件围绕一个点旋转圆圈。为此,我使用了在设计时间和 运行 时间工作的计时器。当一个控件添加到表单时没有问题。但是加了两个成型,设计师就慢了那么多。为什么会出现此问题,我该如何解决?
我在这个项目中的代码:
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();
}
}
}
Why does this problem occurs and how can I fix it?
该问题与设计时间和计时器没有任何共同之处,而是您的 MakeTransparent
方法的错误实现。
首先,条件有一个明显的bug
c.Bounds.IntersectsWith(c.Bounds)
此错误的影响是它为 每个 控件调用 c.DrawToBitmap
而不是调用者。但是 DrawToBitmap
会触发 OnPaint
,所以当另一个控件也是 SpinningCircles
时,它也会做同样的事情,所以它会命中当前的调用者,你最终会陷入无限的 OnPaint
循环.
根据预期修复条件
c.Bounds.IntersectsWith(cont.Bounds)
只要两个自定义控件不重叠,就会解决此问题。
整个实现是不正确的。你一开始不应该这样做,但一旦你这样做了,它至少应该只为与调用者 相交并且具有较低 ZOrder 的控件调用 DrawToBitmap
.像这样:
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();
}