如何防止滑块由于异步绑定而跳转

本文关键字:异步 绑定 何防止 于异步 | 更新日期: 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绑定中,它将减少任务的数量(没有它,滑块的一次移动可能会产生数十个任务(。