Error when Painting (long message) [modified]
-
I have created a Form that has a PictureBox in it used for displaying multiple images. The constructor for the Form receives an array of filenames. The Form will then display the first image, Sleep() for a user-specified interval, then show the second image, Sleep(), etc. This all works find and dandy in the normal case. However, if I am dragging another window and it overlaps the Form while trying to change the image, I get an InvalidOperationException telling me that "the object is in use elsewhere", but it won't tell me which object, and then the area where the picture is supposed to be turns into a big red X. The entire class is about 550 lines so I won't post it here, but here are some relevant code snippets: [Everything looks OK in the preview but it looks like it eats my whitespace in the <pre> tags when it actually gets posted]
System::Void initialize_thread()
{
using System::Threading::Thread;
using System::Threading::ThreadStart;draw\_images\_thread = new Thread(new ThreadStart(this, &PlotDisplayWindow::draw\_images)); draw\_images\_thread->IsBackground = true;
}
System::Void draw_image(int image_num) {
picturebox->Image = __try_cast(images_[image_num]);
Text = filenames_[image_num];
graphics = this->CreateGraphics();
graphics->DrawImage(images_[image_num],
AutoScrollPosition.X, AutoScrollPosition.Y,
images_[image_num]->Width, images_[image_num]->Height);
graphics->Dispose();
}__delegate System::Void DrawImageDelegate(int image_num);
System::Void draw_images()
{
//using System::IAsyncResult;
using System::Threading::Thread;while (running) { if (picturebox->InvokeRequired) { DrawImageDelegate\* draw\_image\_delegate = new DrawImageDelegate(this, &PlotDisplayWindow::draw\_image); draw\_image\_delegate->Invoke(image\_number); //IAsyncResult\* async\_result = draw\_image\_delegate->BeginInvoke(image\_number, 0, 0); //draw\_image\_delegate->EndInvoke(async\_result); } else { draw\_image(image\_number); } Thread::Sleep(sleep\_time\_ms); ++image\_number; if (image\_number == filenames\_->Length) { image\_number = 0; } }
}
System::Void PlotDisplayWindow_Paint(System::Object* /*sender*/, System::Windows::Forms::PaintEventArgs* e)
{
usin -
I have created a Form that has a PictureBox in it used for displaying multiple images. The constructor for the Form receives an array of filenames. The Form will then display the first image, Sleep() for a user-specified interval, then show the second image, Sleep(), etc. This all works find and dandy in the normal case. However, if I am dragging another window and it overlaps the Form while trying to change the image, I get an InvalidOperationException telling me that "the object is in use elsewhere", but it won't tell me which object, and then the area where the picture is supposed to be turns into a big red X. The entire class is about 550 lines so I won't post it here, but here are some relevant code snippets: [Everything looks OK in the preview but it looks like it eats my whitespace in the <pre> tags when it actually gets posted]
System::Void initialize_thread()
{
using System::Threading::Thread;
using System::Threading::ThreadStart;draw\_images\_thread = new Thread(new ThreadStart(this, &PlotDisplayWindow::draw\_images)); draw\_images\_thread->IsBackground = true;
}
System::Void draw_image(int image_num) {
picturebox->Image = __try_cast(images_[image_num]);
Text = filenames_[image_num];
graphics = this->CreateGraphics();
graphics->DrawImage(images_[image_num],
AutoScrollPosition.X, AutoScrollPosition.Y,
images_[image_num]->Width, images_[image_num]->Height);
graphics->Dispose();
}__delegate System::Void DrawImageDelegate(int image_num);
System::Void draw_images()
{
//using System::IAsyncResult;
using System::Threading::Thread;while (running) { if (picturebox->InvokeRequired) { DrawImageDelegate\* draw\_image\_delegate = new DrawImageDelegate(this, &PlotDisplayWindow::draw\_image); draw\_image\_delegate->Invoke(image\_number); //IAsyncResult\* async\_result = draw\_image\_delegate->BeginInvoke(image\_number, 0, 0); //draw\_image\_delegate->EndInvoke(async\_result); } else { draw\_image(image\_number); } Thread::Sleep(sleep\_time\_ms); ++image\_number; if (image\_number == filenames\_->Length) { image\_number = 0; } }
}
System::Void PlotDisplayWindow_Paint(System::Object* /*sender*/, System::Windows::Forms::PaintEventArgs* e)
{
usinHi, to be honest I didn't even try to understand the whole code but I most probably know the problem: Don't mix threading and painting! Dragging another window over your form will trigger dozens of paint events and generally its not safe to access the GUI from a different thread that it was created in. In your case a simple Timer should be all you need.
-
Hi, to be honest I didn't even try to understand the whole code but I most probably know the problem: Don't mix threading and painting! Dragging another window over your form will trigger dozens of paint events and generally its not safe to access the GUI from a different thread that it was created in. In your case a simple Timer should be all you need.
Thanks.
Dragging another window over your form will trigger dozens of paint events
Yeah, that's pretty much what I thought was happening but I couldn't see a way to make it safe. I originally tried using a Timer but I couldn't get it to work right so I switched to the Threaded approach. However, this seems to add too much complexity. I will look at Timers again. I assume I should use System::Windows::Forms::Timer instead of System::Threading::Timer or System::Timers::Timer, but I am still a little unclear on the differences among the three. -- Marcus Kwok
-
Thanks.
Dragging another window over your form will trigger dozens of paint events
Yeah, that's pretty much what I thought was happening but I couldn't see a way to make it safe. I originally tried using a Timer but I couldn't get it to work right so I switched to the Threaded approach. However, this seems to add too much complexity. I will look at Timers again. I assume I should use System::Windows::Forms::Timer instead of System::Threading::Timer or System::Timers::Timer, but I am still a little unclear on the differences among the three. -- Marcus Kwok
-
Thanks.
Dragging another window over your form will trigger dozens of paint events
Yeah, that's pretty much what I thought was happening but I couldn't see a way to make it safe. I originally tried using a Timer but I couldn't get it to work right so I switched to the Threaded approach. However, this seems to add too much complexity. I will look at Timers again. I assume I should use System::Windows::Forms::Timer instead of System::Threading::Timer or System::Timers::Timer, but I am still a little unclear on the differences among the three. -- Marcus Kwok
Use the windows forms Timer. Basically I would just set some member variable to the image you want to show and then call Invalidate on your form/control in the Tick eventhandler. In the paint function you could then draw the image according to the field.
-
Use the windows forms Timer. Basically I would just set some member variable to the image you want to show and then call Invalidate on your form/control in the Tick eventhandler. In the paint function you could then draw the image according to the field.
Robert, Thank you for your advice. I was able to get the System::Windows::Forms::Timer to work, and it works well, and is much cleaner than my threaded solution. I am now getting some flickering and a little bit of weirdness when painting, but these problems are orthogonal to my original issue so I will do my research and start a new thread (no pun intended) once I figure out some stuff. Thanks. -- Marcus Kwok
-
Robert, Thank you for your advice. I was able to get the System::Windows::Forms::Timer to work, and it works well, and is much cleaner than my threaded solution. I am now getting some flickering and a little bit of weirdness when painting, but these problems are orthogonal to my original issue so I will do my research and start a new thread (no pun intended) once I figure out some stuff. Thanks. -- Marcus Kwok
Look for double buffering and
SetStyle
from the Control class ;). -
Look for double buffering and
SetStyle
from the Control class ;).