2011-02-08 25 views
3

我有一個表單正在從SQL服務器加載相當數量的數據。以下是將提供良好提示的代碼:WinApp表單在加載事件過程中加載太多數據

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是帶有選項卡控件的窗體。上面的每個細節都轉到標籤頁。我需要儘快加載並向用戶顯示錶單。一旦顯示錶格,我需要啓動一系列後臺工作人員來獲取每個頁面的數據。

我一直在試圖弄亂後臺工作人員,但無法理解它的用法。我最終得到了「不同線程嘗試訪問您的主線程控制......」或類似的東西......「

理想的情況是有一個進度條加載每個選項卡上的數據和一旦相應的後臺工作者完成,標籤就會變爲可互動的。

任何策略或建議?感謝您的閱讀。

回答

1

爲每個選項卡創建一個BackgroundWorker。在窗體的Load事件中,禁用所有選項卡併爲每個後臺工作者調用RunWorkerAsync()方法。

在每個DoWork事件處理程序中,將數據庫中關聯選項卡頁所需的數據加載到數據表中,並將DoWorkEventArgs的Result屬性設置爲數據表。

注意:在DoWork事件處理程序中,您不應該更新任何UI控件,因爲它在不同的線程中運行。您只應從數據庫中檢索數據並設置Result屬性。

在RunWorkerCompleted事件處理程序中,可以通過獲取RunWorkerCompletedEventArgs的Result屬性來訪問在DoWork事件處理程序中檢索到的數據表。然後,您可以相應地設置UI控件的屬性,然後啓用與當前後臺工作人員相關聯的選項卡頁面。

3

如果每種類型的數據只顯示在一個頁面上,我會移動代碼,以便在每次加載標籤頁時首次加載每種相應類型的數據。這樣,數據加載工作將會在更長的時間內分配,如果用戶在會話期間不導航到所有選項卡,可能會完全避免。

加載頁面時,可以使用BackgroundWorker或任何其他異步機制。在標籤頁中,您可以有兩個面板控件。一個包含用戶界面的頁面,當表單被加載時有Visible = false,另一個包含一個帶有「加載,請稍候...」等文本的標籤。當爲頁面加載數據並更新UI時,切換兩個面板上的可見性以顯示UI。這樣,表單在數據加載時仍然可以響應,並且由於您一次只加載一個頁面的數據,加載時間應該相當短。

1

這裏的問題是BackgroundWorker的DoWork方法不能觸及GUI。

它必須使用BackgroundWorker.ReportProgress(int progress, object state)在GUI線程上調用可自由更新GUI的方法。

這裏重要的一點是,GUI控件應該只能從GUI線程本身更新,否則隨機的異常會在您的程序中出現。

3

理想的解決方案是,您不會真正在表單加載事件中加載數據。

讓表單正確加載,完全讓UI渲染完成。

之後,您可以在任何地方顯示進度條,並且可以執行實際的數據加載,無論是同步還是異步。

如果你不需要在同一時間加載所有的東西,我也會考慮只在第一次訪問標籤時加載數據,所以如果用戶從不點擊最後一個標籤,例如,你沒有加載任何事情。

1

除了UI線程外,您不能從任何其他線程中調用UI;這是你錯誤的根源。如果您想了解更多關於如何從不同線程正確更新UI元素的信息,則需要使用調用This堆棧溢出問題可能會幫助你。

0

爲了解決您遇到的後臺工作線程問題,聽起來好像您可能試圖從線程本身訪問UI組件。要從非UI線程執行此操作,您需要使用Control.Invoke而不是直接訪問組件。

1

是的,BackgroundWorker對此很好。

我認爲你做錯了,因爲你想在DoWork事件中改變你的表單。

然而,你應該只收集這導致事件

然後在RunWorkerCompleted你使用它的e.Results改變形式的e.Results。

+0

在e.Result中,數據太多以至於不能很好地表現出來......但我只能在實際嘗試變形後才能回來。 – DoomerDGR8

+0

爲什麼太多?無論您想如何異步獲取,都必須在非GUI線程中將其收集到某個變量中,然後調用UI線程並使用數據更新UI。 BackgroundWorker實際上是這樣做的。如果你使用Thread,你可以更靈活,但它可能不值得麻煩。所以,我不確定我是否已經用一些真正的大型數據集嘗試過BackgroundWorker,但是除非它們對於計算機內存來說太大,否則我不會看到任何不好的事情會發生。 –

0

好的。在幾乎沒有時間的大規模響應。喜歡這個地方。從鏈接2鏈接跳轉,我與Reactive Extensions for .NET (Rx)面對面。這可以成爲一個很好的替代品嗎?在我的方案中找不到關於如何使用Rx的教程。

1

TabControl已經做了延遲加載。

幫你一個忙,把每個標籤的內容放在UserControl上。

+0

「_TabControl_已經做了延遲加載。」 ..不能把這首歌從我腦海中拿出來。所以,在表單加載時,我稱之爲'LoadBranchInit();'我調用其餘的方法'LoadBranch *();'每個標籤頁的init? – DoomerDGR8

+0

或多或少。您需要一些邏輯來決定何時首次顯示選項卡。使用UC,您可以使用它的OnLoad事件。 –

+0

參見http://stackoverflow.com/q/3670694/60761 –

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

} 

前兩步(任務)是至關重要的。現在,選項卡的選項卡頁面被禁用。我需要根據他們相關的任務完成來啓用它們。我可以找到一個訂閱的事件,表明某個任務已完成或未完成。

+0

另外,通過並行或任務方法訪問任何用戶界面仍然不行。那麼,我保存或加載的內容是什麼,以後我可以在Tasked方法中解包到我的UI控件中? – DoomerDGR8