16

我有一个滑块,它在值更改时会强制进行相当严重的计算,所以我想在用户完成滑动后例如 50 毫秒过去后对其进行节流以触发实际事件。

虽然我了解了一些关于 Rx 的各种东西,但不清楚我应该如何使用 MVVM 模式来解决这个问题。

在我当前的 MVVM 方法中,我将滑块值绑定到我的 viewModel。我更愿意添加 Rx 节流阀,对现有代码的影响最小(至少作为开始)。

我已经看到了一些关于 MVVM 和 Rx 的其他线程,但我认为它们并没有引导我找到解决问题的确切方向。我看到了各种可能的方法,不想发明自行车。

4

2 回答 2

25

在这种情况下,您应该绑定到 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);
于 2011-09-20T17:47:57.317 回答
-8

让我们简单概述一下问题。你有一个视图模型,它有一些double类型的属性。当为该属性分配一个值时,会进行相当昂贵的计算。通常不会有问题,但是当 UI 将 a 的值绑定Slider到此属性时,生成的快速更改确实会产生问题。

首先要做的决定是在视图和负责处理这个问题的视图模型之间。可以说,视图模型“选择”将属性分配作为费用操作的两种方式,另一方面,视图“选择”使用Slider.

我的选择是从事物的角度来看,因为那是实现这一点的更好地方。但是,与其直接摆弄 View,不如构建一个新Control的来添加该功能。我们称它为DelaySlider. 它将派生自Silder并具有两个额外的依赖属性DelayDelayedValue. 将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();
        }

    }
}

现在替换您的SilderwithDelaySlider并将您的 View-Model 属性绑定到DelayedValue并在其属性中指定您的毫秒延迟值Delay

你现在有一个有用的可重用控件,你没有在视图中弄乱讨厌的技巧,你在视图的代码隐藏中没有额外的代码,视图模型没有改变并且不受干扰,你没有必须完全包括 Rx 的东西。

于 2011-09-20T21:43:06.080 回答