Click here to Skip to main content
15,395,257 members
Please Sign up or sign in to vote.
2.00/5 (1 vote)
See more:
Hello everyone,

i am new in .net programming and i would like to get some help about it.

I'm trying to create an easy client GUI application using the VS2010 Designer to create a single form in which i have:
1 comboBox, (containing the list of possible commands);
1 button, (used to execute the command selected in the combobox);
1 picturebox (in which i display images received from my server application);

I was able to create my client application and display a different image in the picturebox received from the server everytime i press the button.

Next step was creating a thread in which i continuously receive images from the server and display them in my pictureBox when calling the function PictureRefresh()
The thread is running fine and receives images sent from the server.

I can compile and run the code, but nothing is displayed in the picturebox.
I know it has to be something silly, but still i don't know why nothing happens.

Below is the function i'm using:

C++
void PicureRefresh()
{
   int once = 0;
   Bitmap^ image1;
   image1 = gcnew Bitmap(500, 1000, 
      System::Drawing::Imaging::PixelFormat:: Format24bppRgb);	

   //Lockbits
   System::Drawing::Rectangle rect = System::Drawing::Rectangle(0,0,image1->Width,image1->Height);
   System::Drawing::Imaging::BitmapData^ bmpData = image1->LockBits( rect,
      System::Drawing::Imaging::ImageLockMode::ReadWrite, image1->PixelFormat );

   // Get the address of the first line.
   IntPtr ptr = bmpData->Scan0;

   // Declare an array to hold the bytes of the bitmap.
   // This code is specific to a bitmap with 24 bits per pixels.
   int bytes = Math::Abs(bmpData->Stride) * image1->Height;
   array<Byte>^rgbValues = gcnew array<Byte>(bytes);

   // Copy the RGB values into the array.
   System::Runtime::InteropServices::Marshal::Copy( ptr, rgbValues, 0, bytes );			  

   for ( int counter = 0; counter < rgbValues->Length; counter++ )							
   {
      rgbValues[ counter ] = mat[counter/3];					  						                                               
   }
   
   // Copy the RGB values back to the bitmap
   System::Runtime::InteropServices::Marshal::Copy( rgbValues, 0, ptr, bytes );

   // Unlock the bits.
   if(!once)
   {					
      image1->UnlockBits( bmpData );
      once++;
   }
   
   // Draw the modified image.
   pictureBox1->Image= image1;
   pictureBox1->Refresh();					
}


i also tried adding pictureBox1->Invalidate(); to force the PictureBox to Re PAINT the control, but still nothing happens

Any help or suggestion would be much appreciated.
Thanks.
Posted
Updated 22-Mar-12 6:32am
v3
Comments
Sergey Alexandrovich Kryukov 22-Mar-12 12:25pm
   
Ultimately, all data comes from mat[counter/3]. The method has no parameters. Where this mat comes from? From outer scope? Are you serious? Now, you hope to get correct index by integer division?

No, this is a mess. What is the purpose of it? What's the image got from server? Why not transmitting the whole image?
Why using PictureBox at all?
--SA
lukin4that 22-Mar-12 12:59pm
   
Hi,
thanks for your answer.
i know the code is a mess i was looking for a quick and dirty temporary solution just to test how to constantly refresh my picturebox.


The purpose of it is learning, and what i receive from the server is a vector of pseudorandom values(from 0 to 255) from which it is possible updating the RGBvalues in the image.

The scenario is receiveing fixed size vectors (containing values from 0 to 255) from the server and display on the client images built from those vectors in a picturebox.

Despite of how useless and messy is the code, still, why nothing is displayed in the picturebox when executing pictureBox1->Image= image1;
pictureBox1->Refresh(); ?

Thanks
Sergey Alexandrovich Kryukov 22-Mar-12 15:47pm
   
If you do it all right, the image should be refreshed by the mere assignment of image1, at least if it is referentially different from the previous value; no refresh should be needed (it would be needed only if you manipulate the image data, at the same reference). So, refresh should have happened, may be the image is nearly the same -- that's it. Why won't you do simple research thing: save image in the file each time, then compare those files, something like that?

You see, you receive some who-knows-what data, pass it who-knows-how (look, first of all, stop it! explicitly pass "mat" or something as a parameter! after all, what is ins-and-outs comes first), and then you do tricky manipulations with data (because counter/3 causes rounding errors, to start with), and expect to see some effect with unarmed eye. Things do not work this way. You need to search what data comes, what comes into effect, debug it... just staring at code is not so effective.

Also, using PictureBox is totally pointless. How it helps you, compared to simple custom control? It only adds you hassles like the one you mentioned in the question. With "normal" control you would only do Invalidate.

