如何在.Net 4.0的binding上实现.Net 4.5 (描述为here)中的延迟属性?
我知道我不能继承BindingBase,因为ProvideValue是密封的。
我可以实现MarkupExtension,但这意味着我现在必须重写BindingExtension中的所有属性,还有其他方法吗?
发布于 2011-10-05 21:32:25
我将创建一个AttachedProperty来指定延迟的时间量。AttachedProperty将在绑定值更改时启动(或重置)计时器,并在达到指定的时间量时手动更新绑定源。
您可以使用以下内容来更新源绑定:
BindingOperations.GetBindingExpressionBase(
dependencyObject, dependencyProperty).UpdateSource();编辑
我今天修复了一些旧代码中的一个bug,并注意到它使用附加的行为实现了延迟的属性更改通知。我想到了这个问题,所以沿着我在代码中注释的链接,发现自己遇到了一个问题,我之前在so delaying a binding上发布了一个问题。最上面的答案是我目前已经实现的,它是一些附加的属性,在X毫秒后更新绑定的源代码。
发布于 2011-10-07 00:00:48
最后,我决定使用组合将DelayedBinding实现为MarkupExtension。
我遇到的唯一问题是,如果TargetProperty from IProvideValueTarget为空,DataTemplates ProvideValue应该返回这个。
[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<ValidationRule> 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();
}
}
}发布于 2011-10-05 21:41:08
直接移植是不可能的,但是我们可以用MultiBinding来“模拟”吗?
请注意,这是一个非常紧密耦合的解决方案,如果在一个页面上使用许多这样的绑定,可能不会有很好的性能……
两个必须有 ...
ArrayList中的延迟作为转换器参数。测试XAML如下...
<TextBlock xmlns:Collections="clr-namespace:System.Collections;assembly=mscorlib"
xmlns:System="clr-namespace:System;assembly=mscorlib" >
<TextBlock.Resources>
<local:DelayHelper x:Key="DelayHelper"/>
<Collections:ArrayList x:Key="MultiConverterParameter">
<System:Int32>2000</System:Int32>
</Collections:ArrayList>
</TextBlock.Resources>
<TextBlock.Text>
<MultiBinding UpdateSourceTrigger="LostFocus"
Converter="{StaticResource DelayHelper}"
ConverterParameter="{StaticResource MultiConverterParameter}">
<Binding Path="Text" ElementName="MyTextBox" Mode="OneWay" />
<Binding RelativeSource="{RelativeSource Self}"/>
<Binding BindsDirectlyToSource="True"
Source="{x:Static TextBlock.TextProperty}"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBox x:Name="MyTextBox" Text="Test..."/>在此示例中,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
}因此,这段代码利用了以下事实
TextBlock)及其依赖属性(TextBlock.TextProperty),多绑定不能更改其属性(包括ConveterParameter )。但是转换器参数本身可以是一个引用对象,它在绑定处于活动状态时始终保持其引用,例如,ArrayList.DispatcherTimer必须在其第一个Tick之后停止。因此,我们使用essential.temp变量为每个源文本更新生成2次转换。此行为不存在任何escpae。这可能会导致速度变慢,因为许多延迟的绑定都是used.让我知道这是否有帮助。
https://stackoverflow.com/questions/7661149
复制相似问题