通过将UI分解为';地区';-这可能吗

本文关键字:地区 分解 UI | 更新日期: 2023-09-27 17:49:38

我在WPF客户端应用程序上运行了一个非常简单的性能测试:

public partial class MainWindow : Window
{
    private ObservableCollection<int> data = new ObservableCollection<int>();
    public ObservableCollection<int> DataObj { get { return data; } }
    private void button1_Click(object sender, RoutedEventArgs e)
    {
        for (int j = 0; j < 5; j++)
        {
            Thread t = new Thread(() =>
                {
                    for (int i = 0; i < 100; i++)
                    {
                        Thread.Sleep(5);
                        Dispatcher.Invoke(new Action(() => { data.Add(1); })); //updates the count
                        Dispatcher.Invoke(new Action(() => { richTextBox1.AppendText("1"); })); //updates the string data
                    }
                });
            t.Start();
        }
    } 

然后,我在UI中有两个控件:TextBlockRichTextBox

TextBlock绑定到数据源的Count属性,而RichTextBox将每个新的数据值附加到其文本字符串中(即显示数据的内容(。

如果我禁用了RichTextBox绑定,TextBlock会很快更新,并循环计数。然而,启用RichTextBox绑定会减慢所有的速度,两个控件都会在"globs"中更新,可能每秒更新一到两次。换句话说,整个UI以RichTextBox绑定的速度运行。

有没有办法打破这种性能依赖性?我知道RichTextBox可能很慢,但为什么它必须放慢原本闪电般快速的TextBlock?

通过将UI分解为';地区';-这可能吗

WPF的特殊之处在于每个窗口只有一个UI线程。

尽管可以使用其他窗口并使其看起来像是当前应用程序的一部分(将WindowStyle属性设置为None并更新位置和大小(,但它看起来并不自然,而且有更好的方法来解决性能问题。

众所周知,有必要使用Dispatcher类从后台线程更新UI。BeginInvoke方法具有DispatcherPriority类型的可选参数,该参数具有以下值。

  1. 系统空闲
  2. 应用程序空闲
  3. 上下文空闲
  4. 背景
  5. 输入
  6. 已加载
  7. 渲染
  8. 数据绑定
  9. 正常
  10. 发送

默认值是Normal (9),它几乎是最高优先级,并且无论何时调用不带参数的BeginInvoke方法都会隐式应用它。在您的示例中,对RichTextBox的调用具有此优先级。

但是绑定到属性并且不手动更新的TextBlock具有较低的优先级DataBind (8),这就是它更新较慢的原因。

为了使绑定更快,可以降低对RichTextBox的调用的优先级,并设置一个低于8的值,例如Render (7)

Dispatcher.Invoke(/*...*/, DispatcherPriority.Render);

它将有助于绑定,但用户界面不会对鼠标单击做出响应,您甚至无法关闭窗口。

继续降低优先级:

Dispatcher.Invoke(/*...*/, DispatcherPriority.Input);

应用程序的响应更好,但在由文本填充的RichTextBox中仍然无法键入内容。

因此,最终值为Background (4):

Dispatcher.Invoke(new Action(() => { richTextBox1.AppendText("1"); }),
                  DispatcherPriority.Background);