2015-07-13 73 views
1

應用程序說明:
在C#中開發了一個小型Windows MDI應用程序,其中多個用戶在分派之前更新和掃描產品條形碼。我們使用SQL作爲數據庫。C#窗口顯示從數據庫更新窗體GUI時加載GIF

活動:
監事已命名的形式在應用程序,他們可以選中多個計數的細節,如掃描每個用戶,每天掃描計數,每小時掃描計數,產品掃描計數等

所做的一切儀表板:
我已經使用了多個列表視圖和標籤來成功顯示這些細節。

要求:
該數據庫包含數百萬條掃描記錄。我正在使用多個存儲過程來更新列表視圖和標籤。加載同樣需要時間。我想在後臺完成加載時在圖片框中顯示動畫GIF。

我嘗試過使用線程和後臺工作器,但更新多個控件,如列表視圖和主GUI上的標籤看起來不可行[跨線程]。這是實現這一目標的唯一方法,請指導我朝正確的方向發展。

 string queryString = "select scanby,count(distinct refno),count(1) from SecRec where scan='Y' and convert(varchar(10),ScanTime,111) like '" + comboDate + "' group by scanby order by 3,2 desc"; 
     lstVUser.Clear(); 

     lstVUser.Columns.Add("User", 105); 
     lstVUser.Columns.Add("Cust", 60); 
     lstVUser.Columns.Add("Imp", 60); 

     using (SqlConnection conn = new SqlConnection(connectString)) 
     { 
      SqlCommand cmd = new SqlCommand(queryString, conn); 
      try 
      { 
       conn.Open(); 
       SqlDataAdapter adp = new SqlDataAdapter(queryString, conn); 
       DataTable dt = new DataTable(); 
       adp.Fill(dt); 
       for (int i = 0; i < dt.Rows.Count; i++) 
       { 
        DataRow dr = dt.Rows[i]; 
        ListViewItem listitem = new ListViewItem(dr[0].ToString()); 
        listitem.SubItems.Add(dr[1].ToString().PadLeft(3)); 
        listitem.SubItems.Add(dr[2].ToString().PadLeft(3)); 
        lstVUser.Items.Add(listitem); 
       } 
      } 
      catch (Exception ex) 
      { 
       MessageBox.Show(ex.Message); 
      } 
     } 
+0

你在使用:WPF或WinForms的? – Zippy

+0

Windows窗體(WinForms)。 –

回答

1

這聽起來像你來自Delphi/FoxPro背景。在.NET中,網格默認情況下不是虛擬的,所以數據綁定數百萬行總是總是一個可怕的想法。

這需要一點思考。

首先,弄清楚有多少數據你真的需要得到在同一時間 - 也許一切都加載一次(但分別從數據綁定)是一個不錯的選擇,也許你想讀的數據導通需求。

其次,弄清楚如何呈現數據。對於幾百行,將它直接綁定到網格上是可以的。對於其他任何事情,您確實需要一些分區數據的方式,以避免總是呈現所有內容。實際上,默認的winforms DataGridView就是爲了解決這個問題而設計的 - 您只需啓用虛擬模式並編寫一些代碼即可。有關更多信息,請參閱https://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.virtualmode(v=vs.110).aspx

在大多數情況下,您不需要真的想要使用具有數百萬行的網格,虛擬或不虛擬。您可能需要考慮添加一些符合客戶要求的過濾器。例如,最好只顯示最後一百行沒有過濾器的行,並且只能通過過濾器訪問其餘行。

應用這些想法,你應該能夠擺脫必須使用加載動畫。如果加載仍然需要一些可察覺的時間量(例如大於100-200ms),則可以添加動畫,但要確保將數據加載和數據綁定分開 - 這允許您在後臺加載數據(使用異步數據庫請求或後臺工作者),並且只在執行實際綁定時阻止GUI(使用虛擬網格時不可避免,但無痛)。

編輯:

與您的代碼工作,你會想要做這樣的事情:

async Task<DataTable> LoadData() 
{ 
    // Setup the command, connection etc. as usual 
    using (var reader = await command.ExecuteReaderAsync()) 
    { 
    var results = new DataTable(); 
    results.Load(reader); 

    return results; 
    } 
} 

async void btnDoStuff_Click(object sender, EventArgs e) 
{ 
    try 
    { 
    loadingAnimation.Show(); 

    var dataTable = await LoadData(); 

    // Use the data table to bind the ListView's data as usual 
    } 
    finally 
    { 
    loadingAnimation.Hide(); 
    } 
} 
+0

我已經添加了一個示例代碼。我正在調用像上面那樣的多個過程來更新表單中的列表視圖。時間消耗在後端從sql獲取數據。我想在後臺進程執行時顯示gif。 @luaan –

+0

@ Danish_k12是的,所以只需在UI線程之外運行DB代碼(使用'ExecuteReaderAsync'或者在必要的情況下在單獨的線程上執行工作),並且僅收集數據。然後,您可以使用UI線程上的數據更新UI。您可以像在任何其他情況下一樣顯示您的加載動畫。 – Luaan

+0

我應該如何將多個SQL數據讀取器數據編組回到UI線程? –

1

好吧,如果你想訪問在另一個線程創建然後控制你將永遠面臨一個[cross-threading]問題。因此您需要使用Control.Invoke以及與之相關的模式。

這裏有一個例子:

private delegate void SetListProperties(DataTable myData); 

private void UpdateListView(DataTable myData) 
{ 
    if (lstVUser.InvokeRequired) 
    { 
     SetListProperties d = new SetListProperties(UpdateListView); 
     lstVUser.BeginInvoke(d, myData); 
    } 
    else 
    { 
     for (int i = 0; i < dt.Rows.Count; i++) 
     { 
      DataRow dr = dt.Rows[i]; 
      ListViewItem listitem = new ListViewItem(dr[0].ToString()); 
      listitem.SubItems.Add(dr[1].ToString().PadLeft(3)); 
      listitem.SubItems.Add(dr[2].ToString().PadLeft(3)); 
      lstVUser.Items.Add(listitem); 
     } 
    } 
} 

關於閃屏,你需要絕對確保所有初始化邏輯發生了GUI線程之外。

這裏是一個很好的解決方案Splash_Screen_Sample