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.
精彩评论