2017-01-11 40 views
0

我有一個單例類的winforms應用程序,它包含一個屬性(列表),它是搜索網格的數據源。填充此屬性需要大量的時間(> 1分鐘),因此,我想在用戶啓動程序時異步開始填充此屬性。單例異步加載列表屬性

主窗體有一個按鈕來啓動另一個搜索表單。如果用戶在數據源準備就緒的情況下啓動搜索,則沒有問題。但是,如果數據源仍在填充,用戶將看到一個等待光標,並在數據源完成填充後立即填充搜索網格。

爲此,我創建了一個方法,在異步方法完成後觸發,然後將網格綁定到數據源。

一切似乎正常工作,事件觸發,然後我嘗試將列表綁定到網格,沒有任何反應......調試暫停,我從來沒有擊中下一行代碼(請參閱FrmSearch.cs中的註釋) 。

任何有關什麼是錯誤或一般代碼改進的想法將非常贊同,謝謝!

的Program.cs

static class Program 
    { 
     [STAThread] 
     static void Main() 
     { 
      Application.EnableVisualStyles(); 
      Application.SetCompatibleTextRenderingDefault(false); 

      Task.Run(async() => { Singleton.DogsList = await Dogs.FindAllAsync(); }); 

      Application.Run(new FrmMain()); 
     } 
    } 

Singleton.cs

public static class Singleton 
    { 
     public static event DogsListHandler DogsListLoaded; 
     public delegate void DogsListHandler(object sender, EventArgs e); 

     public static BindingList<Dogs> DogsList 
     { 
      get 
      { 
       if (dogsList == null) 
       { 
        Task.Run(async() => 
        { 
         dogsList = await Dogs.FindAllAsync(); 
         notifyListLoaded(); 
        }); 
       } 
       return dogsList; 
      } 
      set { dogsList = value; } 
     } 
     private static BindingList<Dogs> dogsList; 
     private static void notifyListLoaded() 
     { 
      if (DogsListLoaded != null) { DogsListLoaded(null, EventArgs.Empty); } 
     } 
    } 

FrmSearch.cs

public partial class FrmSearch : Form //launched using the .Show() method from a button on the main form 
{ 
    public FrmSearch() 
    { 
     InitializeComponent(); 
    } 

    private void FrmSearch_Load(object sender, EventArgs e) 
    { 
     Singleton.DogsListLoaded += new Singleton.DogsListHandler(Dogs_ListLoaded); 

     Cursor = Cursors.WaitCursor; 
     if (Singleton.DogsList != null) 
     { 
      grid.DataSource = Singleton.DogsList; 
      Cursor = Cursors.Default; 
     } 
     else { Cursor = Cursors.WaitCursor; } 
    } 

    public void Dogs_ListLoaded(object sender, EventArgs e) 
    { 
     grid.DataSource = Singleton.DogsList; //freezes here 
     Cursor = Cursors.Default; //this line never gets hit 
    } 
} 

Dogs.cs(將拉動從數據庫通常只是做一些重複的樣品)

public class Dogs 
    { 
     public string Name { get; set; } 
     public string Breed { get; set; } 
     public int Age { get; set; } 
     public string Summary { get { return string.Format("Name: {0}/Breed: {1}/Age: {2}", Name, Breed, Age.ToString()); } } 

     public static async Task<BindingList<Dogs>> FindAllAsync() 
     { 
      BindingList<Dogs> dl = new BindingList<Dogs>(); 

      await Task.Run(() => 
      { 
       int i = 0; 
       while (i <= 999999) 
       { 
        dl.Add(new Dogs() { Name = "River" + i.ToString(), Breed = "Border Collie", Age = 3 }); 
        dl.Add(new Dogs() { Name = "Jack" + i.ToString(), Breed = "Labrador", Age = 2 }); 
        dl.Add(new Dogs() { Name = "Emma" + i.ToString(), Breed = "Beagle", Age = 7 }); 
       i++; 
       }    
      }); 
      return dl; 
     } 
    } 
+0

aync屬性...似乎是一個壞主意。如果你需要潛入數據庫,它應該是一種方法。由於您可以等待「任務」或「任務」,因此您無法等待屬性。你需要做一些重新設計!你甚至不使用線程安全集合。 –

+0

嗨,彼得,我添加了上面的dogs.cs類。在我真正的應用程序中,是的,它確實從db中拉出來,但對於dogs.cs示例,我只是在做一些迭代。狗類確實返回一個任務>。我是新來的異步。任何關於重新設計的建議? – beeker

+2

您應該查看['NotifyTaskCompletion'](https://msdn.microsoft.com/zh-cn/magazine/dn605875.aspx)它是專爲處理異步綁定而設計的,就像您正在嘗試執行的那樣。這是[最新版本的GitHub頁面](https://github.com/StephenCleary/AsyncEx/wiki/NotifyTaskCompletion)。 –

回答

0

您應該檢索列表,因爲您將檢索從任何地方獲取的所有其他列表:

調用異步方法並等待。

好辦

public static class SomeLookups 
{ 
    private static object _lock = new object(); 
    private static Task<IList<string>> _foosAsync; 

    public static Task<IList<string>> GetFoosAsync() 
    { 
     lock (_lock) 
     { 
      return _foosAsync = _foosAsync ?? PrivateGetFoosAsync(); 
     } 
    } 

    private static async Task<IList<string>> PrivateGetFoosAsync() 
    { 
     var list = new List<string>(); 
     for (int i = 0; i < 20; i++) 
     { 
      await Task.Delay(200).ConfigureAwait(false); 
      list.Add("item " + i); 
     } 
     return list.AsReadOnly(); 
    } 
} 

在消費類

private async Task RetrieveAllData() 
{ 
    IsBusy = true; 
    MyFooCollection = await SomeLookups.GetFoosAsync(); 
    IsBusy = false; 
} 

一旦任務完成後你仍然可以等待它。但是,因爲它已經結束,你會毫不遲疑地得到結果。