使用异步绑定时如何避免闪烁

时间:2016-11-22 09:22:41

标签: c# wpf asynchronous data-binding

我已经创建了一个例子来说明我的问题。

视图模型:

public class VM : INotifyPropertyChanged
{
    private double _value = 1;
    public double Value
    {
        get { return _value; }
        set
        {
            _value = value; 
            OnPropertyChanged();
        }
    }

    public VM()
    {
        var timer = new DispatcherTimer();
        timer.Interval = TimeSpan.FromTicks(1);
        timer.Tick += (s, e) => { Value += 1; };
        timer.Start();
    }

    // OnPropertyChanged stuff ... 
    }
}

查看:

<Window.DataContext>
    <namespace:VM/>
</Window.DataContext>
<Grid>
    <TextBox Text="{Binding Value, IsAsync=True, FallbackValue=Test}"/>
</Grid>

运行我的应用程序时,文本框中的文字会闪烁。在更新过程中,会显示FallbackValue,这对我来说毫无意义。

有谁知道更新过程中显示FallbackValue的目的或有什么好处?有没有办法在异步更新过程中显示旧值?

4 个答案:

答案 0 :(得分:2)

鉴于您在绑定中使用IsAsync=True,这对我来说似乎很正常。来自the documentation

  

在等待值到达时,绑定会报告FallbackValue(如果有的话)

当引发PropertyChanged事件时,WPF会启动更新绑定目标的过程。通常这会同步发生,并立即调用属性getter来更新值。

但是您使用的是IsAysnc=True,因此WPF会使用回退值填充目标,并启动异步请求以便稍后检索实际的属性值。在该请求完成之前,将显示后备值。

  

有谁知道更新过程中显示FallbackValue的目的或有什么好处?

根据文档,IsAsync=True设置背后的意图是当属性getter是或可能很慢时使用它。您的代码告诉WPF属性值已更改,因此它知道旧值不再有效。您的代码还告诉(通过XAML中的IsAsync)属性获取器可能需要一些时间来提供新值,因此它会延迟检索该值直到以后。

与此同时,WPF应该展示什么?这就是后备价值所在。

  

有没有办法在异步更新过程中显示旧值?

如果您不想要在WPF中为此功能设计的行为,您应该自己异步检索新数据,并在拥有它时通过setter更新属性。对于物业吸气者而言,无论如何都不是一个好主意,所以无论如何这将是一个更好的设计。

答案 1 :(得分:1)

我遇到了同样的问题,但有图像源。我已经删除了绑定上的 IsAsync 并且我已经让我的 getter 异步了:

// This field hold a copy of the thumbnail.
BitmapImage thumbnail;

// Boolean to avoid loading the image multiple times.
bool loadThumbnailInProgress;

// I'm using object as the type of the property in order to be able to return
// UnsetValue.
public object Thumbnail
{
  get {
    if (thumbnail != null) return thumbnail;
 
    if (!loadThumbnailInProgress) {
      // Using BeginInvoke() allow to load the image asynchronously.
      dispatcher.BeginInvoke(new Action(() => {
        thumbnail = LoadThumbnail();
        RaisePropertyChanged(nameof(Thumbnail));
      }));
      loadThumbnailInProgress = true;
    }
    // Returning UnsetValue tells WPF to use the fallback value.
    return DependencyProperty.UnsetValue;
  }
}

答案 2 :(得分:0)

有时绑定会失败,失败很重要。如果发生错误,后备值选项会向用户显示消息,而不是发生任何事情。如果您希望您的fallbackvalue显示包含的先前值,我可以考虑一些尝试方法:可能将值保存在引用字符串和/或另一个控件中,然后绑定到该控件

但是如果您根本不想显示回退值,则需要进行代码检查,以查看绑定失败的方式/或是否很慢,并将其包含在您的代码中

答案 3 :(得分:0)

我找到了一种避免闪烁的方法,只需继承文本框并覆盖它的textproperty-metadata。

自定义TextBoxControl

public class CustomTextBox : TextBox
{
    static CustomTextBox()
    {
        TextProperty.OverrideMetadata(typeof(CustomTextBox), new FrameworkPropertyMetadata(null, null, CoerceChanged));
    }

    private static object CoerceChanged(DependencyObject d, object basevalue)
    {
        var tb = d as TextBox;
        if (basevalue == null)
        {
            return tb.Text;
        }
        return basevalue;
    }
}

查看

<Window.DataContext>
    <namespace:VM/>
</Window.DataContext>
<Grid>
    <namespace:CustomTextBox Text="{Binding Value, IsAsync=True}"/>
</Grid>

让文本绑定没有后备值非常重要。因此,在更新过程中,文本设置为textproperty defalut值 - 因此在本例中为 null

CoerceChanged处理程序检查新值是否为null。如果是这样,他返回旧值,以便在更新过程中仍然显示旧值。