Thread Crossing in Windows Forms Application - c#

I have used two threads in my application(WindowsForms). The one thread for getting ThumbnailImages of clients and another thread for getting fullsize Images of Clients...its working but not properly... When i click thumbnail image button its giving thumbnail image properly after that i click that fullsize image button,It came as fullsize image and thumbnail images too... then return back to thumbnail image button it came as thumbnail images and Fullsize images also....
The two threads are crossing Thats why i didnt get proper output....
How can i Solve this Problem? Tell me the solution Of this...
Here is my Code....
{
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
th = new Thread(new ThreadStart(startlooping));
th.Start();
}
void StartLooping()
{
localconnection();
for (int i = 0; i < Num_Picbox; i++)
{
clsImage pcimg = frmDisplay.Serviceobj.ConnectToPcAndGetImage(listBox1.Items[i].ToString(), imgid);
Image img = objConvertByteToStream.byteArrayToImage(pcimg.pcimage);
if (listBox1.Items[i].ToString() == pcimg.IPadd && imgid == 0)
{
shapes[i].Image = img;
}
pcimg = null;
}
}
private void PictureBox_Click(object sender, EventArgs e)
{
pb= sender as PictureBox;
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;
thread = new Thread(new ThreadStart(GetImage));
thread.Start();
}
void GetImage()
{
pictureBox1.Visible = true;
btnBack.Visible = true;
pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
label1.Visible = true;
pictureBox1.Height = this.flowLayoutPanel1.Height;
pictureBox1.Width = this.flowLayoutPanel1.Width;
btnGetConnectedPcs.Location = new Point(168, 616);
btnGetImageFromSelectedPc.Location = new Point(352, 616);
for (; ; )
{
count++;
int imageid = 1;
localconnection();
string strIpaddress = listBox1.Items[Convert.ToInt32(pb.Name)].ToString();
.........................
.........................
}
}

Do not use CheckForIllegalCrossThreads and use Invoke on the controls you're using.

