System.InvalidOperationException в System.Drawing
Есть функция UpdateImageData, которая меняет цвет пикселей PictureBox в отдельном потоке:
var bmp = (Bitmap) pictureBox1.Image;
var bmpData = bmp.LockBits(tile, ImageLockMode.WriteOnly, bmp.PixelFormat);
var bmpRaw = (byte*) bmpData.Scan0.ToPointer();
// Здесь происходит изменение через bmpRaw[]
bmp.UnlockBits(bmpData);
При взаимодействии с окном (изменение размера, перемещение и т.п.) вылетает ошибка
Application.Run(new Form1());
/* System.InvalidOperationException: "Область растрового изображения уже заблокирована."
в System.Drawing.Graphics.CheckErrorStatus(Int32 status)
в System.Drawing.Graphics.DrawImage(Image image, Int32 x, Int32 y, Int32 width, Int32 height)
в System.Drawing.Graphics.DrawImage(Image image, Rectangle rect)
в System.Windows.Forms.PictureBox.OnPaint(PaintEventArgs pe)
в System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer)
в System.Windows.Forms.Control.WmPaint(Message& m)
в System.Windows.Forms.Control.WndProc(Message& m)
в System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
в System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
в System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
*/
Блок try/catch не обрабатывает эту ошибку.
Скорее всего, что при взаимодействии с окном происходит одновременная перерисовка PictureBox
из двух потоков, но я не знаю как заставить их подождать друг друга.
Если использовать pictureBox1.Invoke(UpdateImageData), то сильно теряется скорость обновления изображения.
Ещё был вариант использовать копию изображения (код ниже), но тогда видно чересстрочную отрисовку (медленно обновляется всё изображения, вместо участка, что изначально было очевидно).
var bmp = new Bitmap(pictureBox1.Image);
// ...
pictureBox1.Image = (Image) bmp;
Ответы (1 шт):
Все действия по рисованию или изменению чего-либо в графических элементах формы (например, изменение надписи в Label) должны выполняться из основного - главного потока. Если у вас есть потребность рисовать из параллельных потоков, то на момент вывода на экран надо передавать управление главному потоку с помощью Invoke(), например, вот так:
new Thread(() =>
{
while (working)
{
pictureBox1.Invoke(new Action(() =>
{
try
{
label1.Text="new text";
DrawMyPictures();
pictureBox1.Invalidate();
Application.DoEvents();
}
catch
{
MessageBox.Show("Draw Exception!");
}
}));
}
}).Start();
При рисовании происходят блокировки, и система налетает на такую блокировку, когда сама пытается рисовать, поэтому нельзя давать ей шанса.