首页 > 解决方案 > 启动时的 C# WPF 异步

问题描述

我尝试构建一个简单的 WPF 应用程序并listboxMainWindow. 背景是它 ping 一些机器,但我希望 GUI 已经打开,并且每当 ping 完成时,listbox应该更新内容。

让我展示我的代码。主窗口.xaml.cs

        public ObservableCollection<String> LbStatus
        {
            get { return BL.GetStatus(); }
        }

BL.cs

        public static ObservableCollection<string> GetStatus()
        {
            List<string> hostnames = new List<String>(GetHostnames());

            ObservableCollection<string> status = new ObservableCollection<string>();
 
            foreach(string hostname in hostnames)
            {
                Task<string> task = Task.Run(async () => await 
 PingHost(HM.GetHostname(hostname)));
                status.Add(task.Result); 
            }
            return status; 
        }
        static public async Task<string> PingHost(string hostname)
        {
            Ping x = new Ping();
            try
            {
                PingReply reply = await x.SendPingAsync(hostname, 5000);
                if (reply.Status == IPStatus.Success)
                {
                    return "Online";
                }
                else
                    return "Offline";
            }
            catch (Exception excep)
            {
                //exception handling
            }
        }

我认为问题在于我的程序等待task.result. 但在我的脑海中,它应该只是跳过并且什么都不返回,并且每当它完成时它应该返回正确的字符串。但是(这就是我这样做的全部原因)GUI 应该已经启动了。

我希望我的问题是有道理的,有人可以帮助我:-)

谢谢!

标签: c#wpfuser-interfaceasynchronousasync-await

解决方案


我建议您进行一些更改。首先,您对该属性
的实现非常可疑。 在这样的实现中,每次访问此属性时,都会创建一个新集合,这可能会导致:它们的不同状态;同时调用多次该方法。还有许多其他问题与多个集合实例的存在相关,但是当视图“认为”这是同一个实例时。 通过为您提供此属性的此实现:LbStatus
BL.GetStatus()

private ObservableCollection<string> _lbStatus;
public ObservableCollection<string> LbStatus
{
    get 
    {
        if (_lbStatus == null)
            _lbStatus = BL.GetStatus();
        return _lbStatus;
    }
}

第二个问题,在方法中BL.GetStatus()- 您需要返回集合而不等待其完成。
因此,填充它的循环应该在一个单独的异步方法中。
BL.GetStatus()方法应该调用这个异步方法,而不是等待它完成。

我建议你这个实现:

public static ObservableCollection<string> GetStatus()
{
    ObservableCollection<string> status = new ObservableCollection<string>();

    FillStatus(status);

    return status;
}

public static async Task FillStatus(ICollection<string> status)
{
    foreach (string hostname in GetHostnames())
    {
        status.Add(await PingHost(HM.GetHostname(hostname)));
    }
}

实现属性的另一种选择,考虑到将方法分为两部分。
它只会使用异步部分。

private ObservableCollection<string> _lbStatus;
public ObservableCollection<string> LbStatus
{
    get 
    {
        if (_lbStatus == null)
        {
            _lbStatus = new ObservableCollection<string>();
            BL.FillStatus(_lbStatus);
        }
        return _lbStatus;
    }
}

考虑到集合在 UI 元素中的使用,它的更改应该发生在主 UI 线程中。为此,您必须将 Dispatcher 传递给该方法。

public static async Task FillStatus(ICollection<string> status, Dispatcher dispatcher)
{
    foreach (string hostname in GetHostnames())
    {
        string item = await PingHost(HM.GetHostname(hostname));
        dispatcher.BeginInvoke(new Action(() => status.Add(item)))
    }
}
private ObservableCollection<string> _lbStatus;
public ObservableCollection<string> LbStatus
{
    get 
    {
        if (_lbStatus == null)
        {
            _lbStatus = new ObservableCollection<string>();
            BL.FillStatus(_lbStatus, Dispatcher);
        }
        return _lbStatus;
    }
}

推荐阅读