如何限制滑块的值更改事件?
Posted
技术标签:
【中文标题】如何限制滑块的值更改事件?【英文标题】:How do I throttle a slider's value change event? 【发布时间】:2011-11-21 03:35:01 【问题描述】:我有一个滑块,它在值更改时会强制进行相当严重的计算,所以我想在用户完成滑动后例如 50 毫秒过去后对其进行节流以触发实际事件。
虽然我了解了一些关于 Rx 的各种知识,但我不清楚我应该如何使用 MVVM 模式来解决这个问题。
在我当前的 MVVM 方法中,我将滑块值绑定到我的 viewModel。我更愿意添加 Rx 节流阀,尽可能减少对现有代码的影响(至少作为开始)。
我看到了一些关于 MVVM 和 Rx 的其他线程,但我认为它们并没有引导我找到解决问题的确切方向。我看到了各种可能的方法,不想发明自行车。
【问题讨论】:
很公平,但请相信我们的标签系统。例如,您可以在 [system.reactive] 标签 上获取 RSS 提要,但无法获取标题中包含“系统响应式”的问题提要。 【参考方案1】:在这种情况下,您应该绑定到 ViewModel 的 PropertyChanged 事件,例如:
Observable.FromEvent<PropertyChangedEventArgs>(x => this.PropertyChanged +=x, x => this.PropertyChanged -= x)
.Where(x => x.PropertyName == "SliderName")
.Select(_ => this.SliderName)
.Throttle(TimeSpan.FromMilliseconds(50));
或者,如果您使用的是ReactiveUI,它看起来像这样:
this.WhenAnyValue(x => x.SliderName)
.Throttle(TimeSpan.FromMilliseconds(50), RxApp.DeferredScheduler);
【讨论】:
在 ViewModel 中,View 应该绑定到 ViewModel 上有意义的属性 所以如果我理解正确的话,你需要一些东西来订阅这个 observable 吗? 是的,您可能会执行 .Subscribe(x => DebouncedSliderValue = x),或者可能实际执行您将要执行的操作,例如 .Subscribe(x => RecalculateTheStuff(x) )【参考方案2】:让我们概述一下问题。你有一个视图模型,它有一些 double
类型的属性。当为该属性分配一个值时,会进行相当昂贵的计算。通常不会有问题,但是当 UI 将 Slider
的值绑定到此属性时,生成的快速更改确实会产生问题。
首先要在视图和负责处理此问题的视图模型之间做出决定。可以说,视图模型“选择”将属性分配作为费用操作的两种方式,另一方面,视图“选择”使用Slider
分配属性。
我的选择是从事物的角度来看,因为那是实现这一点的更好地方。但是,与其直接摆弄 View,不如构建一个新的 Control
来添加该功能。我们称之为DelaySlider
。它将派生自Silder
,并具有两个额外的依赖属性Delay
和DelayedValue
。 DelayedValue
将匹配 Value
属性的现有值,但仅在自上次更改 Value
后经过 Delay
毫秒之后。
这里是控件的完整代码:-
public class DelaySlider : Slider
private DispatcherTimer myTimer;
private bool myChanging = false;
#region public double DelayedValue
public double DelayedValue
get return (double)GetValue(DelayedValueProperty);
set SetValue(DelayedValueProperty, value);
public static readonly DependencyProperty DelayedValueProperty =
DependencyProperty.Register(
"DelayedValue",
typeof(double),
typeof(DelaySlider),
new PropertyMetadata(0.0, OnDelayedValuePropertyChanged));
private static void OnDelayedValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
DelaySlider source = d as DelaySlider;
if (source != null && !source.myChanging)
source.Value = (double)e.NewValue;
#endregion public double DelayedValue
#region public int Delay
public int Delay
get return (int)GetValue(DelayProperty);
set SetValue(DelayProperty, value);
public static readonly DependencyProperty DelayProperty =
DependencyProperty.Register(
"Delay",
typeof(int),
typeof(DelaySlider),
new PropertyMetadata(0, OnDelayPropertyChanged));
private static void OnDelayPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
DelaySlider source = d as DelaySlider;
if (source != null)
source.OnDelayPropertyChanged((int)e.OldValue, (int)e.NewValue);
private void OnDelayPropertyChanged(int oldValue, int newValue)
if (myTimer != null)
myTimer.Stop();
myTimer = null;
if (newValue > 0)
myTimer = new DispatcherTimer();
myTimer.Tick += myTimer_Tick;
myTimer.Interval = TimeSpan.FromMilliseconds(newValue);
void myTimer_Tick(object sender, EventArgs e)
myTimer.Stop();
myChanging = true;
SetValue(DelayedValueProperty, Value);
myChanging = false;
#endregion public int Delay
protected override void OnValueChanged(double oldValue, double newValue)
base.OnValueChanged(oldValue, newValue);
if (myTimer != null)
myTimer.Start();
现在将您的 Silder
替换为 DelaySlider
并将您的 View-Model 属性绑定到 DelayedValue
并在其 Delay
属性中指定您的毫秒延迟值。
您现在拥有了一个有用的可重用控件,您没有在视图中使用讨厌的技巧,您在视图的代码隐藏中没有额外的代码,视图模型未更改且不受干扰,并且您根本不需要包含 Rx 的东西。
【讨论】:
嗯,安东尼,我想知道,你一般不喜欢 Rx 吗?当我看到这个和另一个答案时,我认为另一个更干净。还想象一下,我正在使用付费控件库中的一些高级滑块,我可能无法创建自己的滑块,但很容易延迟。实际上,在我的情况下,我使用的范围滑块本身就相当复杂,两个值上都没有几个计时器,所以在我的情况下,我认为 Rx 更好。在RX组装上加个引用,改写几行代码也不算太糟糕。 大声笑,我的解决方案是 2 行长。这是为什么你应该使用Rx的教科书原因。 @Valentin:请重新阅读我的第一段。它是否充分描述了您所面临的场景? @Paul:我会坚持我的立场。 > “而“聪明”的方法需要在每个实例中实现” - 通过创建 method以上是关于如何限制滑块的值更改事件?的主要内容,如果未能解决你的问题,请参考以下文章