I hope I gave you the ideas on what to look for. Ask further questions if you still need more of a real answer.
--SA
Sergey Alexandrovich Kryukov 22-Mar-12 15:53pm
   
Oh, almost forgot... Where do you call your method? From what thread? On what event? This is important. Everything might work but you might not see it, because...

Well, just explain it. You are supposed to have a thread with network communications. Then you should update the view with newly received data. You need to use Invoke/BeginInvoke. Where is all that?..

--SA
lukin4that 23-Mar-12 5:20am
   
Thanks for your answer.
I'm calling the method from the network thread. when "start imaging" is selected in my combobox on the event button click a flag is set to true and the network thread starts receiving data, when "stop imaging" is selected on the event button click a flag is set to false and the network thread stops receiving data.
After receiving new data the network thread calls the method PictureRefresh().
Where should i use Invoke/BeginInvoke?
Sergey Alexandrovich Kryukov 23-Mar-12 11:16am
   
I'll explain later, but I'm not sure you got it, because without invocation you would have cross thread exception. So, something is wrong, not related to your function, but outside it.

You need to start investigation one step at a time. First, call this function directly on click on something, and supply some data, not from network. And use non-random data. Make sure the picture is updated...

Let me ask you, how much of bits do you change each time. Why not send and change the image completely.
Do you still use PictureBox?

--SA
lukin4that 26-Mar-12 4:21am
   
When i call the function directly on button click, the picture is updated everytime i click the button, either data are from network or not.

i would say i'm changing all the image bits everytime.
i am still using PictureBox, because i don't know yet how to create a custom control where to display images.
Sergey Alexandrovich Kryukov 23-Mar-12 11:29am
   
Please see my answer on the two of the topics.
You need to explain how you do the thread.
Did you compare files?
Think about sending the whole image, answer why not...
--SA
lukin4that 26-Mar-12 4:28am
   
Hi,

i start the thread in the main() this way:

Form1^ threadWork = gcnew Form1;
Thread^ newThread = gcnew Thread( gcnew ThreadStart( threadWork, &Form1::EthThread ) );
newThread->Start();

EthThread is a method used to request and receive data to/from the server.
While debugging i can see data being updated, even though it is happening in an outer scope.

I am sending the whole image

Without seeing where mat comes from, or how it what values it holds it's difficult to say for sure, but I'm guessing it's something fairly simple.

Get rid of the if(!once), you should always unlock your bits.
Call Invalidate on your PictureBox at the end, that will refresh it.
This statement is weird;
C++
System::Runtime::InteropServices::Marshal::Copy( ptr, rgbValues, 0, bytes );	

You copy all the pixels of the newly created image into a buffer, rgbValues, but since you just created the image all pixels will be black. And immediately after you overwrite the values in rgbValues anyway.

