【问题标题】:C# WPF Freezing UIC# WPF 冻结用户界面
【发布时间】:2017-10-21 09:25:42
【问题描述】:

应用程序从 inputField 中获取文本,在其中搜索正则表达式匹配并将结果返回到 outputField。把它放在几行代码中,我有这样的东西:

public class Parser {
    public Parser(string _text)
    {
        text = _text;
    }

    private string text { get; set; }

    public string[] find()
    {

        string r1 = "...";
        string r2 = "...";
        string r3 = "...";

        string[] regArray = new string[] { r1, r2, r3 };

        List<string> resL = new List<string>();

        for (int i = 0; i < regArray.Length; i++)
        {
            MatchCollection matchList = Regex.Matches(text, regArray[i]);
            var list = matchList.Cast<Match>().Select(match => match.Value).ToList();
            resL.AddRange(list);
        }

        string[] res = resL.Distinct().ToArray();
        if (res.Length > 0)
            return res;
        return new string[0];
    } }

private async void FindButton_Click(object sender, RoutedEventArgs e) {
    FindButton.Content = "Searching...";
    FindButton.IsEnabled = false;
    await Task.Delay(1);

    try
    {
        string parsed = string.Empty;
        if (string.IsNullOrWhiteSpace(new TextRange(InputField.Document.ContentStart, InputField.Document.ContentEnd).Text)) ;
        {
            OutputField.Document.Blocks.Clear();
            MessageBox.Show("Empty input");
        }
        else
        {
            Parser nOb = new Parser(new TextRange(InputField.Document.ContentStart, InputField.Document.ContentEnd).Text);
            string[] result = nOb.find();

            if (result.Length == 0)
            {
                OutputField.Document.Blocks.Clear();
                MessageBox.Show("Nothing found");
            }
            else
            {
                for (int i = 0; i < result.Length; i++)
                {
                    parsed += result[i] + Environment.NewLine;
                }

                OutputField.Document.Blocks.Clear();
                OutputField.Document.Blocks.Add(new Paragraph(new Run(parsed)));

                MessageBox.Show("Success");
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("Error: " + ex.Message);
    }

    FindButton.Content = "Default";
    FindButton.IsEnabled = true; 
}

问题是当来自 inputField 的文本非常大并且程序试图找到所有匹配项时,UI 开始冻结。最小化程序窗口变得不可能,Windows 说该应用程序没有响应并询问我是否要关闭它。如果我在工作期间不点击程序,它就可以完成。那么是否有可能以某种方式避免冻结,并在应用大输入时最小化应用程序?任何帮助表示赞赏。

【问题讨论】:

  • 在不知道您的 Task.Delay 是什么的情况下,我们无能为力。

标签: c# wpf user-interface freeze


【解决方案1】:

尝试在后台线程上执行find()方法:

private async void FindButton_Click(object sender, RoutedEventArgs e)
{
    FindButton.Content = "Searching...";
    FindButton.IsEnabled = false;

    try
    {
        if (string.IsNullOrWhiteSpace(new TextRange(InputField.Document.ContentStart, InputField.Document.ContentEnd).Text))
        {
            OutputField.Document.Blocks.Clear();
            MessageBox.Show("Empty input");
        }
        else
        {
            string[] result;
            await Task.Run(() =>
            {
                Parser nOb = new Parser(new TextRange(InputField.Document.ContentStart, InputField.Document.ContentEnd).Text);
                result = nOb.find();
            });

            if (result == null || result.Length == 0)
            {
                OutputField.Document.Blocks.Clear();
                MessageBox.Show("Nothing found");
            }
            else
            {
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < result.Length; i++)
                {
                    sb.AppendLine(result[i]);
                }

                OutputField.Document.Blocks.Clear();
                OutputField.Document.Blocks.Add(new Paragraph(new Run(sb.ToString())));

                MessageBox.Show("Success");
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("Error: " + ex.Message);
    }

    FindButton.Content = "Default";
    FindButton.IsEnabled = true;
}

【讨论】:

    【解决方案2】:

    这太可怕了:

    for (int i = 0; i < result.Length; i++)
    {
        parsed += result[i] + Environment.NewLine;
    }
    

    因为字符串是不可变的,这会创建 N 个字符串,每个字符串都比上一个字符串长,总内存消耗为 O(N2)。而且复制浪费的时间也是O(N2)。

    最好使用String.Join,但是当您需要在循环中进行字符串操作时,请使用System.Text.StringBuilder,它会修改其内部缓冲区,而不是像@那样将其丢弃并为每个操作制作完整的副本987654324@必须。

    但是,为什么你首先要把一个多行字符串变成一个单独的 Run 呢?可能每一个都应该是一个单独的 Paragraph 对象,并让 WPF 容器管理剪辑(并且不必绘制完全在屏幕外的 Paragraph 对象)。但是,如果您这样做,则构建一个与 UI 断开连接的内容对象,然后一步将其添加到 UI,这样您就不会导致布局引擎在每次连接时再次运行。

    【讨论】:

    • 这不能回答问题。
    • @Erno:选择正确的算法是保持计算机响应的唯一方法。如果整个操作系统在交换到磁盘时绑定在一起,那么执行此操作的程序的 UI 也将无响应,即使使用后台线程也是如此。频繁的垃圾回收也会冻结 UI。
    • @Erno:当然,如果在固定操作的总体成本之后,它仍然运行的时间足够长以至于 UI 挂起很明显,那么应该使用后台任务。但修复性能问题总比隐藏它好。
    • 我并没有声称答案是错误的(我认为解决方案是正确算法和卸载到另一个线程的组合)它根本没有回答为什么应用程序“冻结”的问题
    【解决方案3】:

    这是默认情况下在 UI 线程上运行任务时的预期行为,只要它没有完成,就不可能进行任何 UI 操作。

    您需要使用Task.Run 使代码在非UI 线程上运行。但是你可能需要更新一些控件,所以你需要回到 UI 线程,然后你可以使用Dispatcher.RunAsync,尽管最好在 WPF 中使用数据绑定。

    这些都是非常基本的东西,您应该在 MSDN 上进一步阅读这些主题。

    【讨论】:

      【解决方案4】:

      我不得不承认我不明白你的代码的目标是什么,但我会尝试这样的事情:

      我简化了你的Parser 类:

          public class Parser 
          {
              const string _r1 = "...";
              private string _text;
      
              public Parser(string text)
              {
                  _text = text;
              }
      
              public IEnumerable<string> Find()
              {
                  return Regex.Matches(_text, _r1)
                      .Cast<Match>()
                      .Select(match => match.Value)
                      .Distinct();
              } 
          }
      

      还有一部分按钮点击代码,使用StringBuilder

      Parser nOb = new Parser(yourText);
      var result = nOb.Find();
      string parsed = string.Empty;
      
      if (!result.Any())
      {
          // Nothing found
      }
      else
      {
          var stringBuilder = new StringBuilder();
          foreach (var line in result)
          {
              stringBuilder.AppendLine(line);
          }
      
          parsed = stringBuilder.ToString();
      
          // ...parsed
      }
      

      如果您更好地指定代码的最终目标,我们可以更准确地提供帮助。

      【讨论】:

        猜你喜欢
        • 2016-07-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-02-08
        • 1970-01-01
        • 2021-09-02
        • 1970-01-01
        相关资源
        最近更新 更多