通过将UI划分为"区域"来提高WPF性能 - 这可能吗?

fle*_*esh 10 c# wpf performance

我在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();
        }
    } 
Run Code Online (Sandbox Code Playgroud)

然后我在UI中有两个控件:a TextBlock和a RichTextBox.

TextBlock被绑定到Count数据源的属性,而RichTextBox追加的每个新的数据值到它的文本字符串(即显示的数据的内容).

如果我禁用RichTextBox绑定,TextBlock更新非常快,循环计数.但是,启用RichTextBox绑定会减慢所有内容,两个控件都会以"globs"更新,可能每秒一次或两次.换句话说,整个UI以RichTextBox绑定的速度运行.

有没有办法打破这种性能依赖?我理解RichTextBox可能会很慢,但为什么它必须减慢快速的快速TextBlock?

vor*_*olf 6

WPF的具体是每个窗口只有一个UI线程.

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

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

  1. SystemIdle
  2. ApplicationIdle
  3. ContextIdle
  4. 背景
  5. 输入
  6. 加载
  7. 给予
  8. 的DataBind
  9. 正常
  10. 发送

默认值是Normal (9),它几乎是最高优先级,只要您调用BeginInvoke不带参数的方法,就会隐式应用它.RichTextBox您示例中的调用具有此优先级.

但是你TextBlock的属性绑定并且没有手动更新,优先级较低DataBind (8),这就是更新速度较慢的原因.

例如,为了更快地进行绑定,可以降低对该调用的优先级,并将RichTextBox值设置为低于8 Render (7).

Dispatcher.Invoke(/*...*/, DispatcherPriority.Render);
Run Code Online (Sandbox Code Playgroud)

它将有助于绑定,但UI不会响应鼠标点击,你甚至无法关闭窗口.

继续降低优先级:

Dispatcher.Invoke(/*...*/, DispatcherPriority.Input);
Run Code Online (Sandbox Code Playgroud)

应用程序响应更好,但在RichTextBox文本填充时仍然无法输入内容.

因此最终的价值是Background (4):

Dispatcher.Invoke(new Action(() => { richTextBox1.AppendText("1"); }),
                  DispatcherPriority.Background);
Run Code Online (Sandbox Code Playgroud)