我有一个滑块,在值变化时会强制进行相当严重的计算,所以我希望在用户完成滑动后,例如在50ms后传递实际事件。
虽然我学到了一些关于Rx的各种东西,但我不知道如何使用MVVM模式来解决这个问题。
在我目前的MVVM方法中,我将滑块值绑定到我的viewModel。我更愿意添加Rx节流,对现有代码的影响最小(至少作为开头)。
我已经看到了一些关于MVVM和Rx的其他线程,我认为它们并没有引导我对我的问题采取一些确切的方向。我看到了各种可能的方法,并且不想发明一个自行车。
答案 0 :(得分:24)
在这种情况下,您应该绑定到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);
答案 1 :(得分:-8)
让我们概述一下这个问题。您有一个View Model,它有一些double
类型的属性。为此属性分配值时,会进行相当昂贵的计算。通常不会出现问题但是当UI将Slider
的值绑定到此属性时,生成的快速更改确实会产生问题。
首先要做的是在视图和视图模型之间进行处理这个问题。可以认为,View-Model已经“选择”两种方式使属性赋值成为一项费用操作,另一方面View已“选择”使用Slider
分配属性。
我的选择将放在观点方面,因为这是实现这一目标的更好地方。但是,不是直接使用View,我会构建一个新的Control
来添加该功能。我们称之为DelaySlider
。它将派生自Silder
,并具有两个额外的依赖项属性Delay
和DelayedValue
。 DelayedValue
将匹配Value
属性的现有值,但仅在自上次Delay
更改后经过Value
毫秒后才会匹配。
以下是控件的完整代码: -
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
属性中指定毫秒延迟值。
你现在有一个有用的可重用控件,你没有在视图中讨论过令人讨厌的技巧,你在视图的代码隐藏中没有额外的代码,View-Model没有变化且不受干扰你没有必要包括Rx的东西。