渲染一遍又一遍winforms picturebox

时间:2018-01-12 08:31:02

标签: c# winforms visual-studio-2015

我有一些需要修复的错误,其中一个涉及内存不足错误。 有谁知道如何正确地做到这一点?谢谢,我不希望它太乱,太复杂。我只是想将一个新图像作为缓冲区来处理另一个图像(因为位置变化),并通过后台线程来完成。不是UI线程(可能太慢)。 我出现内存错误等等。也无法从线程函数中访问Form1的成员(图像等抛出访问错误,例如"对象已在使用")

这是我的代码:

System.Threading.Thread t;
public Image b;
public Bitmap c;
public Bitmap d;
public Bitmap e;
public Bitmap bg;
public Bitmap spr;
int spritex = 0;
int spritey = 0;
int spritedir = 1;

public Form1()
{

    InitializeComponent();
    Text = "Escape The Hypno Mansion!!".ToString();

    t = new System.Threading.Thread(DoThisAllTheTime);
    t.Start();
    textBox1.Text = "Press Begin button to start!";
    pictureBox1.Image = Image.FromFile(@"Images\introgirl.jpg");
    b = new Bitmap(@"Images\introgirl.jpg");
    c = new Bitmap(@"Images\sprite.png");
    var graphics = Graphics.FromImage(b);

    Pen blackpen = new Pen(Color.Black, 3);
    graphics.DrawLine(blackpen, 0, 0, 100, 100);
    graphics.DrawImage(c, new Point(500, 500));
    pictureBox1.Image = b;

    //pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
}

public void DoThisAllTheTime()
{
    while (true)
    {
        Point p = new Point(spritex, spritey);
        bg = new Bitmap(@"Images\test.bmp");
        spr = new Bitmap(@"Images\sprite.png");

        using (var graphics = Graphics.FromImage(bg))
        {

            graphics.DrawImage(spr, p);
        }
        if (pictureBox1.Image != null)
        {

            pictureBox1.Image.Dispose();
        }
        pictureBox1.Image = bg;
        pictureBox1.Invalidate();
        if (spritedir == 1) { spritex += 5; }
        if (spritedir == 2) { spritex -= 5; }

        if (spritex < 0) { spritex = 0; spritedir = 1; }
        if (spritex > 700) { spritex = 700; spritedir = 2; }

    }
}

2 个答案:

答案 0 :(得分:1)

在循环结束前处理每个一次性实例。您的内存泄漏与未从内存中清除的一次性物品有关,因此您最终会在无限循环中耗尽内存。

至少,你需要在循环结束时处理两个位图:

    bg = new Bitmap(@"Images\test.bmp");
    spr = new Bitmap(@"Images\sprite.png");

答案 1 :(得分:1)

您无法更改图片框中图像的原因是因为创建图像的线程不是创建图片框的线程。

在调试器中,您可以在更改函数之前通过询问图片框中的InvokeRequired(函数Control.IsInvokeRequired)来检查这一点。

所以让我们重写你的函数并表明现代类Like Task更容易使用你的线程。

我将在加载表单时启动您的任务,并在表单关闭时尝试停止它。

private Task myTask = null;
private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();

private void OnFormLoading(object sender, EventArgs e)
{
     // Start the task
     this.myTask = Task.Run( () => DoMyWork(this.cancellationTokenSource.Token));
}

private void OnFormClosing(object sender, FormClosingEventArgs e)
{
    // if the Task is still running, ask it to stop itself:
    if (myTask != null && !myTask.IsCompleted)
    {   // ask the task to stop and wait until it is completed:
        this.cancellationTokenSource.Cancel();
        // all Tokens extractes from this source will get state CancellationRequested

        // wait maximum 5 seconds until the task is completed:
        this.UseWaitCursor = true;
        this.myTask.Wait(TimeSpan.FromSeconds(5));
        this.UseWaitCursor = false;            

        //  cancel closing if the task is still not completed
        e.Cancel = !this.myTask.Completed;
    }
}

现在功能DoMyWork:

private void DoMyWork(CancellationToken cancellationToken)
{
    // Do the same as in your DoThisAllTheTime
    // except that you regularly check cancellationToken.IsCancelRequested:
    while(!cancellationToken.IsCancelRequested)
    {
        // calculate the image to display
        var imageToDisplay = ...
        this.DisplayImage(imageToDisplay);
    }
}

void DisplayImage(Image imageToDisplay)
{
    if (this.pictureBox1.InvokeRequired)
    {
        this.Invoke(new MethodInvoker( () => this.DisplayImage(imageToDisplay)));
    }
    else
    {
        this.PictureBox1.Image = imageToDisplay;
    }
}

请参阅: