Краткое введение в мою программу (решено)
Моя программа делает снимок и показывает плитку за плиткой (игра в угадайку). Я решил использовать затухание (вручную) каждой плитки. Поскольку мне нужно, чтобы пользователь мог взаимодействовать с графическим интерфейсом, я выполняю вычисления и блокирую такие вещи, как Thread.Sleep
, в другом потоке. Я также добавил событие OnClickEvent в окно изображения (которое перекрывает изображение для отображения). =› если кто-то угадал изображение, пользователь может щелкнуть по картинке, чтобы полностью открыть изображение. (Для затухания я обрезаю область изображения, а затем очищаю ее цветом. Альфа-значение цвета постепенно уменьшается, пока он не станет полностью прозрачным. Затем я перехожу к следующей области.)
Входящая проблема
После каждой итерации мне нужно обновить изображение, чтобы оно отображало новую ситуацию. Поэтому я должен вызвать действие в потоке GUI. Теперь, если время между каждым обновлением становится слишком коротким, например, 10 мс, графический интерфейс, кажется, настолько занят обновлением/рисованием изображения, что больше не запускает мой OnClickEvent.
Функция раскрытия
public void Reveal(int step, int intervalFading, int intervalNextTile)
{
StopThread = false; // Changed by the OnClickEvent
Graphics grx = Graphics.FromImage(Overlay.Image);
step = 255 / step;
foreach (RectangleF R in AreasShuffled)
{
grx.Clip = new Region(R);
for (int i = 255; i >= 0; i-=step) //Fading out loop
{
Thread.Sleep(intervalFading); //if intervalFading < 15 GUI is too busy
if (StopThread) //Condition if someone guessed correctly
{
grx.ResetClip();
grx.Clear(Color.FromArgb(0, 0, 0, 0)); //revealing the image
ParentControl.BeginInvoke((Action)(() => Overlay.Refresh()));
grx.Dispose();
return;
}
grx.Clear(Color.FromArgb(i, 0, 0, 0)); //Clearing region
ParentControl.BeginInvoke((Action)(() => Overlay.Refresh())); //Redrawing image
}
grx.Clear(Color.FromArgb(0, 0, 0, 0));
ParentControl.BeginInvoke((Action)(() => Overlay.Refresh()));
Thread.Sleep(intervalNextTile);
}
grx.ResetClip();
grx.Clear(Color.FromArgb(0, 0, 0, 0));
ParentControl.BeginInvoke((Action)(() => Overlay.Refresh()));
grx.Dispose();
}
Решение
По совету я использовал асинхронные задачи. Вот обновленная функция. (Да, я не обновлял grx.dispose() ^^)
public async Task Reveal(int step, int intervalFading, int intervalNextTile)
{
taskIsRunning = true;
stopTask = false; // Changed by the OnClickEvent
Graphics grx = Graphics.FromImage(Overlay.Image);
step = 255 / step;
foreach (RectangleF R in AreasShuffled)
{
grx.Clip = new Region(R);
for (int i = 255; i >= 0; i -= step)
{
await Task.Delay(intervalFading);
if (stopTask)
{
grx.ResetClip();
grx.Clear(Color.FromArgb(0, 0, 0, 0));
Overlay.Refresh();
grx.Dispose();
taskIsRunning = false;
return;
}
grx.Clear(Color.FromArgb(i, 0, 0, 0));
Overlay.Refresh();
}
grx.Clear(Color.FromArgb(0, 0, 0, 0));
Overlay.Refresh();
await Task.Delay(intervalNextTile);
}
grx.ResetClip();
grx.Clear(Color.FromArgb(0, 0, 0, 0));
Overlay.Refresh();
grx.Dispose();
taskIsRunning = false;
}
и вызывающая функция, которая проверяет, запущена задача или нет
private void pictureBoxOverlay_Click(object sender, EventArgs e)
{
if (UCM != null && UCM.taskIsRunning) //if it is running the function is being notified
{ //and reveals the image.
UCM.stopTask = true;
}
else //makes sure that the user has to click again to start with the next image
{
if (index < Images.Count - 1)
{
PrepareNextImage();
UCM.Reveal(Properties.Settings.Default.steps, Properties.Settings.Default.fadeInterval, Properties.Settings.Default.nextTileInterval);
}
else
MessageBox.Show("End of presentation.");
}
}
Спасибо за вашу помощь ;)