如何防止滑块因异步绑定而跳转?

时间:2016-02-17 09:44:40

标签: c# wpf mvvm

我有一个滑块,它使用慢速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");
  }
}

3 个答案:

答案 0 :(得分:1)

不要在财产内部调用阻止操作,但这并不意味着这一点。

正常绑定值并在命令中查询/验证它的输入。

<i:Interaction.Triggers>
    <i:EventTrigger EventName="ValueChanged">
        <cmd:EventToCommand Command="{Binding UpdateSomethingCommand}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

在命令中执行async

public ICommand UpdateSomethingCommand { get; } = new RelayCommand(UpdateSomething);
private async void UpdateSomething() 
{
    await SomeLengthlyCallOrValidation(this.SliderValue);
}

答案 1 :(得分:1)

我不知道这个解决方案是否可以被视为“纯MVVM”,但让我们提出它并看看它是否适合你。 首先,我们定义一个Slider并订阅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();
}

总结一下,我们在这里做的只是在“滑动”过程完成时才进行绑定(获取/设置滑块值)。

答案 2 :(得分:0)

没有必要使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在绑定中,它会减少任务数量(没有它一个滑块的移动可能产生几十个任务)。