C# 如何防止滑块因异步绑定而跳转?
我有一个滑块,它绑定到一个具有慢速getter和setter的属性。由于UI需要响应,因此绑定具有属性C# 如何防止滑块因异步绑定而跳转?,c#,wpf,mvvm,C#,Wpf,Mvvm,我有一个滑块,它绑定到一个具有慢速getter和setter的属性。由于UI需要响应,因此绑定具有属性IsAsync。但当我拖动滑块时,它会在光标和0之间跳转(默认值FallbackValue) 是否有人知道如何防止这种行为,如何禁用回退值 XAML: 只是不要在属性内调用阻塞操作,它们不适用于此 正常绑定该值,并在命令中查询/验证其输入 <i:Interaction.Triggers> <i:EventTrigger EventName="ValueChanged"&
IsAsync
。但当我拖动滑块时,它会在光标和0之间跳转(默认值FallbackValue
)
是否有人知道如何防止这种行为,如何禁用回退值
XAML:
只是不要在属性内调用阻塞操作,它们不适用于此 正常绑定该值,并在命令中查询/验证其输入
<i:Interaction.Triggers>
<i:EventTrigger EventName="ValueChanged">
<cmd:EventToCommand Command="{Binding UpdateSomethingCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
不需要使getter异步,只需存储(缓存)最后一个值。 您可以正常绑定到属性并自己实现异步更新: 视图: 其思想是:记录滑块值的变化,并只执行最后一个 录制是通过将任务与
ContinueWith
链接来完成的。刚完成任务时,每次更改值
都将创建一个新任务
我不知道是否有一种方法可以检查当前任务是否有继续,因此使用了\u id
。当\u id==id
任务是链中的最后一个时(否则任务什么也不做,因为有更多的实际更新)
第二个如果使用检查工作后的是否有更多任务,并防止值更改回原来的值(您当前的效果)。如果您的工作返回的值与设置的值不同,则需要此通知。否则,如果将第二个完全删除(不需要)
请注意延迟
在绑定中,它将减少任务数量(如果没有它,滑块的一次移动可能会产生几十个任务)。我不知道这个解决方案是否可以被视为“纯MVVM”,但让我们提出它,看看它是否适合您。
首先,我们定义一个滑块并订阅Thumb.DragCompleted事件。我们还将UpdateSourceTrigger设置为explicit:
<Slider Maximum="100" Minimum="0" Width="300" x:Name="slid" Thumb.DragCompleted="MySlider_DragCompleted" >
<Slider.Value>
<Binding Path="Value" UpdateSourceTrigger="Explicit"/>
</Slider.Value>
</Slider>
总而言之,我们在这里所做的只是在“滑动”过程完成后才进行绑定(获取/设置滑块值)。是的,我在标记中提到过示例:不要在属性中放置阻塞操作。当滑块更改命令时,使用交互性触发器调用命令values@Tseng没有帮助。它挡不住!因此,属性存在。我只是有了这个属性。@Andy:它不是阻止UI线程,而是阻止vm本身。由于它没有激发OnPropertyChanged值,UI没有收到关于新值的通知,因此会返回。只需设置值,然后通过命令更正/验证即可(或者通过命令传递当前值并在那里进行验证,如果ViewModel中的successfulProperties不应该做任何会导致延迟的事情,则设置它。这就是MVVM模式的全部要点。任何需要计算值的工作都应该在设置属性值之前完成,或者,作为最后的手段,在e setter仍然在阻塞,您正在调用.Result
。并且您不能使用wait
在那里当您调用Task…Result
时,它阻塞setter,因为它等待任务完成。您是对的,没有注意到100 ms
的行为。让我重新考虑一下。
<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);
}
<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)));
}
}
});
}
}
<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();
}