Пока используется Thread.Sleep с pictureBox, Winform зависает

Я работаю над проектом, в котором pictureBox показывал изображения из каталога один за другим.

Сначала я нажимаю кнопку Пуск. Он вызывает backgroundWorker, который постоянно работает, пока я не нажму кнопку "Стоп". Затем внутри backgroundWorker я вызвал таймер с интервалом времени 500 мс. Он вызывает метод обработчика событий.

Сначала в методе Evenhandler я беру имя файла из каталога, который является числовым, и показываю его в pictureBox с именем "PicBox", увеличиваю и затем показываю следующее изображение. Я использовал цикл while, который запускает весь процесс, пока я не нажму кнопку остановки.

Проблема в том, что он запускается через 500 мс (интервал между таймерами) и показывает все изображения с высокой скоростью. Я хочу показать их в потоке, чтобы пользователь не мог понять, что все представляет собой комбинацию нескольких изображений, но не с такой скоростью.

Я использовал Thread.Sleep(), но он замораживает winform, и я даже не могу нажать кнопку остановки.

КодPictureBox находится здесь:

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 ::
}
}

1 ответ

Решение

Нет-нет-нет-нет-нет.

Это все неправильно. Threading и WinForm, вы должны уловить эти вещи. WinForm работает в том, что называется потоком пользовательского интерфейса. Это поток, в котором вы должны создавать элементы управления, и единственный поток, с которым можно получить доступ к элементам управления (в противном случае вы можете получить тупики... или в наши дни исключения).

Во-вторых, фоновый работник использует фоновый поток в doWork. Это не поток пользовательского интерфейса, и его можно использовать для обработки множества функций. Когда вы выполняете обновления с помощью фонового работника (я забыл точный API), он автоматически перенаправляется в поток пользовательского интерфейса. Мы впорядке?

ХОРОШО. Теперь вот ваша проблема: System.Windows.Forms.Timer работает на UI THREAD. Это простая система, которая вставляет элементы в очередь потока пользовательского интерфейса через определенные промежутки времени.

Так что в вашем примере кажется, что вы вызываете фоновый поток, который затем пытается основать свою работу на таймере, который находится в потоке пользовательского интерфейса. Это безумие.

Вместо этого используйте System.Threading.Timer ИЛИ полностью исключите BackgroundWorker и просто сделайте это с одним потоком (потоком пользовательского интерфейса). Поскольку вы загружаете только 1 изображение за раз, пока изображение локальное, вы, вероятно, можете обойтись без него, не делая интерфейс слишком не отвечающим.

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);
            }
        }
    }
    }
Другие вопросы по тегам