C#/WPF合并连续的事件调用
C#/WPF merging consecutive event calls
Win2D 具有此功能 Invalidate()
,调用时将重绘整个控件,并且在快速连续调用时,它们将合并为一个更新,其中包含先前调用的任何更改。我正在尝试在我自己的应用程序中重新创建它,但找不到与此描述完全匹配的框架。
比如说,每次我单击图表时,都会添加一条线。我想要发生的是,如果有人连续点击 100 次,它将等到点击完成后立即添加所有行,而不是一次添加一行。同样,调整 window 的大小应该只重绘图表一次,而不是每次触发事件时。
我试过使用 System.Reactive
,但他们的大多数 merging/throttling 似乎都忽略了之前的调用,而且我无法在事件上使用订阅的 OnCompleted()
部分作为它永远不会 "complete".
有没有人有解决过这样的问题的经验?我正在研究使用计时器来创建一种延迟,但我想知道是否已经有一些东西可以像描述的那样工作。
这是我解决这个问题的方法。为了便于理解,我将所有内容都放在 Mainwindow.xaml.cs 中,但您可能希望将逻辑移至它自己的 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;
}
}
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".
我想你错过了一些,比如 Buffer
运算符:
Button button = new Button();
...
var subscription = button
.ButtonClicks()
.Buffer(TimeSpan.FromSeconds(0.5), 100) // Perform action after 100 clicks or after half a second, whatever comes first
.Select(buffer => buffer.Count)
.Subscribe(clickCount =>
{
// Do something with clickCount
})
帮手class:
public static class Extensions
{
public static IObservable<EventPattern<RoutedEventArgs>> ButtonClicks(this Button control)
{
return Observable.FromEventPattern<RoutedEventHandler, RoutedEventArgs>(
h => control.Click += h,
h => control.Click -= h);
}
}
申请结束时:
subscription.Dispose();
当然 Buffer 有一些重载:
Buffer(TimeSpan.FromSeconds(1))
-> 缓冲一秒钟,然后执行操作(与接受的答案相同的结果)
Buffer(100)
-> 点击 100 次后执行操作
这在 Rx 中非常简单。这就是你需要的:
IObservable<long> query =
source
.Select(t => Observable.Timer(TimeSpan.FromMilliseconds(100.0)))
.Switch();
因此,只要 source
是一个 IObservable<T>
,它会在用户执行某个操作时产生一个值,那么这个可观察对象只会在 100.0
出现时产生一个值自最后一个值以来的毫秒不活动时间。
现在,您可能想要整理一下,以便查询生成原始值并编组回调度程序。这也很简单。
试试这个:
IObservable<T> query =
source
.Select(t =>
Observable
.Timer(TimeSpan.FromMilliseconds(100.0))
.Select(_ => t))
.Switch()
.ObserveOnDispatcher();
简单。
Win2D 具有此功能 Invalidate()
,调用时将重绘整个控件,并且在快速连续调用时,它们将合并为一个更新,其中包含先前调用的任何更改。我正在尝试在我自己的应用程序中重新创建它,但找不到与此描述完全匹配的框架。
比如说,每次我单击图表时,都会添加一条线。我想要发生的是,如果有人连续点击 100 次,它将等到点击完成后立即添加所有行,而不是一次添加一行。同样,调整 window 的大小应该只重绘图表一次,而不是每次触发事件时。
我试过使用 System.Reactive
,但他们的大多数 merging/throttling 似乎都忽略了之前的调用,而且我无法在事件上使用订阅的 OnCompleted()
部分作为它永远不会 "complete".
有没有人有解决过这样的问题的经验?我正在研究使用计时器来创建一种延迟,但我想知道是否已经有一些东西可以像描述的那样工作。
这是我解决这个问题的方法。为了便于理解,我将所有内容都放在 Mainwindow.xaml.cs 中,但您可能希望将逻辑移至它自己的 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;
}
}
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".
我想你错过了一些,比如 Buffer
运算符:
Button button = new Button();
...
var subscription = button
.ButtonClicks()
.Buffer(TimeSpan.FromSeconds(0.5), 100) // Perform action after 100 clicks or after half a second, whatever comes first
.Select(buffer => buffer.Count)
.Subscribe(clickCount =>
{
// Do something with clickCount
})
帮手class:
public static class Extensions
{
public static IObservable<EventPattern<RoutedEventArgs>> ButtonClicks(this Button control)
{
return Observable.FromEventPattern<RoutedEventHandler, RoutedEventArgs>(
h => control.Click += h,
h => control.Click -= h);
}
}
申请结束时:
subscription.Dispose();
当然 Buffer 有一些重载:
Buffer(TimeSpan.FromSeconds(1))
-> 缓冲一秒钟,然后执行操作(与接受的答案相同的结果)
Buffer(100)
-> 点击 100 次后执行操作
这在 Rx 中非常简单。这就是你需要的:
IObservable<long> query =
source
.Select(t => Observable.Timer(TimeSpan.FromMilliseconds(100.0)))
.Switch();
因此,只要 source
是一个 IObservable<T>
,它会在用户执行某个操作时产生一个值,那么这个可观察对象只会在 100.0
出现时产生一个值自最后一个值以来的毫秒不活动时间。
现在,您可能想要整理一下,以便查询生成原始值并编组回调度程序。这也很简单。
试试这个:
IObservable<T> query =
source
.Select(t =>
Observable
.Timer(TimeSpan.FromMilliseconds(100.0))
.Select(_ => t))
.Switch()
.ObserveOnDispatcher();
简单。