1

我正在为一个项目工作,在该项目中,pictureBox 用于一一显示目录中的图像。

首先我点击开始按钮。它调用一个 backgroundWorker 持续运行,直到我按下停止按钮。然后在 backgroundWorker 中,我调用了一个时间间隔为 500 毫秒的计时器。它调用事件处理程序方法。

在 evenhandler 方法中,我首先从目录中获取文件名,该文件名是数字的,并显示在名为“PicBox”的图片框中并递增,然后显示下一张图像。我使用了一个运行整个过程的 while 循环,直到我单击停止按钮。

问题是它在 500 毫秒(定时器间隔)后开始并以高速显示所有图片。我想以流的形式展示它们,这样用户就不会意识到这一切都是几个图像的组合,但不是以这种速度。

我使用了 Thread.Sleep() 但它冻结了 winform,我什至无法单击停止按钮。

图片框代码在这里:

if (_performReceiving)
{
    try
    {
        while (_performReceiving)
        {
            switch (firstNum)
            {
                case 0:
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                case 7:
                case 8:
                case 9:
                    PicBox.Image = Image.FromFile(path + "0" + firstNum + ".png");
                    this.PicBox.SizeMode = PictureBoxSizeMode.Zoom;
                    PicBox.Refresh();
                    firstNum++;
                    break;
                default:
                    PicBox.Image = Image.FromFile(path + firstNum + ".png");
                    this.PicBox.SizeMode = PictureBoxSizeMode.Zoom;
                    PicBox.Refresh();
                    firstNum++;
                    break;
            } 
        }
    }
    catch (FileNotFoundException)
    {
        MoveTimer.Stop();
    }
}

有什么建议吗?

编辑:(代码的可视化视图)

Form class
{
start_button clicked()
{
   //call backgroundWorker1
}

backgroundWorker1_DoWork()
{
   //create Timer1_handler
   //Timer1.Start()
}

Timer1_handler()
{
   //Process to get firstNames numeric value which passes to switch parameter

   :: This code posted on the main question ::
}
}
4

1 回答 1

4

不不不不不不

这都是错误的。Threading 和 WinForm,你必须了解这些东西。WinForm 在所谓的 UI 线程上运行。这是您必须在其上创建控件的线程,并且是唯一可以访问控件的线程(否则您可能会遇到死锁......或者这些天的例外情况)。

其次,后台worker在doWork中使用了后台线程。这不是 UI 线程,可以用来做很多可爱的处理。当您使用后台工作人员进行更新时(我忘记了确切的 API),它会自动为您编组到 UI 线程。我们好吗?

好的。现在这是您的问题:System.Windows.Forms.Timer 在 UI THREAD 上运行。这是一个简单的系统,它以特定的时间间隔在 UI 线程的队列中插入项目。

因此,在您的示例中,您似乎正在调用一个后台线程,然后该线程试图将其工作基于位于 UI 线程中的计时器。这太疯狂了。

改用 System.Threading.Timer 或完全丢弃 BackgroundWorker,只使用一个线程(UI 线程)执行此操作。由于只要图像是本地图像,您一次只加载 1 张图像,您可能可以摆脱它,而不会使 UI 太无响应。

Form1_Load(object sender, EventArgs e) {
    Timer t = new System.Windows.Forms.Timer();
    t.Interval = 5000;
    t.Tick += SomeEventHandlerThatLoadsAndDisplaysTheNextImage;
    t.Start();
}

或者只是正确使用 BackgroundWorker。如果你这样做,你可以添加 .sleeps。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        // this is the UI thread
        public Form1()
        {
            InitializeComponent();
            Load += new EventHandler(Form1_Load);
        }

        private BackgroundWorker worker;

        // this is the UI thread
        void Form1_Load(object sender, EventArgs e)
        {
            worker = new BackgroundWorker();
            worker.DoWork += new DoWorkEventHandler(worker_DoWork);
            worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
            worker.RunWorkerAsync();
        }
        // this is the UI thread, the BackgroundWorker did the marshalling for you (Control.Invoke or Control.BeginInvoke)    
        void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            pictureBox1.Image = (Image) e.UserState;
        }
        // this is a background thread, don't touch the controls on this thread we use .ReportProgress (offered by the backgroundWorker) to marshal back to the UI thread.
        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            while (iNeedToKeepRotatingImages)
            {
                Thread.Sleep(5000);
                var image = LoadAnImage(myState);
                worker.ReportProgress(0, image);
            }
        }
    }
    }
于 2013-09-10T17:19:36.543 回答