Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/332.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sql-server-2005/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何从另一个线程绘制PictureBox?_C#_Multithreading - Fatal编程技术网

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事件处理程序会为您提供需要重画的区域,您只需在该区域内绘制椭圆即可进行正确的重画。一般来说,人们不再经常做这样的事情了——这是非常低级的,你可以找到更多高级的解决方案。