2017-01-11 38 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; 
} 

一旦任务完成后你仍然可以等待它。但是,因为它已经结束,你会毫不迟疑地得到结果。