Form not shown until the entire process is done - c#

I have a Winform application that receives the command line arguments and does some processing (creating a file, insert data to the database and upload a file to FTP). So in order to see the process I have a listbox that displays information to the user. There is also a progress bar to see the insert status.
Inside my form load event I have a RunCampaign() function that itself invokes some other functions sequentially until the process is done. All these functions add items to the listbox.
The problem is: Nothing (not even an empty form) is shown until the entire process is done. And when the process is complete the entire form containing the listbox of information and the progress bar is shown. And that is not my expected behavior.
How can I solve this problem?

You need to multithread your application so your UI thread is free to show the form:
private void Form1_Load(object sender, System.EventArgs e)
{
Task t = new Task(() =>
{
//Logic
//...
//Update UI
this.Invoke((MethodInvoker)delegate
{
listbox.Items.Add(...); // runs on UI thread
});
});
t.Start();
}
If you need more examples check out this question: How to update the GUI from another thread in C#?

The best way is to use a BackgroundWorker.
If you don't want to use any multi-threading technique, then call RunCampaign() function in Form_Shown() event. Also, invoke Listbox.Update() & Progressbar.Update() whenever you modify any item/value. At least you will see the window, but it will not be responsive until the control comes out of Form_Shown().

Related

Hosting multiple instance of app in a tabcontrol/ queuing

So I have This app with a MainForm that has a few buttons on it. and the buttons would do time consuming tasks that includes working with MainForm UI. Because some times we need to run multiple instances of this app at the sametime I decided to Create a MainFormHost where it is a form with a tab control which under each tab I create an instance of my MainForm and host it there. And so far everything was ok. The problem is when I click on a button on MAinForm1 it starts working fine but as soon as I click on a button on MainForm2 the process of MainForm1 button gets queued behind the MainForm2 process.
MainForm GetMainFrom(TabPage tabPage)
{
tabPage.Invoke(new Action(() =>
{
mainForm = new MainForm();
mainForm.TopLevel = false;
mainForm.FormBorderStyle = FormBorderStyle.None;
mainForm.Dock = DockStyle.Fill;
_mainForms.Add(mainForm);
tabPage.Controls.Add(mainForm);
mainForm.Show();
}));
}
And then call the method:
var mainFormThread = new Thread(() =>
{
mainForm = GetMainFrom(tabPage);
});
mainFormThread.SetApartmentState(ApartmentState.STA);
mainFormThread.Start();
I cannot change the code inside the MainForm to Invoke things when they want to work with the UI because it is going to be too much of work but I can easily make each button click to be called from a thread/task or...
TIA
In short, you can only have one 'main thread' that can interact with the UI controls. Ever. Period. There is no way to get around this (in standard code, you can draw to your form from a different process, but I won't go into that)
So, what you need to do is arrange your code in a set pattern.
Methods that do background work - this can be done on a separate thread(s)
Methods that Update the UI - MUST be done on the UI/Main Thread
The idea being simple, background work takes a long time, and updating the UI shouldn't.
Because you have 1 or 2 long running actions running ON THE MAIN UI THREAD, this 'blocks the UI' and causes the behavior your are experiencing.
By blocks the UI, what you actually have is the Windows Message pump can't pump. So every single command to resize a window or update a control on a form is a message in the pump. That must run on the main thread. If you do 'work' on that thread, such as connecting to a database, download a file etc., then the pump can't continue, so you get the 'this application is not responding' message.
So, you must download the file or whatever on a thread, and when it's finished, transition to the UI thread, and update the UI, e.g. say finished in a text box.
The way to transition between the two threads (background to main) is to use begininvoke, and you know if this is needed by using 'invoke required' .
From MSDN
// This method demonstrates a pattern for making thread-safe
// calls on a Windows Forms control.
//
// If the calling thread is different from the thread that
// created the TextBox control, this method creates a
// SetTextCallback and calls itself asynchronously using the
// Invoke method.
//
// If the calling thread is the same as the thread that created
// the TextBox control, the Text property is set directly.
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}
This is a great article and will get you where you need to be:
MSDN about thread safe code and invoke required

UI Container in thread?

