绑定的延迟属性来自.Net 4.0中的.Net 4.5

如何在.Net 4.0中实现关于绑定的.Net 4.5( 此处描述)的延迟属性?

我知道我不能inheritanceBindingBase,因为ProvideValue是密封的。

我可以实现MarkupExtension,但这意味着我现在必须重写BindingExtension中的所有属性还有其他方法吗?

我将创建一个AttachedProperty ,指定延迟的时间量。 当绑定值更改时, AttachedProperty将启动(或重置)计时器,并在达到指定的时间量时手动更新绑定的源。

您可以使用以下命令更新源绑定:

 BindingOperations.GetBindingExpressionBase( dependencyObject, dependencyProperty).UpdateSource(); 

编辑

我今天正在修复一些旧代码中的错误,并注意到它使用附加行为实现了延迟的属性更改通知。 我想到了这个问题,所以按照我在代码中评论过的链接,发现自己刚才发布了一个关于延迟绑定的问题 。 最常见的答案是我当前实现的一个,它是一些附加的属性,它们在X毫秒过去后更新绑定源。

最后,我决定使用合成将DelayedBinding实现为MarkupExtension。

我唯一的问题是DataTemplates ProvideValue如果来自IProvideValueTarget TargetProperty为null,则返回此值。

 [MarkupExtensionReturnType(typeof(object))] public class DelayedBindingExtension : MarkupExtension { private readonly Binding _binding = new Binding(); public DelayedBindingExtension() { //Default value for delay Delay = TimeSpan.FromSeconds(0.5); } public DelayedBindingExtension(PropertyPath path) : this() { Path = path; } #region properties [DefaultValue(null)] public object AsyncState { get { return _binding.AsyncState; } set { _binding.AsyncState = value; } } [DefaultValue(false)] public bool BindsDirectlyToSource { get { return _binding.BindsDirectlyToSource; } set { _binding.BindsDirectlyToSource = value; } } [DefaultValue(null)] public IValueConverter Converter { get { return _binding.Converter; } set { _binding.Converter = value; } } [TypeConverter(typeof(CultureInfoIetfLanguageTagConverter)), DefaultValue(null)] public CultureInfo ConverterCulture { get { return _binding.ConverterCulture; } set { _binding.ConverterCulture = value; } } [DefaultValue(null)] public object ConverterParameter { get { return _binding.ConverterParameter; } set { _binding.ConverterParameter = value; } } [DefaultValue(null)] public string ElementName { get { return _binding.ElementName; } set { _binding.ElementName = value; } } [DefaultValue(null)] public object FallbackValue { get { return _binding.FallbackValue; } set { _binding.FallbackValue = value; } } [DefaultValue(false)] public bool IsAsync { get { return _binding.IsAsync; } set { _binding.IsAsync = value; } } [DefaultValue(BindingMode.Default)] public BindingMode Mode { get { return _binding.Mode; } set { _binding.Mode = value; } } [DefaultValue(false)] public bool NotifyOnSourceUpdated { get { return _binding.NotifyOnSourceUpdated; } set { _binding.NotifyOnSourceUpdated = value; } } [DefaultValue(false)] public bool NotifyOnTargetUpdated { get { return _binding.NotifyOnTargetUpdated; } set { _binding.NotifyOnTargetUpdated = value; } } [DefaultValue(false)] public bool NotifyOnValidationError { get { return _binding.NotifyOnValidationError; } set { _binding.NotifyOnValidationError = value; } } [DefaultValue(null)] public PropertyPath Path { get { return _binding.Path; } set { _binding.Path = value; } } [DefaultValue(null)] public RelativeSource RelativeSource { get { return _binding.RelativeSource; } set { _binding.RelativeSource = value; } } [DefaultValue(null)] public object Source { get { return _binding.Source; } set { _binding.Source = value; } } [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public UpdateSourceExceptionFilterCallback UpdateSourceExceptionFilter { get { return _binding.UpdateSourceExceptionFilter; } set { _binding.UpdateSourceExceptionFilter = value; } } [DefaultValue(UpdateSourceTrigger.Default)] public UpdateSourceTrigger UpdateSourceTrigger { get { return _binding.UpdateSourceTrigger; } set { _binding.UpdateSourceTrigger = value; } } [DefaultValue(null)] public object TargetNullValue { get { return _binding.TargetNullValue; } set { _binding.TargetNullValue = value; } } [DefaultValue(null)] public string StringFormat { get { return _binding.StringFormat; } set { _binding.StringFormat = value; } } [DefaultValue(false)] public bool ValidatesOnDataErrors { get { return _binding.ValidatesOnDataErrors; } set { _binding.ValidatesOnDataErrors = value; } } [DefaultValue(false)] public bool ValidatesOnExceptions { get { return _binding.ValidatesOnExceptions; } set { _binding.ValidatesOnExceptions = value; } } [DefaultValue(null)] public string XPath { get { return _binding.XPath; } set { _binding.XPath = value; } } [DefaultValue(null)] public Collection ValidationRules { get { return _binding.ValidationRules; } } #endregion [DefaultValue(null)] public TimeSpan Delay { get; set; } public override object ProvideValue(IServiceProvider serviceProvider) { try { _binding.Mode = BindingMode.TwoWay; _binding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit; } catch (InvalidOperationException) //Binding in use already don't change it { } var valueProvider = serviceProvider.GetService(typeof (IProvideValueTarget)) as IProvideValueTarget; if (valueProvider != null) { var bindingTarget = valueProvider.TargetObject as DependencyObject; var bindingProperty = valueProvider.TargetProperty as DependencyProperty; if (bindingProperty != null && bindingTarget != null) { var result = (BindingExpression)_binding.ProvideValue(serviceProvider); new DelayBindingManager(result, bindingTarget, bindingProperty, Delay); return result; } } return this; } private class DelayBindingManager { private readonly BindingExpressionBase _bindingExpression; private readonly DependencyProperty _bindingTargetProperty; private DependencyPropertyDescriptor _descriptor; private readonly DispatcherTimer _timer; public DelayBindingManager(BindingExpressionBase bindingExpression, DependencyObject bindingTarget, DependencyProperty bindingTargetProperty, TimeSpan delay) { _bindingExpression = bindingExpression; _bindingTargetProperty = bindingTargetProperty; _descriptor = DependencyPropertyDescriptor.FromProperty(_bindingTargetProperty, bindingTarget.GetType()); if (_descriptor != null) _descriptor.AddValueChanged(bindingTarget, BindingTargetTargetPropertyChanged); _timer = new DispatcherTimer(); _timer.Tick += TimerTick; _timer.Interval = delay; } private void BindingTargetTargetPropertyChanged(object sender, EventArgs e) { var source = (DependencyObject)sender; if (!BindingOperations.IsDataBound(source, _bindingTargetProperty)) { if (_descriptor != null) { _descriptor.RemoveValueChanged(source, BindingTargetTargetPropertyChanged); _descriptor = null; } return; } _timer.Stop(); _timer.Start(); } private void TimerTick(object sender, EventArgs e) { _timer.Stop(); _bindingExpression.UpdateSource(); } } } 

直接移植是不可能的,但我们可以使用MultiBinding “模拟”这个

请注意,这是一个非常紧密耦合的解决方案,如果在页面上使用了许多此类绑定,可能效果不佳……

两个必须有 ……

  1. 它接受单个项ArrayList的延迟(以毫秒为单位)作为转换器参数。
  2. 每个这样的延迟绑定必须携带自己的转换器参数实例。

测试XAML如下……

      2000            

在此示例中, TextBlock呈现2秒延迟后在下面的TextBox中键入的内容。 TextBox.Text是主要的数据源。

DelayHelper是多转换器,其工​​作方式如下所示……

 public class DelayHelper : IMultiValueConverter { #region IMultiValueConverter Members public object Convert( object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) { var sourceElement = values[1] as FrameworkElement; var dp = values[2] as DependencyProperty; var paramArray = parameter as ArrayList; var existingValue = paramArray != null && paramArray.Count == 2 ? paramArray[1] : sourceElement.GetValue(dp); var newValue = values[0]; var bndExp = BindingOperations.GetMultiBindingExpression(sourceElement, dp); var temp = new DispatcherTimer() { IsEnabled = false }; var dspTimer = new DispatcherTimer( new TimeSpan(0,0,0,0, int.Parse(paramArray[0].ToString())), DispatcherPriority.Background, new EventHandler( delegate { if (bndExp != null && existingValue != newValue) { var array = bndExp.ParentMultiBinding.ConverterParameter as ArrayList; var existingInterval = array[0]; array.Clear(); array.Add(existingInterval); array.Add(newValue); bndExp.UpdateTarget(); } temp.Stop(); }), sourceElement.Dispatcher); temp = dspTimer; dspTimer.Start(); return existingValue; } public object[] ConvertBack( object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } #endregion } 

所以这段代码利用了这些事实

  1. MultiBinding可以接受目标UI元素( TextBlock )及其自身多边界的依赖属性( TextBlock.TextProperty )。
  2. 绑定后,多重绑定不能改变其属性,包括ConveterParameter 。 但转换器参数本身可以是一个参考对象,它在整个绑定过程中保持其引用是活动的,例如ArrayList
  3. DispatcherTimer必须在第一个Tick后停止。 因此我们使用temp变量是非常必要的。
  4. 更新为每个源文本更新生成2个转换器通道。 没有这种行为的escpae。 这可能导致缓慢使用许多延迟绑定。
  5. 确保在多个延迟绑定中不共享相同的转换器参数

如果这有帮助,请告诉我……