C# 如何从另一个线程绘制PictureBox?
我有一张C# 如何从另一个线程绘制PictureBox?,c#,multithreading,C#,Multithreading,我有一张表格和一个图片盒。当我的PictureBox更新时,我想在PictureBox中绘制某种背景(只是按特定顺序排列的点阵列)。用于绘制的代码可以很好地工作,但它需要花费太多的时间(因为我绘制了太多的点),并且我无法正常使用该表单,因为程序正在等待所有的点被绘制出来。所以我决定在单独的线上画点,像这样: private void flowchartDrawingArea_Paint(object sender, PaintEventArgs e) { Thread thread =
表格
和一个图片盒
。当我的PictureBox
更新时,我想在PictureBox
中绘制某种背景(只是按特定顺序排列的点阵列)。用于绘制的代码可以很好地工作,但它需要花费太多的时间(因为我绘制了太多的点),并且我无法正常使用该表单,因为程序正在等待所有的点被绘制出来。所以我决定在单独的线上画点,像这样:
private void flowchartDrawingArea_Paint(object sender, PaintEventArgs e)
{
Thread thread = new Thread(() => DrawBackground(sender, e));
thread.Start();
}
private void DrawBackground(object sender, PaintEventArgs e)
{
GridBackground.GetAreaSize((Control)sender);
GridBackground.Draw(e);
}
class GridBackground
{
/*...*/
public void Draw(PaintEventArgs e)
{
int positionWidth = 0;
int positionHeight = 0;
while (positionHeight < PanelHeight)
{
while (positionWidth < PanelWidth)
{
Rectangle rectangle = new Rectangle(positionX, positionY, R, R);
e.Graphics.FillEllipse(Brushes.White, rectangle);
e.Graphics.DrawEllipse(new Pen(Color.Gray, 2), rectangle);
positionWidth += DotInterval;
}
positionHeight += DotInterval;
positionWidth = 0;
}
}
}
我的绘画课是这样的:
private void flowchartDrawingArea_Paint(object sender, PaintEventArgs e)
{
Thread thread = new Thread(() => DrawBackground(sender, e));
thread.Start();
}
private void DrawBackground(object sender, PaintEventArgs e)
{
GridBackground.GetAreaSize((Control)sender);
GridBackground.Draw(e);
}
class GridBackground
{
/*...*/
public void Draw(PaintEventArgs e)
{
int positionWidth = 0;
int positionHeight = 0;
while (positionHeight < PanelHeight)
{
while (positionWidth < PanelWidth)
{
Rectangle rectangle = new Rectangle(positionX, positionY, R, R);
e.Graphics.FillEllipse(Brushes.White, rectangle);
e.Graphics.DrawEllipse(new Pen(Color.Gray, 2), rectangle);
positionWidth += DotInterval;
}
positionHeight += DotInterval;
positionWidth = 0;
}
}
}
classgridbackground
{
/*...*/
公共无效绘图(PaintEventArgs e)
{
int位置宽度=0;
int位置高度=0;
同时(位置高度<面板高度)
{
while(位置宽度<面板宽度)
{
矩形=新矩形(位置X、位置Y、R、R);
e、 图形。填充椭圆(画笔。白色,矩形);
e、 图形。抽屉(新钢笔(颜色:灰色,2),矩形);
位置宽度+=点间距;
}
位置高度+=点间距;
位置宽度=0;
}
}
}
但我总是在
e.Graphics.FillEllipse(Brushes.White,rectangle)的“e”处有一个“parameter not valid”异常代码>字符串。我读到当试图使用其他线程的表单控件时会发生这种情况,但我不知道在我的例子中如何解决它 流程图绘图区域是您的图片盒名称吗?
你可以试试这个代码
Thread thread = new Thread(() => flowchartDrawingArea.BeginInvoke(new Action<object, PaintEventArgs>((o, args) => { DrawBackground(o, args); }), sender, e));
thread.Start();
Thread-Thread=new-Thread(()=>flowchartDrawingArea.BeginInvoke(新操作((o,args)=>{trabackground(o,args);}),发送方,e));
thread.Start();
如果只想将绘图延迟到另一个线程,可以使用后缓冲区。基本思想是对屏幕外的位图进行所有渲染,完成后,让UI线程在实际的PictureBox
上提取该缓冲区
你也可以做很多优化。如果您的代码速度不够快,无法实时呈现美观的功能,这对于UI来说是一个问题,而将呈现委托给其他线程只是隐藏了这个问题。例如:
- 不要在循环中执行
FillEllipse
和DrawEllipse
,而是将椭圆渲染一次为位图,然后继续执行DrawImage
。那会快得多
- 尊重在事件处理程序中重新绘制的区域。通常只要求您进行部分重画,而您总是在浪费CPU来重画所有内容
- 如果需要处理滚动,请尝试使用尽可能多的现有后台缓冲区。使用偏移量绘制backbuffer将使相同的外观更便宜
- 事实上,您甚至可以一次绘制整行,并绘制多行。同样,绘制三个位图要比绘制200个单独的椭圆快得多
“如何从另一个线程绘制PictureBox?”你不能,你需要在主线程上绘制。但是,您可以使用另一个线程从操作位图,并一次更新所有位图(在UI线程上)。您可以在位图上绘制位图,然后将其设置为PictureBox。这就像DoubleBuffer一样工作。在第二个while循环中放置一个锁。请参阅:每个绘图都必须在UI线程中进行。是的,这种方法似乎效果最好,在位图中进行所有绘图(如果需要,可以在另一个线程中进行),然后在完成后更新图片框,因此建议您启动一个线程并立即将所有内容封送回UI线程?这是一种效率低下的一事无成的方式……或者你可以试试BackgroundWorker?谢谢你的回答!但看起来这个在我的情况下不起作用:(非常感谢你的回答!它对我帮助很大!我试着按照你的建议去做,但我有一些问题:1)我试着将elipse绘制到位图,效果很好,但是如果我需要一行行相同的elipse,我只需要一些循环,将重复这个已经绘制的位图,并把它放到另一个位图,最后我只把这个大位图,包括所有这些小的elipse位图到PictureBox?我明白你的意思了吗?2)每当我的画盒发生绘画事件时,我都会调用我的绘画方法。在绘图动作的最后,我喜欢PictureBox.Image=myBigBitmap。看起来这个相等的动作也算是PictureBox修改,所以看起来像是im进入总是调用事件处理程序的无限循环。因为内存增长如此之快,一直增长到0.5-0.8GB。你怎么想,我该如何防止这种情况发生?3)以及如何仅绘制已更新的区域?也许你可以给我发一些链接,我可以在那里读到。那会让我恢复这么快的速度!因为,老实说,我也从来没有意识到为什么我应该重画整个区域,如果只有一小部分被改变:)4)你能推荐我读一些关于滚动和后缓冲区的东西吗?我将不胜感激。@Donchan要么不要在Paint
事件中这样做,要么不要使用Image=bitmap
-您可以使用e.DrawImage
绘制位图。此外,您不需要不断创建新位图-仅在必要时更改位图(例如,调整图片框大小时)。至于部分重画,基本思想很简单-paint事件处理程序会为您提供需要重画的区域,您只需在该区域内绘制椭圆即可进行正确的重画。一般来说,人们不再经常做这样的事情了——这是非常低级的,你可以找到更多高级的解决方案。