Как регулировать событие изменения значения ползунка? - PullRequest
16 голосов
/ 20 сентября 2011

У меня есть ползунок, который при изменении значения вызывает довольно серьезные вычисления, поэтому я хочу ограничить его, чтобы запустить фактическое событие после, например, 50-минутного прохода, когда пользователь закончил его скользить.

В то время как я изучил некоторые различныеЧто-то про Rx, неясно, как мне подходить к этому, используя шаблон MVVM.

В моем текущем подходе MVVM я привязал значение слайдера к моей viewModel.Я бы предпочел добавить Rx Throttle с минимально возможным воздействием на существующий код (по крайней мере, в начале).

Я видел некоторые другие потоки о MVVM и Rx, и я не думаю, что они ведут меня в каком-то конкретном направлениис моей проблемой.Я вижу различные возможные подходы и хотел бы не изобретать велосипед.

Ответы [ 2 ]

25 голосов
/ 20 сентября 2011

В этом случае вам следует привязать к событию PropertyChanged вашей ViewModel что-то вроде:

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);
0 голосов
/ 21 сентября 2011

Давайте просто наметим проблему. У вас есть модель просмотра, у которой есть double типизированное свойство. Когда этому свойству присваивается значение, выполняется довольно дорогой расчет. Обычно это не проблема, но когда пользовательский интерфейс связывает значение 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.

Теперь у вас есть полезный элемент многократного использования, вы не перепутали неприятные трюки в представлении, у вас нет дополнительного кода в коде позади представления, модель представления не изменилась и не потревожена, и вы вообще не нужно было включать Rx.

...