WinApp表单在Load事件期间加载了太多数据

时间:2011-02-08 13:23:10

标签: c# winforms backgroundworker

我有一个从SQL Server加载大量数据的表单。下面是提供良好提示的代码:

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
}

UI是一个带有标签控件的表单。上面的每个细节都转到标签页。我需要加载并尽快向用户显示表单。一旦表单显示,我需要启动一系列后台工作程序来获取每个页面的数据。

我一直试图搞砸背景工作者但却无法理解它的用法。我最终得到的消息是“尝试在主线程上访问控制的不同线程...或类似的东西......”

理想情况是在每个选项卡上都有一个进度条加载数据,并且一旦相应的后台工作人员完成,该选项卡就会变得可以互动。

任何策略或建议?感谢您的阅读。

10 个答案:

答案 0 :(得分:3)

如果每种类型的数据只显示在一个页面上,我会移动代码,以便在第一次加载每个标签页时加载每个相应类型的数据。这样,数据加载工作将在较长时间内分发,如果用户在会话期间未导航到所有选项卡,则可能完全避免。

加载页面时,您可以使用BackgroundWorker或任何其他异步机制。在标签页中,您可以拥有两个面板控件。一个包含页面的UI,并且在加载表单时具有Visible = false,另一个包含带有文本的标签,如“正在加载,请稍候......”。为页面加载数据并更新UI时,切换两个面板上的可见性以显示UI。这样,在加载数据时表单仍然会响应,并且由于您一次只加载一页的数据,因此加载时间应该相当短。

答案 1 :(得分:3)

理想的解决方案是您不会在表单加载事件中真正加载数据。

让表单正确且完整地加载,以便UI渲染完成。

之后,您可以在任何地方显示进度条,并且可以执行实际的数据加载,无论是同步还是异步。

如果您不需要同时加载所有内容我还会考虑仅在第一次访问选项卡时加载数据,因此,如果用户从不点击最后一个选项卡,例如,您没有加载任何内容为了它。

答案 2 :(得分:1)

这里的问题是BackgroundWorker的DoWork方法无法触及GUI。

必须使用BackgroundWorker.ReportProgress( int progress, object state)在GUI线程上调用一个可以自由更新GUI的方法。

这里重要的一点是GUI控件只能从GUI线程本身更新,否则程序中会出现随机异常。

答案 3 :(得分:1)

除了UI线程之外,你不能从任何其他线程中弄乱UI;这是你的错误的来源。如果您想了解有关如何从其他线程正确更新UI元素的更多信息,则需要使用调用。 Stack Overflow上的This问题可能对您有帮助。

答案 4 :(得分:1)

是的,BackgroundWorker非常适合这一点。

我认为你做错了,因为你想在DoWork活动中改变你的形式。

但是,您只应在该事件的结果中收集此结果

然后在RunWorkerCompleted中使用其e.Results来更改表单。

答案 5 :(得分:1)

为每个标签创建BackgroundWorker。在窗体的Load事件中,禁用所有选项卡并为每个后台工作程序调用RunWorkerAsync()方法。

在每个DoWork事件处理程序中,将关联标签页所需的数据从数据库加载到数据表中,并将DoWorkEventArgs的Result属性设置为数据表。

注意:在DoWork事件处理程序中,您不应更新任何UI控件,因为它在不同的线程中运行。您应该只从数据库中检索数据并设置Result属性。

在RunWorkerCompleted事件处理程序中,您可以通过获取RunWorkerCompletedEventArgs的Result属性来访问在DoWork事件处理程序中检索的数据表。然后,您可以相应地设置UI控件的属性,然后启用与当前后台工作程序关联的选项卡页面。

答案 6 :(得分:1)

TabControl已经进行了延迟加载。

帮自己一个忙,并将每个标签的内容放在UserControl上。

答案 7 :(得分:0)

为了解决您遇到后台工作线程的问题,听起来您可能正在尝试从线程本身访问UI组件。要从非UI线程执行此操作,您需要使用Control.Invoke而不是尝试直接访问组件。

答案 8 :(得分:0)

好。几乎没有时间的大量反应。喜欢这个地方。从link-2-link跳转,我与Reactive Extensions for .NET (Rx)面对面。这也是一个很好的替代品吗?在我的方案中找不到有关如何使用Rx的教程。

答案 9 :(得分:0)

好的,我面临一个新的场景。我转移到Visual Studio 2010并遇到了Task.Factory.StartNew(()=> myMethod());

这是我到目前为止所做的:

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()
    );

}

前两个步骤(任务)至关重要。现在,禁用选项卡的选项卡页面。我需要根据相关的任务完成启用它们。我可以找到一个要订阅的事件,表示某个任务已经完成。