I'm using winforms in c#.
I simplified my app so that it just has a ListBox and a Button.
Here is my button click event:
private void button1_Click(object sender, EventArgs e)
{
for (long i = 0; i < 66000; i++)
{
listBox1.Items.Add(i.ToString());
listBox1.SelectedIndex = listBox1.Items.Count - 1;
}
}
When I run my app and push the button I see the ListBox updating and after a while (it varies entry 3041 or so) the program will appear to hang as it adds the rest of the entries once its done, the ListBox will appropriately refresh.
Why the hang? Is it just too much to handle? I looked at my cpu usage and memory didn't seem to be using that much so I'm not sure what the problem is.
You misinterpret what's going on. Your listbox will freeze after 5 seconds. That's when Windows steps in when it notices that you are not taking care of the normal duties of a UI thread. Your window is replaced by the so-called "ghost window" to tell the user you've gone catatonic and that doing things like clicking the Close button isn't going to have any effect.
The easiest way to tell that you got the ghost window is from its title bar, it says "Not Responding".
A version of it that will fare somewhat better:
private void button1_Click(object sender, EventArgs e) {
listBox1.BeginUpdate();
for (long i = 0; i < 66000; i++) {
listBox1.Items.Add(i.ToString());
}
listBox1.EndUpdate();
listBox1.SelectedIndex = listBox1.Items.Count - 1;
}
You shouldn't otherwise expect windows to behave reasonable when you do unreasonable things. And you shouldn't expect users to behave reasonable either when they are faced with this UI disaster. But they'll let you know themselves.
Every time you add an item, the memory it takes gets bigger, and since the items are in a dynamic list, the program may have to reallocate space.
Add to that the fact that you're probably using just one thread in that app (you're not using a background worker thread to update the list, right?) and you see why your app hangs. Windows forms always take a while when they have to update their controls... It's just that for normal, everyday use, they take milisseconds to update. For arcane uses like a list containing more than a few hundreds of items in the GUI, they'll take seconds or minutes to refresh, and you'll perceive that as hanging. Place the item handling in another thread, and you'll see that the form will update it's GUI pretty fast, but will take a long time between updates.
Related
my c# winforms program has one tabcontrol with a few tabs displaying listviews, labels, buttons etc. the tabpages are not shown on load. the code simplified looks like this:
public main()
{
InitializeComponent();
removeTabPages(); //removes all but one; deleting this line doesn't change anything
main_tabcontrol.SelectedIndex = 0; //doesn't change anything no matter where i put it
loadData();
doSomeCalculations();
addTabPages();
main_tabcontrol.SelectedIndex = 2; //same issue if i pick any other tab here
}
private void Tabs_SelectedIndexChanged(object sender, EventArgs e)
{
UpdateDataInTab();
}
this loads some data, does some calculations and then switches to tabpage 2. i would expect to see the data processed by the loadData() and doSomeCalculations() functions displayed. instead it displays the default values (mostly nothing) until i switch to another tab and then back. that also verifies Tabs_SelectedIndexChanged() works as intended.
i'd like to understand why this happens and how i can make it work as planned.
running loadData() and doSomeCalculations() as async tasks, and awaiting them, does solve this, but it opens so many other problems that i'd like to avoid it (i don't need this async). since my issue is the exact opposite (i need the code to run synchronous), this shouldn't be the solution anyways.
It is because you have it in your constructor, the best place to put it is on the load:
private void main_Load(object sender, EventArgs e)
{
main_tabcontrol.SelectedIndex = 0;
loadData();
doSomeCalculations();
main_tabcontrol.SelectedIndex = 2;
}
Never do anything beside the actual initialization in constructor
Use Loaded event to implement initial work
Avoid calculations and other CPU heavy work in UI thread. Use the backgroung thread to do this. Use async/await
To use Load Event double click empty space on the form or use Properties Explorer and switch to events.
I'm working on a simulation code where I have a "Project" which can hold numerous simulations. You can choose to run them one at a time, or you can run them all in sequence. In this specific case, I have 18 simulations which run one by one, and overall the process takes about 20 sec.
In short, a button is pressed on a form, and the following actions occur:
1) Create simulation object
2) Perform simulation start command
3) Write simulation data to file
4) Dispose simulation object
5) Update DataGridView which holds the simulation list (rewrites "Processing" to "Complete")
6) Update progress bar value in user control.
7) Refresh user control.
Rough source code is as follows:
for (int i = 0; i < dataSet.Count; i++)
{
using (Processor p = new Processor())
{
bool didTestPass = p.RunTest(dataSet[i]);
if (didTestPass)
dataGridViewProcessList.Rows[i].Cells[5].Value = "Run complete.";
else
dataGridViewProcessList.Rows[i].Cells[5].Value = "Run completed with errors.";
}
progressBarRuntime.Value = ((i+1) / dataSet.Count) * 100;
this.Refresh();
this.OnUpdateMainForm(this, null);
}
What I've found is, if you remain within the application's focus, all 18 simulations run fine. However, if you drop focus (say, switch to another program), it consistently behaves erratically at the 8th simulation. I say erratically because it acts differently:
When debugging through Visual Studio, the form freezes briefly, then suddenly all remaining simulations are processed and the progress bar snaps to full.
When running as a standalone program, it crashes straight to desktop. No warning, no exception throw, nothing.
I've also found that if I stay focused and let it reach, say, simulation 14, then drop focus from the program, it will immediately exhibit the above behavior.
I'm not particularly familiar with the concept of performing large calculation efforts under the hood while a Windows Form is active. At first I felt like maybe the Form needed to be refreshed (since this is all happening on a UserControl) but I saw no difference when I put in an event to force the Form to Refresh().
I ended up discovering that the source of my problem was that I was performing work on the interface's main thread, and as a result it was causing my program to hang and crash to desktop.
I created a BackgroundWorker and placed my work code into the DoWork event, then moved the ProgressBar indicator update into the ProgressChanged event.
More details regarding BackgroundWorker and its implementation at MSDN.
In short, the lesson learned for me is that if an activity in a Form is going to take longer than a few seconds, it should be done using a BackgroundWorker to prevent hanging up the interface.
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();
}
}
}
I am trying to use a background worker in order to retrieve a large amount of data from the database without stalling the main thread. This seems to work well, except that when it comes to update the UI, the update freezes the screen. Relevant code as follows:
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
lvwTest.BeginUpdate();
lvwTest.Items.Clear();
// Populate the UI
foreach (TestItem ti in testData)
{
ListViewItem lvi = lvwTest.Items.Add(ti.Value1);
lvi.SubItems.Add(ti.Value2);
}
lvwTest.EndUpdate();
}
The update takes around 2 - 3 seconds, for which time the screen is locked. I realise that only the main thread can update the screen, but is it possible to load this data into memory in some way (in a background thread or another instance of a listview or something) and then just display it? All I want to happen is for the program to simply refresh the data without taking up time in the main thread.
I recommend loading the data into memory and using a virtual mode ListView. That way, you only create the ListViewItem objects as they are needed.
If you have to load really huge amount of data into UI it will require time and it will block our app. The option is smart scrolling or pagination. You load all data but you put it piece by piece upon user request.
Since most of the above are good advice, but don't really solve your immediate problem, here is another approach:
This will update your GUI and keep it responsive. Assuming you are in WinForm App?
Application.DoEvents();
this.Refresh();
Nevertheless, this does not mean that maybe you should not listen to the ideas from above :-)
In addition to virtualization, I would recommend breaking the items into batches of, say, 100 and adding each batch in its own message. That way, the UI has a change to process other messages whilst the batches are being added to the ListView.
In other words, all the RunWorkerCompleted handler does is queue the first batch for adding in a separate message. The adding method will then add the items and then queue the next batch. This will continue until there are not more items left to add. At that point, you would re-enable the relevant portion of your UI (the ListView).
In the application I am working with, if the user changes the value in a cell that is say positive to negative and the value is supposed to be positive at all times, the application forces the positive value. Right now, when this happens there is no alert shown to the user.
I would like to show a little unobtrusive alert, like the one that shows up when a new mail arrives in outlook, or something similar, so that the user can be alerted that the application did something on her behalf.
I tried using the NotifyIcon class to do this. But the problem with that class seems to be that the timeout on it doesn't work as expected. I want to show this alert for not more than 2s and the BallonTipText lasts for longer than 10s.
Is there a .NET class for this purpose?
If not, is there an alternate way to do something like this?
Using a notification icon for this case seems wrong to me. The user's attention is, when entering something into a cell, on the cell. If you display the notification on the lower right of the screen the user is very likely to miss it, or worse, it disrupts his work flow.
You might instead consider adding a balloon tip to the cell the user is editing. Kinda like the balloon tip Windows Explorer is showing on Vista and Windows 7 on renaming a file when you try entering a character that is disallowed in file names:
I have had this problem in the past. I gather that the timeout problem is because the operating system fixes a minimum value of 10 seconds and a maximum value of 30 seconds (or something like that).Edit Oh and this doesn't include time that a user is idle.Edit
I have used the following code in the past to get around this.
Just to clarify
Declare a public variable, say called ballonTipActive with a value of 0.
Insert a timer control disabled with 100ms delay and create an event from BalloonTipShown from the notifyicon control.
Then
private void ptNotifyIcon_BalloonTipShown(object sender, EventArgs e)
{
timer1.Enabled = true;
balloonTipActive = 0;
}
private void timer1_Tick(object sender, EventArgs e)
{
balloonTipActive++;
if (balloonTipActive == 40)
{
ptNotifyIcon.Visible = false;
ptNotifyIcon.Visible = true;
balloonTipActive = 0;
timer1.Enabled = false;
}
}
Setting the visible property to false then true gets rid of the balloon.