【问题标题】:WPF GUI not updating although main thread is running尽管主线程正在运行,但 WPF GUI 未更新
【发布时间】:2016-01-20 13:55:48
【问题描述】:

我有一个带绑定的文本框:

<TextBox Text="{Binding TestString, UpdateSourceTrigger=PropertyChanged}" />

这里是 TestString 的实现:

    private string testString;
    public string TestString
    {
        get { return testString; }
        set
        {
            if (testString != value)
            {
                testString = value;
                PropChanged(); // INotifyPropertyChanged Implementation ..
                System.Threading.Thread.Sleep(100);
            }
        }
    }

TestString 属性的 set 方法需要几毫秒(例如,你可以用 100 毫秒的睡眠来测试它),然后如果你按住一个字母键,你首先会看到几个字母的编辑更新然后 GUI 在您按住字母键时停止更新/刷新。如果释放密钥,则 gui 会继续更新。

我知道对于长时间运行的操作,我应该使用异步编程机制,但令人讨厌的是,我不知道 WPF 开始不再更新时的阈值是多少。 我个人不想为持续 5 或 10 或 100 毫秒的短操作实现异步开销,因为 UI 仍然足够响应(如果 WPF 会更新......)。

有谁知道为什么会发生这种情况,或者我如何在不使用异步编程的情况下解决这个问题?

在 Borland VCL 中(我猜在 WinForms 中)这根本不是问题,因为 GUI 在每个关键事件之后都会更新。

顺便说一句,如果您使用滑块,则 UI 鼠标移动事件比按住键要多得多,并且如果您使用滑块,WPF 更新 GUI 没有问题,即使 ui 线程中的操作持续很长时间(唯一的问题是 ui 反应更慢)。

【问题讨论】:

  • 请展示TestString属性的实现

标签: c# wpf user-interface


【解决方案1】:

不要在你的 setter 中放置任何同步逻辑。通常我不建议将逻辑放在你的设置器中,但是当你这样做时 - 异步执行。否则,您的 UI 将如您所描述的那样被阻止。

 private string _testString;

public string TestString
{
    get { return _testString; }
    set
    {
        // Custom Method, returns true if property has changed
        if (SetProperty(ref _testString, value))
        {
            DoSomeStuff();
        }
    }
}

private async void DoSomeStuff()
{
    // Do long lasting calls here
    await Task.Delay(3000);
}

【讨论】:

  • 当然可以……但我想知道为什么会这样。我怎么知道这个动作对于二传手来说是否太长了?无论如何感谢您的回答!
  • 您在 setter 中放入了什么样的逻辑会很有趣。在主线程(UI 线程)上执行的每个“持久”操作都会冻结您的 UI。您如何定义“长期任务”是您的定义。通常服务调用、计算、数据库访问等。
  • 我做了一个足够快的计算(正如我写的,几毫秒),所以不需要任务和所有需要的同步内容。我仍然不知道为什么 WPF 停止渲染。
【解决方案2】:

wpf 中的主线程(UI 线程)基本上就是无限循环。循环基本上是这样做的:

  1. 处理所有事件; 2.渲染ui; 1.处理所有事件; 2.渲染ui等。

因此,当您按下键时,它会触发一个事件并更改复选框中的文本。然后 wpf 数据绑定更改 TestString 属性,完成后,呈现 UI

现在,循环重复。第一步是处理所有事件。但是由于上一个周期花费了更长的时间,因此需要处理很多事件(这些事件在某处缓冲)。在 UI 呈现之前,多个事件会多次更改 TestString 属性。所以这个循环比第一个循环需要更多的事件,下一个循环将不得不处理更多的事件,并且你的 UI 线程充满了事件处理程序。

那么在一个循环中你有多少时间?好错误的问题。其中,这取决于客户的 PC 设置以及他的键盘速度(按键重复的频率,当您按住它时)。

你应该这样编写代码,即使事件被多次触发,你也不会用你的事件处理来淹没 UI 线程。

编辑: 您可能对Binding.Delay 属性感兴趣。例如,延迟 100ms 告诉 WPF 以每秒最多 10 次的速度更新绑定源(在本例中为 TestString),如果 TextBox.Text 更改了 25 次则事件。

另一个解决方案是Reactive Extensions 库。它基本上可以帮助您使用类似 linq 的运算符来处理事件。例如:

Observable.FromEvent<PropertyChangedEventArs)(this, nameof(PropertyChanged))
  .Where(e => e.PropertyName == nameof(TestString))
  .Sample(TimeSpan.FromMiliseconds(100))
  .Subscribe(() => Thread.Sleep(...))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多