C# 如何在使用异步绑定时避免闪烁
我已经创建了一个例子来说明我的问题 视图模型:C# 如何在使用异步绑定时避免闪烁,c#,wpf,asynchronous,data-binding,C#,Wpf,Asynchronous,Data Binding,我已经创建了一个例子来说明我的问题 视图模型: public class VM : INotifyPropertyChanged { private double _value = 1; public double Value { get { return _value; } set { _value = value; OnPropertyChanged();
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 ...
}
}
视图:
运行我的应用程序时,文本框中的文本会闪烁。在更新过程中,将显示Fallback值,这对我来说毫无意义
是否有人知道更新过程中显示Fallback值的目的或好处?在异步更新过程中有没有显示旧值的方法? 有时绑定会失败,失败是很重要的考虑。如果发生错误,回退值选项会向用户显示一条消息,而不是什么都没有发生。如果希望fallbackvalue显示包含的前一个值,我可以考虑几种尝试方法:可能将该值保存在引用字符串中和/或另一个控件中,然后绑定到该控件
但是,如果您根本不想显示fallbackvalue,则需要进行代码检查,以查看绑定如何失败/或速度慢,并将其包含在代码隐藏中,这在我看来是正常的,因为您在绑定中使用了
IsAsync=True
。发件人:
在等待值到达时,绑定报告FallbackValue(如果有)
当引发PropertyChanged
事件时,WPF启动更新绑定目标的过程。通常这将同步发生,立即调用属性getter来更新值
但是您使用的是IsAysnc=True
,因此WPF将使用回退值填充目标,并启动异步请求以稍后检索实际属性值。在该请求完成之前,将显示回退值
是否有人知道更新过程中显示Fallback值的目的或好处
根据文档,IsAsync=True设置背后的意图是当属性getter是或可能是慢时使用它。您的代码告诉WPF属性值已更改,因此它知道旧值不再有效。您的代码还告诉(通过XAML中的IsAsync
)属性getter可能需要一些时间来提供新值,因此它会将检索该值推迟到以后
同时,WPF应该显示什么?这就是回退值的作用
有没有办法在异步更新过程中显示旧值
如果您不希望在WPF中使用为该特性设计的行为,那么您应该自己异步检索新数据,并在有新数据时通过setter更新该属性。无论如何,属性获取程序速度慢不是一个好主意,因此无论如何这都是一个更好的设计。我找到了一种方法,通过从textbox继承并重写其textproperty元数据来避免闪烁 自定义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
强制更改处理程序检查新值是否为null。如果是这样,他将返回旧值,以便在更新过程中仍显示旧值 我也有同样的问题,但有一个图像源。我已删除绑定上的
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;
}
}
他的装订没有坏。它只是简单地显示回退值,直到属性getter返回。@Peter,这正是我了解回退值是什么的方式,但我编辑了我的答案以包含slowresponse@ManDani,我会选择Peters的解决方案,但如果你想坚持你的方式和实验,尝试将回退值绑定到正在保存前一个值的字符串的隐藏控件displayed@JohnChris-您不能为FallbackValue设置绑定,因为它不是DependencyProperty。@ManDani,是的,刚才看到了,我同意。虽然在我的情况下,我没有一个缓慢的获得者。正如您所看到的,显示的值变化非常快。在我的实际应用程序中,a有几个文本框,因此视图需要很多时间。通过设置IsAsnyc=True,我试图避免冻结视图。因此,在我的情况下,显示后备值是没有意义的——它只会导致闪烁。“在我的情况下,我没有一个缓慢的获取者”——那么您使用
IsAsync=True
的动机是什么?发生了什么“冻结视图”及其原因?WPF可以轻松显示任意数量的文本框和其他控件,而不会出现明显的延迟;当你有足够的屏幕上的控件来放慢速度时,你有太多的控件让用户无论如何都无法理解。在我看来,您真正的问题不应该是关于IsAsync
(您可能不应该使用它),而应该是关于为您的真实场景改善用户体验。这是一个有趣的解决方案。。虽然我不认为说你在这里压倒一切是准确的。。这更像是向处理程序注册派生类型(配置概念)。。您可能可以为其添加解析器模式。如果在调用TextProperty.OverrideMetadata
时调用了typeof(TextBox)
,它还会调用处理程序并提供相同的结果吗?。。如果您同时指定了typeof(CustomTextBox)
和typeof(TextBox)
处理程序是否触发两次?@BrettCaswell,则无法使用typeof(TextBox)
-您将得到一个异常。因为已经为textbox textproperty注册了元数据。
// 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;
}
}