如何防止滑块由于异步绑定而跳转
本文关键字:异步 绑定 何防止 于异步 | 更新日期: 2023-09-27 18:34:54
我有一个滑块,它绑定到具有慢速getter和setter的属性。由于 UI 需要响应,因此绑定具有属性 IsAsync
。但是当我拖动滑块时,它会在光标和 0 之间跳跃(默认 FallbackValue
(。
有没有人知道如何防止这种行为,如何禁用FallbackValue
?
XAML:
<Slider>
<Slider.Value>
<Binding Path="Value" IsAsync="True"/>
<!-- For testing, best plan till now
<PriorityBinding>
<Binding Path="Value" IsAsync="True"/>
<Binding Path="CurrentValue"/>
</PriorityBinding>
-->
</Slider.Value>
</Slider>
代码隐藏:
public int Value
{
get
{
//if (setterCount > 0) // For testing, best plan till now
//{
// return CurrentValue;
//}
Thread.Sleep(100); // Just for simulating slow functionality
return myValue;
}
set
{
//++setterCount; // For testing, best plan till now
//CurrentValue = value;
Thread.Sleep(200); // Just for simulating slow functionality
myValue = value;
//--setterCount;
OnPropertyChanged("Value");
}
}
只是不要在属性中调用阻塞操作,它们不是为此而设计的。
正常绑定值并在命令中查询/验证其输入。
<i:Interaction.Triggers>
<i:EventTrigger EventName="ValueChanged">
<cmd:EventToCommand Command="{Binding UpdateSomethingCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
并在命令中执行异步操作
public ICommand UpdateSomethingCommand { get; } = new RelayCommand(UpdateSomething);
private async void UpdateSomething()
{
await SomeLengthlyCallOrValidation(this.SliderValue);
}
这个解决方案是否可以被认为是"纯MVVM",但让我们提出它,看看它是否适合你。首先,我们定义一个滑块并订阅 Thumb.DragCompleted 事件。我们还将 UpdateSourceTrigger 设置为显式:
<Slider Maximum="100" Minimum="0" Width="300" x:Name="slid" Thumb.DragCompleted="MySlider_DragCompleted" >
<Slider.Value>
<Binding Path="Value" UpdateSourceTrigger="Explicit"/>
</Slider.Value>
</Slider>
现在,在代码隐藏的事件处理程序中,我们执行以下操作:
private void MySlider_DragCompleted(object sender, System.Windows.Controls.Primitives.DragCompletedEventArgs e)
{
BindingExpression be = slid.GetBindingExpression(Slider.ValueProperty);
be.UpdateSource();
}
总而言之,我们在这里所做的只是在"滑动"过程完成时才进行绑定(获取/设置滑块值(。
没有必要使getter异步,只需存储(缓存(最后一个值。您可以正常绑定到属性并自行实现异步更新:
视图:
<Slider Value="{Binding Value, Delay=100}" Maximum="..." />
视图模型:
Task _task = Task.Run(() => { }); // Task.CompletedTask in 4.6
volatile int _id;
double _value;
public double Value
{
get { return _value; }
set
{
_value = value;
OnPropertyChanged();
// task
var id = ++_id; // capture id
_task = _task.ContinueWith(o =>
{
if (_id == id)
{
Thread.Sleep(1000); // simulate work
// this is optional if value stays the same
if (_id == id)
{
_value = value + 10; // simulate different value
Dispatcher.InvokeAsync(() => OnPropertyChanged(nameof(Value)));
}
}
});
}
}
这个想法:记录滑块值的变化并只执行最后一个。
记录是通过将任务与ContinueWith
链接来完成的。首先,您已经完成了任务,每次更改Value
都会创建一个新任务。
我不知道是否有办法检查当前任务何时有延续,因此_id
用于此任务。当_id == id
任务是链中的最后一个时(否则任务不执行任何操作,因为有更多的实际更新(。
第二个if
用于检查下班后何时有更多任务并防止值变回(您目前的效果(。如果您的工作可以返回与设置的值不同的值,则需要此通知。否则,请完全删除第二个if
(不需要(。
请注意,Delay
绑定中,它将减少任务的数量(没有它,滑块的一次移动可能会产生数十个任务(。