【问题标题】:Singleton Async Loading List Property单例异步加载列表属性
【发布时间】:2017-01-11 18:07:55
【问题描述】:

我有一个带有单例类的 winforms 应用程序,其中包含一个属性(列表),该属性是搜索网格的数据源。填充此属性需要大量时间(> 1 分钟),因此,我想在用户启动程序时开始异步填充此属性。

主表单有一个按钮来启动另一个搜索表单。如果,当用户启动搜索时,如果数据源已准备好,那么没问题。但是,如果数据源仍在填充,用户会看到等待光标,并且搜索网格应该在数据源完成填充后立即填充。

为此,我创建了一个在异步方法完成后触发的方法,然后将网格绑定到数据源。

一切似乎都正常工作,事件触发,然后我尝试将列表绑定到网格,但没有任何反应...调试停止,我从未点击下一行代码(请参阅 FrmSearch.cs 中的 cmets)。

任何与问题或一般代码改进有关的想法都将非常受欢迎,谢谢!

程序.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;
        } 
    }

【问题讨论】:

  • aync 属性... 似乎是个坏主意。如果您需要深入研究数据库,它应该是一种方法。你不能等待一个属性,因为你只能等待一个TaskTask&lt;T&gt;。你需要做一些重新设计!你甚至不使用线程安全集合。
  • 嗨,彼得,我在上面添加了 dogs.cs 类。在我的真实应用程序中,是的,这确实是从数据库中提取的,但是对于 dogs.cs 示例,我只是在进行一些迭代。 dog 类确实返回了一个 Task>。我是异步的新手。对重新设计有什么建议吗?
  • 您应该查看NotifyTaskCompletion,它是为处理异步绑定而设计的,就像您尝试做的那样。这是GitHub page for the most updated version

标签: c# winforms asynchronous


【解决方案1】:

您应该像检索从任何地方获取的所有其他列表一样检索该列表:

调用异步方法并等待。

易于处理

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;
}

任务完成后,您仍然可以等待它。但是因为它已经完成了,所以您可以毫不拖延地得到结果。

【讨论】:

    猜你喜欢
    • 2013-08-01
    • 1970-01-01
    • 2013-04-13
    • 2012-05-04
    • 2012-11-24
    • 1970-01-01
    • 2016-05-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多