brothers in code.
I'm trying to make my WinForms app multi threaded. In DoWork of my background worker I've got a method which changes few controls using MethodInvoker delegate. My question is if I have to invoke every control every time I want to change it from another thread or maybe there is some kind of container of controls which I can invoke to avoid multiple invoking certain controls?
Invoking means scheduling your code to run on the thread that owns the controls, which in all straightforward cases would be the very same thread for all of your controls. So while you do have to invoke every time you want to interact with a control, you can in practice "pool" as many interactions as you wish together and only invoke once for the whole piece (doing so will be more performant).
If you want to "hide" the invocations you 'd have to write a class that, when triggered, would detect changes to its properties and use Invoke on code that interacts with your controls in a manner dependent on these properties. So the workflow would be:
Your worker modifies the "controller"'s properties, without invoking. This does not have any immediate effect.
At some point, the controller is "triggered" (perhaps periodically by the worker?).
The controller detects (or already knows) what changes were made to its properties and how these translate to invoking code on controls. It invokes a block of code that interact with the controls accordingly.
My question is if I have to invoke every control every time I want to change it from another
thread or maybe there is some kind of container of controls which I can invoke to avoid multiple
invoking certain controls?
You have to invoke every time yiou want to change the UI. The invoke operation can do more than changing one peroeprty -it can be a complete function udpating 100 controls.
Minimizing invokes is good for performance.
No, there is no predefined container. You are assumed to be an able program an invoke, for example for an anonymous code block, yourself.
Let's say you want to change the text on two Labels. Assuming they belong to the same Form, you can do this either by individual calls to Invoke...
void buttonInvoke_Click(object sender, EventArgs e) {
Invoke((Action)(() => label1.Text = "A1"));
Invoke((Action)(() => label2.Text = "A2"));
}
...or by grouping then in a single Invoke, to save some typing and increase performance.
private void buttonInvoke_Click(object sender, EventArgs e) {
Invoke(
(Action)(() => {
label1.Text = "B1";
label2.Text = "B2";
})
);
}
Technically all of your GUI is created on the main thread, so If you invoke say a main panel on the GUI then within that invocation method you can alter other controls on the GUI all within that method
Plus if your background worker was created on main thread then you can call report progress event back on main thread... Which means invocation not required. Main purpose of background workers really.
public void UpdateControl<T>(T control, Action<T> action) where T : Control
{
if(control.InvokeRequired)
control.Invoke(() => action(control));
else
action(control);
}

WinApp Form loading too much data during the Load event

I have a form that is loading quite an amount of data from SQL server. Below is the code that will provide a good hint:
private void BranchCenter_Load(object sender, EventArgs e) {
//Combo boxes:
LoadCities();
LoadCoordinators();
LoadComputerSystems();
//Actual entity
LoadBranch();
}
private void LoadCities() {
//LINQ2SQL to load data. ~5000 records.
}
private void LoadCoordinators() {
//LINQ2SQL to load data. ~50 records.
}
private void LoadComputerSystems() {
//LINQ2SQL to load data. ~550 records.
}
private void LoadBranch() {
LoadBranchInit();
LoadBranchDetails();
LoadBranchTimings();
LoadBranchServices();
LoadBranchLocumsHistory();
LoadBranchJobs();
LoadBranchNotes();
}
private void LoadBranchInit() {
//LINQ2SQL to load the Branch object based upon the Branch ID
}
private void LoadBranchDetails() {
//LINQ2SQL to load basic branch stuff. 38 fields. Mixed editors.
}
private void LoadBranchTimings() {
//LINQ2SQL to load timings info into 80 date-time controls
}
private void LoadBranchServices() {
//LINQ2SQL to load services offered at branch info into 20 check-boxes controls
}
private void LoadBranchLocumsHistory() {
//LINQ2SQL to load branch history info into grid control. Always increasing # of rows :(
}
private void LoadBranchJobs() {
//LINQ2SQL to load branch jobs info into grid control. Always increasing # of rows :(
}
private void LoadBranchNotes() {
//LINQ2SQL to load branch notes info into grid control
}
The UI is a form with a tab controls. each detail from above goes to a tab page. I need to load and show the form to user as fast as possible. once the form is shown, i need to launch a series of background workers to get the data for each page.
I have been trying to mess with the background worker but unable to understand it's usage. I end up getting message of "different thread attempted to access control on your main thread... or something like that..."
The ideal situation would be to have a progress bar loading the data on every tab and the tab becoming inter-actable once the respective background worker finishes.
Any strategy or advice? Thanks for the read.
If each type of data is displayed on just one page, I would move the code so that each corresponding type of data is loaded the first time each tab page is loaded. That way the data loading work will be distributed over a longer time, and perhaps avoided altogether if the user does not navigate to all tabs during a session.
When loading a page, you could use a BackgroundWorker or any other asynchronous mechanism. In the tab page you can have two panel controls. One that contains the UI for the page, and that has Visible = false when the form is loaded, and one containing a label with a text like "Loading, please wait...". When the data is loaded for the page, and the UI is updated, toggle visibility on the two panels to show the UI. That way the form will still be responsive while the data is loading, and since you load data for only one page at a time, load times should be fairly short.
the ideal solution is that you do not really load data in the form load event.
Let the form load properly and entirely so the UI render completes.
After that you could display a progress bar wherever you want and you could execute the actual data loading, either synchronous or asynchronous.
if you don't need everything loaded at the same time I would also consider loading data only the first time a tab is accessed, so if the user never clicks on the last tab, for example, you didn't load anything for it.
Create a BackgroundWorker for each of the tabs. In the Load event of the form, disable all tabs and call the RunWorkerAsync() method for each of the background workers.
In each DoWork event handler, load the data required for the associated tab page from the database into a data table and set the Result property of the DoWorkEventArgs to the data table.
Note: In the DoWork event handler you should NOT update any UI control since it is operating in a different thread. You should only retrieve the data from the database and set the Result property.
In the RunWorkerCompleted event handlers, you can access the data table, that was retrieved in the DoWork event handler, by getting the Result property of the RunWorkerCompletedEventArgs. Then you can set the properties of the UI controls accordingly and then enable the tab page associated with the current background worker.
The problem here is that the DoWork method of the BackgroundWorker cannot touch the GUI.
It has to use the BackgroundWorker.ReportProgress( int progress, object state) to invoke a method on the GUI thread that is free to update the GUI.
The important point here is that the GUI controls should only be updated from the GUI thread itself, else random exceptions will occur here and there in your program.
You can't mess around with the UI from any other thread apart from the UI thread; that's the source of your errors. If you want more information on how to properly update a UI element from a different thread, you'll need to use Invoke. This question on Stack Overflow might help you out.
Yes, BackgroundWorker is great for this.
I think that you do it wrong because you want to change your form in the DoWork event.
However, you should only collect results of this in e.Results of that event
Then in RunWorkerCompleted you use its e.Results to change the form.
The TabControl already does lazy-loading.
Do yourself a favor and place the contents of each tab on a UserControl.
To address just the problem you are encountering with the background worker thread, it sounds like you may be trying to access UI components from the thread itself. To do this from a non-UI thread you will need to make use of Control.Invoke rather than trying to access the components directly.
Okay, I face a new scenario. I shifted to Visual Studio 2010 and ran into Task.Factory.StartNew(() => myMethod());
This is what I have so far:
private void LoadLocum() {
var TaskInit = Task.Factory.StartNew(() => LoadLocumInit());
TaskInit.Wait();
var TaskDetails = Task.Factory.StartNew(() => LoadLocumDetails());
TaskDetails.Wait();
var TaskQualifications = Task.Factory.StartNew(() => LoadLocumQualifications());
//Enable Qualification TabPage automatically whenever TaskQualifications is complete
Parallel.Invoke(
() => LoadLocumComputerSystems(),
() => LoadLocumOtherInfo(),
() => LoadLocumEmergencyContacts(),
() => LoadLocumDistanceRates(),
() => LoadLocumDocuments(),
() => LoadLocumNotes(),
() => LoadLocumBannedStatus()
);
}
The first two steps (tasks) are critical. Now, the Tab Pages of the Tab are disabled. I need to enable them based upon their related task completion. I can find an event to subscribe to that indicates a certain task is completed or not.
Okay. Massive response in just about no time. Love this place. Jumping from link-2-link, I came face to face with Reactive Extensions for .NET (Rx). Can this be a good alternate as well? Can't find a tutorial on how to use Rx in my scenario.

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?

