Search code examples
c#wpfsystem.reactivethrottlingwin2d

C#/WPF merging consecutive event calls


Win2D features this function Invalidate(), that when called will redraw an entire control, and on rapid successive calls they will merge into one update that encompasses any changes from previous calls. I am trying to recreate this in my own application, but I can't find a framework that matches this description exactly.

Say every time I click a chart, a line gets added. What I want to happen is that if someone clicks 100 times in a row, it will wait until the clicking is done to add all of the lines at once, as opposed to adding them one at a time. Similarly, resizing the window should only redraw the chart once, as opposed to every time the event is fired.

I have tried using System.Reactive, but most of their merging/throttling seems to ignore the previous calls, and I can't use the OnCompleted() part of Subscribe on an event as it does not ever "complete".

Has anyone had experience with solving a problem like this? I am looking in to using timers to create a sort of delay, but I'd like to know if there is already something out there that works as described.


Solution

  • Here's how I'd solve this. I put this all in Mainwindow.xaml.cs for ease of understanding, but you may want to move the logic to its own class.

        private int clickCount;
        private DateTime lastClick;
        private System.Windows.Threading.DispatcherTimer clickEventTimer;
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            if (clickEventTimer == null || !clickEventTimer.IsEnabled)
            {
                clickCount = 1;
                lastClick = DateTime.Now;
                clickEventTimer = new System.Windows.Threading.DispatcherTimer() { Interval = TimeSpan.FromSeconds(1) };
                clickEventTimer.Tick += (timer, args) =>
                {
                    if (DateTime.Now - lastClick < TimeSpan.FromSeconds(1))
                    {
                        return;
                    }
                    else
                    {
                        clickEventTimer.Stop();
                        MessageBox.Show($"Do stuff for the {clickCount.ToString()} click(s) you got.");
                    }
                };
                clickEventTimer.Start();
            }
            else
            {
                clickCount++;
                lastClick = DateTime.Now;
            }
        }