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!)
Related
I have to show a Window on an application that is not very well thought. The calls from every window are made from the same thread, no background workers. The application is rather yuge, so I can't just go around implementing BW pattern. Problem is, some of those actions take a while (around 5-10 seconds) and the whole app would turn Not Responding while waiting.
My idea was to implement some kind of watchdog on a background thread that would pop a window whenever I set a flag (App.Loading) in order to let the user know that something is going on.
In order to do that, I put this in the main menu
public Menu()
{
ShowInTaskbar = true;
InitializeComponent();
Debug.Ventana = "Menu";
App.Menu = this;
Loading = new Thread(new ThreadStart(CheckLoading));
Loading.SetApartmentState(ApartmentState.STA);
Loading.Start();
}
private void CheckLoading()
{
Loading load = new Andrei.Loading("Cargando datos")
{
ShowInTaskbar = false,
Topmost = true
};
while(true)
{
if (App.Loading && !load.IsVisible)
load.Show();
if (!App.Loading && load.IsVisible)
load.Hide();
}
}
Problem is, sometimes the window does not show up, and when it does, the animation isn't working (it's a small window with a string and an Indeterminate Progress Bar), which must be because the main thread is frozen by the while(true). I've tried putting the loop in yet another thread, but I can't access the window from another thread to hide and show it. I've tried putting a BW on the window to check for progress, but of course, after completing the first cycle, it doesn't go looking for the flag anymore.
Any other suggestions than completely redesigning the application to use BW for any and all time-taking processes?
I have only a little experience with threading in WinForms. I am trying to set a progress bar to 100, sleep for 2 seconds, and then hide the progress bar.
I have:
public void ProgressBarTimerStop()
{
if (!AnyWorkersBusy())
{
if (InvokeRequired)
{
Invoke((MethodInvoker)delegate
{
ProgressBarTimer.Stop();
Thread.Sleep(1000);
Application.DoEvents();
Thread.Sleep(1000);
StatusToolStripProgressBar.Visible = false;
StatusToolStripProgressBar.Value = StatusToolStripProgressBar.Minimum;
});
}
else
{
ProgressBarTimer.Stop();
StatusToolStripProgressBar.Value = StatusToolStripProgressBar.Maximum;
Thread.Sleep(1000);
Application.DoEvents();
Thread.Sleep(1000);
StatusToolStripProgressBar.Visible = false;
StatusToolStripProgressBar.Value = StatusToolStripProgressBar.Minimum;
}
}
}
For the current problem I am experiencing InvokeRequired is false.
When I call the line:
StatusToolStripProgressBar.Value = StatusToolStripProgressBar.Maximum;
my watch window shows the value as set, but the GUI has not updated to reflect this. If I immediately sleep for a full two seconds the GUI does not update before sleeping.
If I call Application.DoEvents() without calling sleep beforehand -- it appears to do nothing.
If I sleep, then call DoEvents -- the GUI updates, and then I go back to sleep. This is correct, but I feel like my code is heinous.
Am I misunderstanding a key concept here?
EDIT: ProgressBarTimerStop is called only through "RunWorkerCompleted" event handlers.
I tried this, but it had no effect:
StatusToolStripProgressBar.GetCurrentParent().Refresh();
The 'key concept' that you are missing is that updating the progress bar's value does not immediately tell the progress bar to redraw itself.
When you update the progress bar, the GUI thread repaints the progress bar on the next window repaint. This only happens when the GUI thread becomes free to do so. Normally this happens when the window receives a paint message from the operating system. This should be picked up by Application.DoEvents, but it could be that the message isn't queued quickly enough to be picked up by DoEvents. You may be able to force it to with a Refresh call.
Call StatusToolStripProgressBar.ProgressBar.Refresh(); on every step. ProgressBar property of ToolStripProgressBar is hidden and it may not be shown in intellisense list.
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.
I have the follow button click event:
private void btnRun_Click(object sender, EventArgs e)
{
label1.Visible = true;
if (SelectDatabase())
{
if (string.IsNullOrEmpty(txtFolderAddress.Text))
MessageBox.Show("Please select a folder to begin the search.");
else
{
if (cbRecurse.Checked == false || Directory.GetDirectories(initialDirectory).Length == 0)
{
CheckSingleFolder();
}
else
{
CheckSingleFolder();
directoryRecurse(initialDirectory);
}
}
}
}
Effectively, it does a few checks and then starts some directory recursion looking for specific files. However, the very first line of code to make the label visible does not occur until after the directories have been recursed? Anybody know why this would be?
Thanks.
You're doing everything within the UI thread, which is a really bad idea - the UI doesn't get to update, react to events etc until you've finished.
You should use a background thread and update the UI with progress etc using Control.BeginInvoke, or perhaps use a BackgroundWorker.
Basically, there are two golden rules in WinForms (and similar with WPF/Silverlight):
Don't do anything which can take a significant amount of time in the UI thread
Don't touch any UI elements from any thread other than the UI thread
your whole method runs as a blocking unit currently - add an Application.DoEvents() as a workaround, but really you should be doing this kind of processing in a background thread, i.e. using a background worker.
The code is executing on the same thread which is drawing your user-interface. Therefore, while the code is executing, your UI is not being re-drawn. Once the button-click code has finished, the UI is redrawn and label1 gets drawn invisibly.
You can move your code into a separate thread using, for example, Task or BackgroundWorker. However, you cannot directly set UI properties from a different thread, so you will need to either be careful to set your UI properties from the UI thread or see this question about how to update the GUI from another thread.
The view is not updated until the code block finished. So I would propose a BackgroundWorker for the recursion part.
The explanation: the label is set to visible and it it is Invalidated (needs repainting) but the Windows message pump doesn't start repainting until it is running idle. So your code blocks it.
A simple solution is to call label1.Update() right after setting it visible.
A better solution is to move the time-consuming code to a thread (Backgroundworker).
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.