Delay loading of combobox when form loads

I've got a Windows Forms (C#) project with multiple comboboxes/listboxes etc that are populated when the form loads.
The problem is that the loading of the comboboxes/listboxes is slow, and since the loading is done when the form is trying to display the entire form isn't shown until all the controls have been populated. This can in some circumstances be 20+ seconds.
Had there been a Form_finished_loaded type of event I could have put my code in there, but I can't find an event that is fired after the form is done drawing the basic controls.
I have one requirement though - the loading has to be done in the main thread (since I get the items from a non-threading friendly COM-application).
I have found one potential solution, but perhaps there is a better way?
I can create a System.Timer.Timer when creating the form, and have the first Tick be called about 1 second later, and then populate the lists from that tick. That gives the form enough time to be displayed before it starts filling the lists.
Does anyone have any other tips on how to delay the loading of the controls?
There is the Shown event that "occurs whenever the form is first displayed.". Also you may want to use the BeginUpdate and EndUpdate functions to make the populating of your combobox faster.
It has that certain smell of workaround, but this approach should fulfil your needs:
private bool _hasInitialized = false;
private void Form1_Shown(object sender, EventArgs e)
{
if (!_hasInitialized)
{
ThreadPool.QueueUserWorkItem(state =>
{
Thread.Sleep(200); // brief sleep to allow the main thread
// to paint the form nicely
this.Invoke((Action)delegate { LoadData(); });
});
}
}
private void LoadData()
{
// do the data loading
_hasInitialized = true;
}
What it does is that it reacts when the form is shown, checks if it has already been initialized before, and if not it spawns a thread that will wait for a brief moment before calling the LoadData method on the main thread. This will allow for the form to get painted properly. The samething could perhaps be achieve by simply calling this.Refresh() but I like the idea of letting the system decide how to do the work.
I would still try to push the data loading onto a worker thread, invoking back on the main thread for populating the UI (if it is at all possible with the COM component).
Can you get your data from a web service that calls the COM component?
That way, you can display empty controls on a Locked form at the start, make Asynchronous calls to get the data, and on return populate the respective combos, and once all of them are loaded, you can unlock the form for the user to use.
You could listen for the VisibleChanged event and the first time it's value is true you put your initialization code.
Isn't FormShown the event you're looking for?
When you say that you cannot use a background thread because of COM what do you mean? I am using many COM components within my apps and running them on background threads.
If you create a new thread as an STAThread you can probably load the ComboBox/ListBox on a Non-UI thread. IIRC the ThreadPool allocates worker threads as MTAThread so you'll need to actually create a thread manually instead of using ThreadPool.QueueUserWorkItem.

Categories

Resources