Silverlight 如何调节滑块';s值更改事件?

Silverlight 如何调节滑块';s值更改事件?,silverlight,mvvm,system.reactive,throttling,Silverlight,Mvvm,System.reactive,Throttling,我得到了一个滑块,它在值改变时会强制执行一个相当严肃的计算,所以我想在用户完成滑动后,对其进行节流,以触发实际事件,例如50毫秒 虽然我了解了一些关于Rx的各种知识,但不清楚我应该如何使用MVVM模式来实现这一点 在我当前的MVVM方法中,我将滑块值绑定到我的viewModel。我更愿意添加Rx油门,对现有代码的影响最小(至少作为一个开始) 我看到了一些关于MVVM和Rx的其他线程,我认为它们并没有引导我找到解决问题的确切方向。我看到了各种可能的方法,不想发明bycicle。在这种情况下,您应该

我得到了一个滑块,它在值改变时会强制执行一个相当严肃的计算,所以我想在用户完成滑动后,对其进行节流,以触发实际事件,例如50毫秒

虽然我了解了一些关于Rx的各种知识,但不清楚我应该如何使用MVVM模式来实现这一点

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


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

在这种情况下,您应该绑定到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));

让我们概括一下这个问题。您有一个视图模型,它具有一些
double
类型化属性。将值指定给此属性时,将进行相当昂贵的计算。通常不会出现问题,但当UI将
滑块的值绑定到此属性时,生成的快速更改确实会产生问题

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

我的选择是站在事物的观点上,因为这是一个更好的地方来实现这一点。但是,我不会直接修改视图,而是构建一个新的
控件来添加该功能。让我们称之为
DelaySlider
。它将派生自
Silder
,并具有两个附加的依赖属性
Delay
DelayedValue
DelayedValue
将与
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
,并将视图模型属性绑定到
DelayedValue
并在其
delay
属性中指定毫秒延迟值


你现在有了一个有用的可重用控件,你没有在视图中弄乱肮脏的把戏,在视图背后的代码中没有额外的代码,视图模型没有改变,没有干扰,你根本不必包含Rx内容。

很公平,但请相信我们的标签系统。你可以在[system.Responsive]标签上获得RSS提要,但你无法获得标题中包含“system Responsive”的问题提要。嗯,安东尼,我想知道,你一般不喜欢Rx吗?当我看到这个和另一个答案时,我认为另一个答案更清晰。再想象一下,我正在使用付费控件库中的一些高级滑块,我可能无法创建自己的滑块,但很容易延迟。实际上,在我的例子中,我使用的是范围滑块,它本身相当复杂,没有两个值上的计时器,所以在我的例子中,我认为Rx更好。在RX程序集上添加一个引用并重写几行代码不会太糟糕。哈哈,我的解决方案是两行。这是一个教科书上关于为什么你应该使用Rx的理由。@Valentin:请重读我的第一段。它是否足够好地描述了您所面临的场景?@Paul:我将坚持我的观点。>“而“聪明”的方法需要每个实例实现”-您可以像重用任何其他代码一样重用它,在ViewModel中创建一个方法,视图应该绑定到ViewModel上的一个有意义的属性,所以如果我理解正确,那么您是否需要订阅此可观察对象?是的,您可能会这样做。订阅(x=>DebouncedSliderValue=x),或者可能实际执行您将要执行的操作,例如。订阅(x=>ReculateTheStuff(x))
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();
        }

    }
}