【问题标题】:Why does the Dispatcher Timer in WPF lag when i hover over my Application Window?当我将鼠标悬停在我的应用程序窗口上时,为什么 WPF 中的调度程序计时器滞后?
【发布时间】:2018-03-14 01:39:59
【问题描述】:

当我将鼠标悬停在应用程序窗口上时,我的 DispatcherTimer 滞后,我使用 Windows 窗体和相同的代码测试了相同的东西,仅使用来自 Forms 的计时器,它没有任何滞后。

这是我的代码:

{
    double dps1 = 2;
    double count;

    DispatcherTimer DPS = new DispatcherTimer();
    public MainWindow()
    {
        InitializeComponent();

        DPS.Tick += new EventHandler(DPS_tick);
        DPS.Interval = new TimeSpan(1);
    }

    private void DPS_tick(object sender, EventArgs e)
    {
        count += dps1 / 2500;
        lbl.Content = count;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        DPS.Start();
    }

这是实际程序的 GIF:https://i.imgur.com/d91bxJD.gifv

【问题讨论】:

  • 可能是我,但我在视频中看不到任何延迟。我应该在什么时候寻找?
  • 因为它不是 Windows 窗体,当鼠标悬停在窗体上时,您正在与 UI 线程进行交互。由于 DispatcherTimer(在这种情况下)使用 UI 线程,因此它需要在 UI 线程上安排其工作,而您正在搞砸。尝试使用常规 Timer 并使用 Dispatcher.Invoke 更新 UI。
  • 还要注意DPS.Interval = new TimeSpan(1) 将间隔设置为 100 纳秒。
  • @Will:我可以发誓这是相反的——像 Windows.Forms.Timer 这样的旧代码在 UI 线程上运行。调度程序没有。
  • DispatcherTimer 的运行方式与原生 winapi 中的计时器非常相似,它们的通知具有最低优先级。用户输入总是先于它,在这种情况下有很多 MouseMove 通知。 WPF 使用 DispatcherPriority 来控制这一点,DispatcherTimer 的默认构造函数使用 DispatcherPriority.Background。但它很好,you can mess with that。请注意,您需要先在没有附加调试器的情况下在发布版本中进行测试,我对可视化树调试功能有点怀疑。

标签: c# wpf


【解决方案1】:

首先,您正在使用 UI 调度程序来安排您的计时器滴答声。为了证明这一点,请将以下内容添加到您的构造函数中。

if(DPS.Dispatcher != Dispatcher.CurrentDispatcher || // UI thread, UI dispatcher
   DPS.Dispatcher != Application.Current.Dispatcher || // Also the UI dispatcher
   DPS.Dispatcher != Dispatcher) // the window's dispatcher
    throw new InvalidOperationException("Will is wrong");

你会注意到我是对的。因此,当您摆动鼠标时,UI 线程正忙于处理鼠标事件,正如 Hans 所说,这比您的计时器滴答声具有更高的优先级。呵呵。

您可以通过使用在后台线程上运行的不同计时器实现来解决此问题,然后进入 UI 线程以在滴答时更新 UI。

我是 MVVM 的粉丝,绑定会自动为您处理 UI 线程上的调用更新,因此我的重现与您的略有不同。

首先,在 UI 中为标签添加绑定。

<Label Content="{Binding Count}" />

我们将重用按钮上的 Click 事件,为此创建一个 ICommand 有点过火。接下来,更新 Window 的代码,使其看起来像这样

public partial class MainWindow : Window
{
    VM vm = new VM();
    public MainWindow()
    {
        InitializeComponent();
        DataContext = vm;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        vm.Start();
    }

    class VM : INotifyPropertyChanged
    {
        double dps1 = 2;
        double count;
        Timer timer;
        PropertyChangedEventArgs args = new PropertyChangedEventArgs(nameof(Count));
        public VM()
        {
            timer = new Timer();
            timer.Interval = .0001;
            timer.Elapsed += (o, e) => Count = (count += dps1 / 2500);
        }
        public double Count
        {
            get
            {
                return count;
            }
            set
            {
                count = value;
                PropertyChanged?.Invoke(this, args);
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
        public void Start()
        {
            timer.Start();
        }
    }
}

我正在使用 System.Windows.Threading.Timer 来处理我的滴答声,它的间隔以毫秒为单位(0.0001ms 是一个滴答声,大致是这样)。如果您运行此示例,您的 UI 将不再滞后。

【讨论】:

    猜你喜欢
    • 2020-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-31
    相关资源
    最近更新 更多