Пока используется 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);
}
}
}
}