Try changing your method to this (this assumes that these methods are inside your form and that it's called Form1);
C++
// Declare a delegate
delegate void Refresh();

// Let this method delegate the call to the actual refresh method on the UI thread
void PictureRefresh()
{
   Refresh^ d = gcnew Refresh(this, &Form1::PictureRefreshInternal);
   this->Invoke(d);
}

void PictureRefreshInternal()
{
   Random^ rnd = gcnew Random();
   int once = 0;
   Bitmap^ image1;
   image1 = gcnew Bitmap(500, 1000, 
      System::Drawing::Imaging::PixelFormat:: Format24bppRgb);	
 
   //Lockbits
   System::Drawing::Rectangle rect = System::Drawing::Rectangle(0,0,image1->Width,image1->Height);
   System::Drawing::Imaging::BitmapData^ bmpData = image1->LockBits( rect,
      System::Drawing::Imaging::ImageLockMode::ReadWrite, image1->PixelFormat );
 
   // Get the address of the first line.
   IntPtr ptr = bmpData->Scan0;
 
   // Declare an array to hold the bytes of the bitmap.
   // This code is specific to a bitmap with 24 bits per pixels.
   int bytes = Math::Abs(bmpData->Stride) * image1->Height;
   array<byte>^rgbValues = gcnew array<byte>(bytes);
 
 
   for ( int counter = 0; counter < rgbValues->Length; counter++ )							
   {
      // Use random value here to see if it works
      rgbValues[ counter ] =  (byte)rnd->Next();					  						                                               
   }
   
   // Copy the RGB values back to the bitmap
   System::Runtime::InteropServices::Marshal::Copy( rgbValues, 0, ptr, bytes );
 
      image1->UnlockBits( bmpData );
   
   // Draw the modified image.
   pictureBox1->Image= image1;
   pictureBox1->Invalidate();					
}


That works for me when I call that method on a button click on the form.
If it works for you then the values in mat are wrong, if it doesn't work then it's got something to do with you updating the wrong picturebox or not updating at all.

Hope this helps,
Fredrik

Updated; Added a wrapper method to invoke this back to the UI thread.
Updated; Fixed missing delegate type name.
   
v3
Comments
lukin4that 22-Mar-12 13:47pm
   
Thanks for your answer Fredrik,

i tried modifying the code as you suggested, but still, nothing happens in the picturebox :(

Considering that i only have 1 picturebox in my form and the compiler gives no errors, i'm confused about the reason why nothing happens.

Thanks
Sergey Alexandrovich Kryukov 22-Mar-12 15:50pm
   
How come? This solution should produce some random noise... (I don't know why, probably just for experiment); don't you see it?
--SA
lukin4that 23-Mar-12 4:37am
   
Exactly, so far this solution should produce just random pixels displayed in the picturebox, but for some reason nothing is diplayed in the picturebox even if the compiler gives no error.
I could see different random pixels displayed in the picturebox, when i was using the same code in the event button click, so every time a clicked the button a new "image" was displayed.
Fredrik Bornander 23-Mar-12 5:13am
   
Ok, I've updated the suggestion to compensate for threading.
lukin4that 23-Mar-12 6:48am
   
Thanks a lot Fredrik,

now that i'm using the code you updated i get a C3374 error(can't take the address of form::Form1::PictureRefreshInternal unless creeating delegate instance), although a delegate is created just before.
I don't know much about delegates, i will try to read more and get rid of this error.
Fredrik Bornander 23-Mar-12 7:03am
   
That's my fault, I left out the delegate name by mistake.
Try it now.
lukin4that 23-Mar-12 7:33am
   
It is possible to compile the code, but when running it it generates an exception inside the method PictureRefresh() when calling Invoke.


(exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll
Additional Information: Invoke or BeingInvoke cannot be called on a control until the window handle has been created)

Fredrik Bornander 23-Mar-12 7:48am
   
You shouldn't try to update the image before the UI is created, that sounds weird.
To work around it, put a try catch around everything in the PictureRefresh method, just to see if it works.
lukin4that 23-Mar-12 8:57am
   
Actually the UI has been already created when the code is trying to update the image :O
Fredrik Bornander 23-Mar-12 9:27am
   
No it hasn't, not if you're getting that message.
[To start with, responding to the discussion in the comments to the question]

First of all, the update image data comes in the array mat. It should be done explicitly. How it is done, through the outer scope? This is wrong.

You are supposed to run some separate thread working with the network. Receiving data is blocking operation. After the data for the image is received, you should call your PictureRefresh method and pass data.

You cannot call anything related to UI from non-UI thread. Instead, you need to use the method Invoke or BeginInvoke of System.Windows.Threading.Dispatcher (for both Forms or WPF) or System.Windows.Forms.Control (Forms only).

You will find detailed explanation of how it works and code samples in my past answers:
Control.Invoke() vs. Control.BeginInvoke()[^],
Problem with Treeview Scanner And MD5[^].

See also more references on threading:
How to get a keydown event to operate on a different thread in vb.net[^],
Control events not firing after enable disable + multithreading[^].

Now, you use the index mat[counter/3], it causes integer division errors. Why doing so.

The use of PictureBox is redundant. This control is designed to be used in the simplest situations, static picture (or replaced rarely). If you do anything a bit more complex, it stops helping and only presents hassle in development, eats up your development time, CPU time and resources, not giving anything in return.

Compared to what? To using custom control derived directly from System.Windows.Forms.Control, using for rendering the event OnPaint or, better yet, overriding the virtual method Paint.

I'll explain what to do. Please see my past answers:
How do I clear a panel from old drawing[^],
draw a rectangle in C#[^].

See also these answers:
What kind of playful method is Paint? (DataGridViewImageCell.Paint(...))[^],
Drawing Lines between mdi child forms[^],
capture the drawing on a panel[^].

—SA
   
Comments
lukin4that 27-Mar-12 6:58am
   
Thanks for your suggestion.

I'm not sure about how to create a custom control to display images in a form and how to use it.

In this while i was still using the pictureBox control and i found out that it is refreshing the image only when i added a messageBox to pop up on the screen.

Why the picturebox is redrawing only after the MessageBox is displayed?

Thanks
lukin4that 27-Mar-12 9:48am
   
I was finally able to make it work by using my own pictureBox1_Paint method.

Thanks a lot for all your help and suggestions.
Sergey Alexandrovich Kryukov 27-Mar-12 11:51am
   
You are very welcome.
Good luck, call again.
--SA

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900