Link to home
Start Free TrialLog in
Avatar of John500
John500Flag for United States of America

asked on

Need to know why my picture boxes won't display during runtime

Greetings:

I have a Form Application and I'm using a thread to call a procedure that sets the 'Visible' state of a few pictures on the Form.  For some reason the pictures won't display if I use the thread, only the frame of the picture box will show.  

If I don't use a thread to make the call, the pictures show fine.  Here is the basic call sequence with the thread:

void Form1::SetPictureStatus(int *PictureUpdate, int *PictureNumber)
{
                int PicUpdate = PictureUpdate;
      char SideNumBuff[10];
      sprintf(SideNumBuff,"%d", PictureNumber]);
      String *ACSideNum = __gc new String(SideNumBuff);


      switch (PicUpdate) {
            case 1:
                  Form1::pictureBox2->Visible = true;
                  listBox22->BeginUpdate();
                  listBox22->Items->Clear();
                  listBox22->Items->Add(ACSideNum);
                  listBox22->EndUpdate();
                  break;
            case 2:      
                  pictureBox3->Visible = true;
                  listBox23->BeginUpdate();
                  listBox23->Items->Clear();
                  listBox23->Items->Add(ACSideNum);
                  listBox23->EndUpdate();
                  break;
            case 3:
                  pictureBox4->Visible = true;
                  listBox24->BeginUpdate();
                  listBox24->Items->Clear();
                  listBox24->Items->Add(ACSideNum);
                  listBox24->EndUpdate();
                  break;
            case 4:
                  pictureBox5->Visible = true;
                  listBox25->BeginUpdate();
                  listBox25->Items->Clear();
                  listBox25->Items->Add(ACSideNum);
                  listBox25->EndUpdate();
                  break;
            case 5:
                  pictureBox6->Visible = true;
                  listBox26->BeginUpdate();
                  listBox26->Items->Clear();
                  listBox26->Items->Add(ACSideNum);
                  listBox26->EndUpdate();
                  break;
         }
      return;
}


void Form1::ClientSocketProc(int *QueueInfo)
{
      int DemoStat;
      int retstat = -1;
      char SideNumBuff[10];
      string TempSideNum;
      LandingInfo QueueObj;          
                LandingInfo *QueObj = &QueueObj;

      DemoStat = GetPanelInfo();

      if((QueueInfo[1] == 1) && (DemoStat == 1)){
            QueueInfo[0]++;                        // increment queue lineup
            QueueInfo[2]++;                        // increment side number
            sprintf(SideNumBuff,"%d",QueueInfo[2]);
            TempSideNum = SideNumBuff;
            QueueObj.SideNum = TempSideNum;
            QueueObj.LineUpNum = QueueInfo[0];
            QueueInfo[1] = insertAirObject(&AircraftQ,QueueObj);
                    SetPictureStatus(QueueInfo[0], QueueInfo[2]);

      }
      return;
}

void Form1::MainPanelThreadProc()
{
      
      int QueueArray[4];
      QueueArray[0]      = 0;            // lineup
      QueueArray[1]      = 1;            // queue status: 0-full, 1- room remains
      QueueArray[2]      = 499;            // side number

      for(;;){

            if((SocketFlag == 1) && (QueueArray[1] != 0))
                  ClientSocketProc(QueueArray);
                             ....
      }
}

What do I need to do in order to make the pictures show using the thread ?  As I said, the pictures show fine without the thread, but with the thread --> only the picture box/frame will display.

Thanks
Avatar of DanRollins
DanRollins
Flag of United States of America image

You should try PictureBox::InvokeRequired()

      http://msdn.microsoft.com/library/en-us/cpref/html/frlrfsystemwindowsformscontrolclassinvokerequiredtopic.asp

to see if you must use an Invoke call to force visibility.   From that page:

   Controls in Windows Forms are bound to a specific thread and are not thread safe.
   Therefore, if you are calling a control's method from a different thread, you must
   use one of the control's invoke methods to marshal the call....

Additional info about cross-thread handling:
 
    Multithreaded Windows Forms Control Sample
    http://msdn.microsoft.com/library/en-us/cpguide/html/cpconDevelopingMultithreadedWindowsFormsControl.asp

