【问题标题】:How can you prevent UI in C# WPF from hanging?如何防止 C# WPF 中的 UI 挂起?
【发布时间】:2018-07-13 20:57:30
【问题描述】:

我有一个连接到远程服务器并显示本地组的所有成员的 C# 脚本。该脚本正在运行,但它在搜索/连接到服务器时挂起。

我在 WPF 中有以下必填字段:

  1. 服务器列表(组合框)
  2. UserAccess(文本框多行)
  3. DataGridResult(用于输出的DataGrid)

这是我的异步/等待脚本,但仍然挂起:

private async void ButtonRun_Click(object sender, EventArgs e)
{
    if (UserAccess.SelectedItem == null)
    {
        MessageBox.Show("What Access are we going to display?");
        return;
    }

    string[] separate = new string[] { "\r\n" };
    string[] strServers = ServerList.Text.Split(separate, StringSplitOptions.RemoveEmptyEntries);

    if (strServers == null || ServerList.Text == "")
    {
        MessageBox.Show("There are no Servers Defined!");
        return;
    }

    int strServersCount = ServerList.LineCount;

    DataTable temptable = new DataTable();
    temptable.Columns.Add("Server");
    temptable.Columns.Add("Comments");

    ButtonRun.IsEnabled = false;

    await Task.Run(() =>
    {
        this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() =>
            {
                for (var i = 0; i <= strServersCount - 1; i++)
                {
                    try
                    {
                        DirectoryEntry directoryServers = new DirectoryEntry("WinNT://" + strServers[i] + ",computer");
                        DirectoryEntry directoryGroup = directoryServers.Children.Find(UserAccess.Text + ",group");
                        object members = directoryGroup.Invoke("members", null);

                        foreach (object GroupMember in (IEnumerable)members)
                        {
                            DirectoryEntry directoryMember = new DirectoryEntry(GroupMember);
                            Console.WriteLine(directoryMember.Name + " | " + directoryMember.Path);
                            temptable.Rows.Add(strServers[i], directoryMember.Name + " | " + directoryMember.Path);
                        }

                    }
                    catch (Exception ex)
                    {
                        temptable.Rows.Add(strServers[i], "Error: " + ex.InnerException + " | " + ex.Message);
                    }
                    DataGridResult.ItemsSource = temptable.DefaultView;
                    ButtonRun.IsEnabled = true;
                }
            }));
    });
}

【问题讨论】:

  • async/await 在哪里?它在哪里?
  • 打开谷歌并写下:“wpf 异步 ui 响应式”。你会在 async/await 和 Tasks/etc 上找到很多,比如这里的简约可用示例:stackoverflow.com/a/37829181/717732 或整个“教程”,如 stephenhaunts.com/2014/10/14/…
  • 啊,注意 async/await/Tasks 不是唯一的方法来不冻结你的 UI,但很可能它们是最容易/最安全的学习方法。
  • Task.Run(() =&gt; { Dispatcher.Invoke(... everything) } Invoke 会立即将您的所有工作折回主线程。 Task.Run 被取消。
  • 您在 Stack Overflow 上通过接受答案将问题标记为已解决(正如您所做的那样,使用复选标记)。您无需编辑标题或类似内容。

标签: c# asynchronous async-await


【解决方案1】:

试图解决这个问题,未经测试:

    string userAccessText = UserAccess.Text;
    await Task.Run(() =>
    {
        //this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() =>
        //        {
                    for (var i = 0; i <= strServersCount - 1; i++)
                    {
                        try
                        {
                            DirectoryEntry directoryServers = new DirectoryEntry("WinNT://" + strServers[i] + ",computer");
                            DirectoryEntry directoryGroup = directoryServers.Children.Find(userAccessText + ",group");
                            object members = directoryGroup.Invoke("members", null);

                            foreach (object GroupMember in (IEnumerable)members)
                            {
                                DirectoryEntry directoryMember = new DirectoryEntry(GroupMember);
                                Console.WriteLine(directoryMember.Name + " | " + directoryMember.Path);
                                temptable.Rows.Add(strServers[i], directoryMember.Name + " | " + directoryMember.Path);
                            }

                        }
                        catch (Exception ex)
                        {
                            temptable.Rows.Add(strServers[i], "Error: " + ex.InnerException + " | " + ex.Message);
                        }
                      //  DataGridResult.ItemsSource = temptable.DefaultView;
                     //   ButtonRun.IsEnabled = true;
                    }
           //     }));  // End of Invoke
    });  

    DataGridResult.ItemsSource = temptable.DefaultView;
    ButtonRun.IsEnabled = true;

基本思路是把所有非GUI的东西都放在Task里面,等待Task之后在async OnClick方法中消费数据。

【讨论】:

  • 我确实尝试过你的脚本,但失败并出现错误“调用线程无法访问此对象,因为不同的线程拥有它。”
  • 在哪一行??
  • 这部分.. DirectoryEntry directoryGroup = directoryServers.Children.Find(UserAccess.Text + ",group");
  • 我错过了,将UserAccess.Text 放入一个字符串变量之前 Task.Run。阅读文本框属于“GUI 内容”。
猜你喜欢
  • 2015-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多