I think you should use the BackgroundWorker, instead. Don't ever use CheckForIllegalCrossThreadCalls; it's there for a reason.
It deals with "marshalling" (getting calls on one thread to switch to another thread for UI updating purposes).
Here's a good starter: http://bigballofmud.wordpress.com/2009/03/28/thread-marshalling-part-2-using-backgroundworker/
Also, you need to control the UI when the other threads are running. Typically by disabling buttons/menus etc to prevent the user asking for more things like one is already running.
At the very least you should be using Control.Invoke. When your thread has finished downloading images, it should call a method to display them. That method should then check this.InvokeRequired, and if it is true, should call itself via this.Invoke or this.BeginInvoke. This will effectively switch back to the main UI thread and avoid the errors you were probably seeing.
Plz to send me the codez?
Okay, this time you can have a bit more, but you also need to master the skill of taking some directions then finding your own way. If we help you with actual code for this problem you'll just come back when you get stuck next time. We'd prefer to set you going like a wind-up toy than steer you the whole way like a remote-controlled car :)
BackgroundWorker
I'm going to refer you to the BackgroundWorker article on MSDN for this.
The BackgroundWorker is a component you drop on your form from the toolbox. It's not a visual control like a textbox, instead it's more like a Timer or Tooltip.
Using it in it's simplest form means subscribing to the DoWork and RunWorkerCompleted events, as described in the MSDN article. When you want some downloading to happen, you need to do the following:
Disable your UI (to prevent subsequent button presses).
Set some sort of progress/working animation or message.
Call BackgroundWorker.RunWorkerAsync, passing in an object containing parameters that tell it what to do (i.e. download large or small images).
The DoWork event in your case will be raised, and your method will be running in a different thread to your UI, so you are not allowed to update the UI at all in this method. Write your DoWork method to download the images and set the DoWorkEventArgs.Result property to that collection of images once the download is complete.
When the DoWork method completes, the BackgroundWorker will fire the RunWorkerCompleted event, and the method you subscribed to it will be called. This code will now automatically be running on the UI thread again, so you should do the following:
Report on any errors in the RunWorkerCompletedEventArgs.Error property (essentially this is any untrapped errors that occurred in the DoWork method. Deal with them here so you can use the UI to inform the user of the problem).
Create your controls to display the images contained in the RunWorkerCompletedEventArgs.
Hide any progress indicators.
Re-enable your UI (preferably using a finally block so that any exceptions don't result in your UI being permanently and irretrievably locked).

You may want to take a quick look at Control.InvokeRequired(). That is likely to shed some light and give you a clear path to success.
Good luck.

Related

BackgroundWorker to fill a DataGridView

EDIT: Solved using this: http://reedcopsey.com/2011/11/28/launching-a-wpf-window-in-a-separate-thread-part-1/
In my project (.net/windows forms) I'm filling a DataGridView with a large DataTable. Filling can take up to 20 seconds, so I'd like an animated loading window. This animation freezes if the thread is busy, so I'll have to use a new thread either for the window or for filling the DataGridView.
I've tried using the BackgroundWorker to show the form, but it'll be a blank white window in the correct shape.
I've also tried using the BackgroundWorker to fill the DataGridView, but it'll throw an error saying the DataGridView is being accessed by a different thread than the one it has been created for. Since the DataGridView is being created in the designer class, I can't just create it in the new thread - plus that solution doesn't sound very elegant.
What's the best way for me to show a working animated form while filling the DataGridView?
Edit: The answer didn't solve the issue for me, so I've tried to break the code down to something that can be presented here. I didn't do it before because it didn't really seem relevant enough to work through some 1k lines of code. Might be something missing or some remnants from previous experiments in the code presented here. Please ignore bad naming of function, that's a legacy thing I'll fix once I got it working. Parts of the code are ancient.
I've made it run without errors, but the frmLoading still isn't animated (it is if I keep it alive while the working thread isn't busy, though).
namespace a
{
public partial class frmMain : DockContent, IPlugin
{
//...
private delegate void SafeCallDelegate(DataTable dt);
private Thread thread1 = null;
private frmLoading frmLoading = new frmLoading();
public frmMain()
{
//...
}
//...
private void FillDataGrid(DataTable dt)
{
if(this.InvokeRequired)
{
var d = new SafeCallDelegate(FillDataGrid);
Invoke(d, new object[] { dt });
}
else
{
//...
DataGridFiller(dt);
}
}
private void DataGridFiller(DataTable dt)
{
BindingSource dataSource = new BindingSource(dt, null);
//...
dgvData.DataSource = dataSource;
//...
frmLoading.Hide();
}
private void btnGetData_Click(object sender, EventArgs e)
{
DataTable dt = [...];
// Wenn Daten vorhanden sind, dann anzeigen
if (dt != null)
{
//...
frmLoading.Show();
thread1 = new Thread(() => FillDataGrid(dt));
thread1.Start();
}
}
}
}
The second approach is the correct one: use a BackgroundWorker to do any work that will freeze the UI if done in the main thread. About the exception you're getting, that's because the call you're making isn't thread-safe (Because the control was created on a different thread that the one whos calling its methods). Plase take a look at this link to MSDN to understand how to make this kind of call between threads, using backgroundWorker's event-driven model.
From the link:
There are two ways to safely call a Windows Forms control from a
thread that didn't create that control. You can use the
System.Windows.Forms.Control.Invoke method to call a delegate created
in the main thread, which in turn calls the control. Or, you can
implement a System.ComponentModel.BackgroundWorker, which uses an
event-driven model to separate work done in the background thread from
reporting on the results.
Take a look at the second example which demonstrates this technique using the backgroundWorker
EDIT
From your comments I get that the real problem here is size. The problem is that the thread that owns the DataGridView control, is the thread that is displaying it, so no matter how, if you load all the data at once it will freeze for the time it takes this thread to draw all that data on the screen. Fortunately you're not the first that had this problem, and microsoft got you covered this time. This is, I think, a great example of what the XY problem is, the real problem is that you want to load a huge dataset (X) and the answer on how to do that is here, but instead you asked how to display a loading icon while filling your datagrid whithout the UI freezing (Y), which was your attempted solution.
This was from a technical perspective but, from an UI/UX perspective I would like you to think about the actuall use of the form. Do you really need all that data loaded every time you visit this section? If the answer is no, maybe you could think about implementing some pagination (example here). Also 200 colums means horizontal scroll even for an ultrawide monitor. I can't really figure out the user case for you to need all that information on a list/table view at once. I think that maybe implemeting a Master-Detail kind of interface could be more useful.
EDIT 2.0
I think that you can try to create a whole new Form window in another thread, place it over your datagrid and paint the loading animation there. Meanwhile in the main window you can draw the grid without its painting making the loading animation freeze (the other thread is taking care of the drawing). You may want to hold a reference to the thread and kill it, or maybe better try to hold a reference to the From and close it more gracefully.
I've never done this but, I think I recall from some legacy application doing something like that, and it was ugly.
Use an asynchronous task that displays the pop-up loading window until the DataGridView is filled up.
Here's a link to a write-up Microsoft made for async programming: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/

C# WPF Window not Updating in foreach loop

in my project I need to load "projects".
I am loading all this "projects" in a foreach loop.
In this foreach loop, I want to show a status window with a progressbar, which gets updated everytime a new project got load.
My problem now is, that the status window with the progrssbar wont update.
It seems it is just "freezed" like all other windows in my project (I also let the programm printing what it is doing into a console).
Here is the Code:
Status StatusWd = new Status();
StatusWd.pb_Status.Maximum = animes.Length;
StatusWd.SetStatusText("Animes in AnimeBibliothek laden...");
StatusWd.Show();
bool white = false;
bool black = true;
foreach (string anime in animes)
{
if (white)
{
//Not working..
AB_Output.WriteLine(GetTime() + "Weißes User Control laden...");
AddWhiteUcpToStk(anime);
white = false;
black = true;
}
else if (black)
{
AB_Output.WriteLine(GetTime() + "Schwarzes User Control laden...(" + anime + ")");
AddBlackUCPToStk(anime);
//white = true;
//black = false;
}
StatusWd.pb_Status.Value = Array.IndexOf(animes, anime);
}
StatusWd.Close();
It´s not just the progressbar which wont update. Also the labels in the window, which get updated when the value changes, wont change. I debuged it and the value from the progressbar changed and the label content changes...
Would be nice if you could explain me your solution.
LG Sinmson
Blocking the UI thread will do that....
Any time you are running a long operation, you need to make sure it is on some other thread, otherwise you will be seen as "non-responsive".
So, first thing is to move your loading onto another thread. Thread, BackgroundWorker and Task are all reasonable ways to do this. Then you need to marshal any UI changes back to the UI thread.
If you used async (and Tasks) then the framework does this for you. Similarly, if you use a proper MVVM pattern PropertyChanged events will automatically marshal onto the correct thread.
If you can't do it a "right" way, then marshal such changes yourself using Dispatcher.BeginInvoke.
Dispatcher.BeginInvoke(new Action(() =>
{
//UI code ONLY
}), DispatcherPriority.Normal);
But do so only as a band-aid. Move to asynchronous or MVVM based code (or even better, both!)

Implementing a Progress Bar in C#

Attempt number 2. Trying to be as clear as possible because I have been on this task for much longer than I should have and made little progress.
I need to make a progress bar for an application. Right now, everything is happening in one UI thread, all the processing and whatnot, so when I do the long running process after a click of a button, the program hangs for roughly 40 seconds, then resumes with the output (I cannot change that part, the application was given to me). And I also have to make a cancel button, so if it is hit during the intermediate process, after that process is done, it checks for the Cancel flag, and if it is ON, break out of all the methods and return to initial position.
What I have to do is make a progress bar for this process. Two of them, one for intermediate process and one for total. Intermediate is a small action, like DB call, output intermediate process (for and foreach loops).
I have tried putting the bars in the same UI window and update them in the same thread as I go on (find major points, start the progress bars there and increment them based on my judgement. And loops). That was just loading the UI even more, but it was working (choppy progress bar movements).
Then I tried using the background worker. Same thing, put the bars in the UI and run them through the background worker. The problem with that even though the BG worker runs, the refresh must happen in the UI window, which is busy processing the greedy request, so it is not updated when I want it to.
I tried using multi threading at core (no BG worker) and create a new form in a new thread that would reflect the progress, but I was cautioned not to do it (running 2 UI threads at the same time) and I had troubles passing values around. Not to mention if using the threading method it is impossible to get an accurate representation of the progress happening without completely overloading the CPU (have another thread run in a while(true) and do an update when you call the updateMethod from some point in the process)
Do I have any other options left? Because the least negative aspect is the first where I update the progress bar as I go along with the program.
Do I have any other options left?
Yes and no. The best option is still to put time to learn how to use the backgroundworker properly, it's by far the easiest solution for this problem.
Go ahead and study these links:
Official MSDN Backgroundworker
Step by Step Tutorial Number 1 (dotnetperls)
Step by Step Tutorial Number 2 (dreamincode)
Most important thing is to set this property:
bw.WorkerReportsProgress = true;
And in the DoWork-Function, make sure you report the progress to the UI, as given in the linked examples:
worker.ReportProgress((i * 10));
Also, don't forget to handle the progresschanged event in the UI:
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.tbProgress.Text = (e.ProgressPercentage.ToString() + "%");
}
Go ahead, and you will see: it is fairly simple.
When using a background worker you should be doing all of the non-UI processing in the background worker; you should not be using it to update the UI while still doing processing in the UI thread.
By moving the work to the DoWork handler of the BGW you free up the UI thread to allow it to process UI events, including the updating of the progress bar.
private void btnCreate_Click(object sender, EventArgs e)
{
Progressbar1.Minimum = 1;
Progressbar1.Maximum = dt.Rows.Count;
Progressbar1.Value = 1;
Progressbar1.Step = 1;
for (int i = 1; i <= dt.Rows.Count; i++)
{
bool result=InsertUserdetails(Username);
if(result== true)
{
Progressbar1.PerformStep();
}
}
}

Toggling Picture Box visibility C#

Why is the picture box control's visibility property not working here. I have initially set them to false, so that when the screen loads they are not visible. But then I wish to toggle this. I have done the following but does not seem to work. This is a windows forms application.
private void Action()
{
while (true)
{
Random r1 = new Random();
int num = r1.Next(1,3);
switch (num)
{
case 1:
pictureBoxLeft.Visible = true;
pictureBoxRight.Visible = true;
break;
case 2:
pictureBoxLeft.Visible = true;
pictureBoxRight.Visible = false;
break;
case 3:
pictureBoxLeft.Visible = false;
pictureBoxRight.Visible = true;
break;
}
Thread.Sleep(200);
pictureBoxLeft.Visible = false;
pictureBoxRight.Visible = false;
Thread.Sleep(500);
}
}
Also to add, this is working properly with a text box!!! Any Ideas???
Many thanks in advance
Setting the Visible property to true doesn't show the control, it only creates a message that will show it. As long as you are keeping the main thread busy with a Sleep, it will not process any messages, and the controls won't be shown.
You should show the picture boxes and then set up a timer with code that will hide the picture boxes when the timer's tick event is triggered. Then you exit your method so that the main thread can handle the messages that will show the picture boxes.
This is your UI thread. UI thread is so busy that it is not getting time to refresh the display. UI thread is busy in endless while loop, so how can it update UI?
In addition to Thread.Sleep(); call Application.DoEvents() to trigger event handling/ui updating. However ensure you're not calling Action() again somehow. A simple Thread.Sleep() won't allow the UI to update as you're still in the handler and the framework won't be able fire any new events on its own as it won't know if you're finished when waiting (due to the thread still being busy).
Overall your approach might be a bad idea (depending on other code etc.). Consider using timers or a background worker instead.
Whenever the UI changes does not apear after you did some changes, always do Refres(). e.g. pictureBoxLeft.Refresh();. This will always make UI changes appear. Please let me know if it works for you.

Run a modal dialog on a non-UI thread

I'm writing a simple data UI using standard .Net databinding to a typed DataSet from SQL Server.
I have a reload button which calls Fill on all of the DataAdapters to get new data from the database (in case another user changed the data).
This takes some time, during which the UI is frozen. It must be run on the UI thread or the databinding event handlers throw cross-thread exceptions.
I'd like to show a modal "Please Wait" dialog on a background thread (so that it can be animated) while the UI thread connects to the database.
How can I show a modal dialog box on the non-UI thread?
EDIT: I'm aware that best practice is to run the operation in the background, but I can't do that because of the databinding events.
You should do the opposite. Run your long-running process on a background thread and leave the UI thread free to respond to the user actions.
If you want to block any user actions while it is processing you have a number of options, including modal dialogs. Once the background thread completes processing you can inform the main thread about the outcome
The code running in the databinding events need to be decoupled from the UI, probably using some kind of data transfer object.
Then you can run the query operation in a separate thread or a BackgroundWorker, and leave the UI thread as it was.
Edit: The really quick way to fix this is to get the events to run in their own delegate using InvokeRequired and .Invoke. That will give the methods UI context. My co-worker does this like it's going out of style and it annoys me to no end because it's rarely a good idea to do it this way... but if you want a fast solution this will work. (I'm not at work so I don't have a sample with me; I'll try to come up with something.)
Edit 2: I'm not sure what you're asking for is possible. I made a sample app that created a modal dialog in another thread, and it ends up being modeless. Instead of using a modal dialog, could you use some other control or set of controls to indicate progress change, most likely directly on the same form?
using( var frmDialog = new MyPleasWaitDialog() ) {
// data loading is started after the form is shown
frmDialog.Load += (_sender, _e) {
// load data in separate thread
ThreadPool.QueueWorkItem( (_state)=> {
myAdapter.Fill( myDataSet );
// refresh UI components in correct (UI) thread
frmDialog.Invoke( (Action)myDataControl.Refresh );
// close dialog
frmDialog.Invoke( (Action)frmDialog.Close() );
}
}
// shows dialog
frmDialog.ShowDialog( this );
}
Here is an example of using BackgroundWorker to do the loading of data and running a user friendly form to show 'Loading records' or similar...
public void Run()
{
bgWorkrFillDS = new BackgroundWorker();
bgWorkrFillDS.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorkrFillDS_RunWorkerCompleted);
bgWorkrFillDS.DoWork += new DoWorkEventHandler(bgWorkrFillDS_DoWork);
bgWorkrFillDS.RunWorkerAsync();
}
void bgWorkrFillDS_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bgWrkrFillDS = (BackgroundWorker)sender as BackgroundWorker;
if (bgWrkrFillDS != null)
{
// Load up the form that shows a 'Loading....'
// Here we fill in the DS
// someDataSetAdapter.Fill(myDataSet);
}
}
void bgWorkrFillDS_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Hide or unload the form when the work is done
}
Hope this helps...
Take care,
Tom.
I solved this problem by creating a new DataSet, loading in in the background, then calling DataSet.Merge on the UI thread. Thanks everyone for your advice, which led to this solution.
As an added bonus, this runs much faster than it used to (calling Fill in the background, which only worked with no grids open). Does anyone know why?

Categories

Resources