首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >.Net 4.0中来自.Net 4.5的绑定的延迟属性

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

Stack Overflow用户
提问于 2011-10-05 20:15:55
回答 3查看 3.9K关注 0票数 13

如何在.Net 4.0的binding上实现.Net 4.5 (描述为here)中的延迟属性?

我知道我不能继承BindingBase,因为ProvideValue是密封的。

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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-10-05 21:32:25

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

您可以使用以下内容来更新源绑定:

代码语言:javascript
运行
复制
BindingOperations.GetBindingExpressionBase(
    dependencyObject, dependencyProperty).UpdateSource();

编辑

我今天修复了一些旧代码中的一个bug,并注意到它使用附加的行为实现了延迟的属性更改通知。我想到了这个问题,所以沿着我在代码中注释的链接,发现自己遇到了一个问题,我之前在so delaying a binding上发布了一个问题。最上面的答案是我目前已经实现的,它是一些附加的属性,在X毫秒后更新绑定的源代码。

票数 3
EN

Stack Overflow用户

发布于 2011-10-07 00:00:48

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

我遇到的唯一问题是,如果TargetProperty from IProvideValueTarget为空,DataTemplates ProvideValue应该返回这个。

代码语言:javascript
运行
复制
[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();
        }
    }
}
票数 6
EN

Stack Overflow用户

发布于 2011-10-05 21:41:08

直接移植是不可能的,但是我们可以用MultiBinding来“模拟”吗?

请注意,这是一个非常紧密耦合的解决方案,如果在一个页面上使用许多这样的绑定,可能不会有很好的性能……

两个必须有 ...

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

测试XAML如下...

代码语言:javascript
运行
复制
    <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是一个多转换器,其工作原理如下所示。

代码语言:javascript
运行
复制
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可以接受本身绑定了multi-bound.
  2. Once的目标UI元素(TextBlock)及其依赖属性(TextBlock.TextProperty),多绑定不能更改其属性(包括ConveterParameter )。但是转换器参数本身可以是一个引用对象,它在绑定处于活动状态时始终保持其引用,例如,ArrayList.
  3. The DispatcherTimer必须在其第一个Tick之后停止。因此,我们使用essential.
  4. The更新中的temp变量为每个源文本更新生成2次转换。此行为不存在任何escpae。这可能会导致速度变慢,因为许多延迟的绑定都是used.
  5. Make的确保您不会在多个延迟的绑定中共享相同的转换器参数

让我知道这是否有帮助。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7661149

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档