Как отключить событие изменения значения ползунка?
У меня есть слайдер, который при изменении значения приводит к довольно серьезному вычислению, поэтому я хочу отключить его, чтобы запустить реальное событие после того, как, например, 50 мс пройдет, когда пользователь закончит его сбрасывать.
В то время как я узнал несколько разных вещей о Rx, неясно, как я должен подходить к этому с использованием шаблона MVVM.
В моем текущем подходе MVVM я получил значение слайдера, привязанное к моей модели viewModel. Я бы предпочел добавить Rx-дроссель с минимальным возможным воздействием на существующий код (как минимум на начало).
Я видел некоторые другие темы о MVVM и Rx, и я не думаю, что они привели меня к определенному направлению с моей проблемой. Я вижу различные возможные подходы и хотел бы не изобретать супруг.
Ответы
Ответ 1
В этом случае вам нужно привязать к событию 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);
Ответ 2
Давайте рассмотрим только проблему. У вас есть модель просмотра, у которой есть определенное свойство double
. Когда значение присваивается этому свойству, имеет место довольно дорогостоящий расчет. Обычно это не проблема, но когда пользовательский интерфейс связывает значение Slider
с этим свойством, сгенерированные быстрые изменения создают проблему.
Первое решение, которое должно быть принято, - это точка зрения и модель представления, которая отвечает за решение этой проблемы. С другой стороны, можно было бы утверждать, что "Модель-образец" выбрала "свойство", чтобы присвоить присвоение свойства операции с затратами, с другой стороны, "Вид" "выбрал" для присвоения свойства с помощью 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 не изменяется и не изменяется, и вы не нужно было включать в себя материал Rx вообще.