=-=-=-=-=-=-=-=-=-=
If that is not the issue, then you could also try (e.g.)
       Form1::pictureBox2->Visible = true;
       Form1::pictureBox2->Invalidate();
to force a redraw.

-- Dan
Avatar of John500

ASKER

Dan,

I understand what you are getting at.  In order to get a better feel for the problem, I put all the 'case' right in the thread.  Hence, no additional method outside of the thread itself.  So it looks something like this:

void Form1::MainPanelThreadProc()
{
     
     int QueueArray[4];
     QueueArray[0]     = 0;          // lineup
     QueueArray[1]     = 1;          // queue status: 0-full, 1- room remains
     QueueArray[2]     = 499;          // side number

     for(;;){

          if((SocketFlag == 1) && (QueueArray[1] != 0))
               ClientSocketProc(QueueArray);
               LineUp = QueueArrary[0];
               swithch(LineUp){
                     case 1:
                              bool TestInvoke;
                              TestInvoke = pictureBox2->InvokeRequired();
                              ....
     }
}

Incidently, without the ' pictureBox2->InvokeRequired() ' line the pictures don't show and when I added the Invalidate(); line that didn't change things either.

When I try to compile this with the InvokedRequired() line, I get the error:

C2064:  term does not evaluate to a function taking 0 arguements

What's up with that, any ideas ?   Also, assuming I get this to compile and it returns ' true ', what is the syntax or invoke method I would use ?  Thanks
It must be that InvokeRequired is treated as a member variable.  TYe it without the parentheses.

There are some additional warnings related to cross-threading access to U/I elements:
   http://msdn2.microsoft.com/en-us/system.windows.forms.control.invokerequired.aspx

It seems that if the HANDLE for the control has not been created, the InvokeRequied property can contain an incorrect value.

>> Hence, no additional method outside of the thread itself.

BTW, moving code around does not put it inside of or outside of the thread.  It is important to realize that a thread is not a place: it is a concept... the action of a executing some code (not the code itself).  A function -- global or object memeber function -- can be executed by any thread that calls that function.

=-=-=-=-=-=-=-=
In non-DotNet MFC apps, I have had to go to great lengths to insure that worker threads do not access ListBoxes or other controls.  For instance, I've had to have the worker threads just add to a queue of "U/I things that need to be done" and have the main thread process that queue periodically on a Window Timer.

-- Dan
Avatar of John500

ASKER

>> Try it without the parentheses

That was the problem.... and it did return ' true '

>> A function -- global or object memeber function -- can be executed by any thread that calls that function.

Yes but the only function that is calling the pictureBox is thread itself -->  each time the thread makes one iteration.  With each iteration of the for lool  ( for(;;) )  the appropriate picture box is updated.  This should not be a case of cross-threading or multithreading.

I decided to go with your idea of removing all threads and only having one thread that calls individual sockets for an update during each iteration.  You mentioned this idea in this question:

http:Q_21864971.html

I'll close that question if you don't want to consider the multithreading scenario.

At any rate, since ' InvokeRequired ' returned ' true ' what is the next step ?  What must I do ?
ASKER CERTIFIED SOLUTION
Avatar of DanRollins
DanRollins
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of John500

ASKER

Dan,

Thanks for the help.  I found this to be the answer:

Form1.h
-----------

public __gc class Form1 : public System::Windows::Forms::Form
{
     ....
     ....
     private:

           // used to toggle state of picture
           bool PictureBox1State;        

           // Handlers to set the PictureBox controls Visible property
           System::Void HandlePictureBox1Update();

           private: System::Void Form1_Load(System::Object *  sender, System::EventArgs *  e)
          {
      // Set the PictureBox controls back to Visible=false once the handles
      // have been created but before the Form is displayed.

      pictureBox1->Visible = false;
                ...
           }
           ...
           ...
}

Form1.cpp
-------------

void Form1::HandlePictureBox1Update()
{
       pictureBox1->Visible = PictureBox1State;      // boolean sets pictureBox state
}

MyPictureSettingFunction(bool PictureState)
{
       PictureBox1State = PictureState;
       if(pictureBox1->IsHandleCreated && pictureBox2->InvokeRequired)
       {
      // Invoke the UI thread handler to change the PictureBox visibility
      pictureBox1->Invoke( __gc new System::Threading::ThreadStart(this, &Form1::HandlePictureBox1Update));
       }
}