WPF - 延迟多重绑定(bind)

标签 wpf multibinding

我有一个看起来像这样的多重绑定(bind):

<UserControl.Visibility>
    <MultiBinding Converter="{StaticResource isMouseOverToVisibiltyConverter}">
        <Binding ElementName="otherElement" Path="IsMouseOver" />
        <Binding RelativeSource="{RelativeSource Self}" Path="IsMouseOver" />
    </MultiBinding>
</UserControl.Visibility>

而且,我希望能够在两个绑定(bind)的 IsMouseOver 变为 false 以及将 Visibility 设置为 Collapsed 之间添加延迟。

我找到了这个 DelayBinding 实现: http://www.paulstovell.com/wpf-delaybinding

但是,这不适用于 MultiBinding,而且我一直无法弄清楚如何制作一个适用于 MultiBinding 的产品。

我确实可以选择在代码隐藏中对事件中的可见性进行更改,这会起作用,但如果有某种方法可以通过绑定(bind)系统来完成此操作,那就太好了。

是否有某种方法可以为多重绑定(bind)添加延迟?

编辑:Ray,为了让你的类编译和运行,我必须进行一些修复。然而,问题仍然存在,因为更新没有被传播。似乎只更新一次目标属性。

[ContentProperty("Bindings")]
public class DelayedMultiBindingExtension : MarkupExtension, IMultiValueConverter, INotifyPropertyChanged
{
    public Collection<BindingBase> Bindings { get; private set; }
    public IMultiValueConverter Converter { get; set; }
    public object ConverterParameter { get; set; }
    public CultureInfo ConverterCulture { get; set; }
    public BindingMode Mode { get; set; }
    public UpdateSourceTrigger UpdateSourceTrigger { get; set; }

    public object CurrentValue { get { return _delayedValue; } set { _delayedValue = _undelayedValue = value; _timer.Stop(); } }

    private object _undelayedValue;
    private object _delayedValue;

    private DispatcherTimer _timer;
    public int ChangeCount { get; private set; }  // Public so Binding can bind to it

    public DelayedMultiBindingExtension()
    {
        this.Bindings = new Collection<BindingBase>();
        _timer = new DispatcherTimer();
        _timer.Tick += _timer_Tick;
        _timer.Interval = TimeSpan.FromMilliseconds(500);
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        var valueProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
        if (valueProvider != null)
        {
            var bindingTarget = valueProvider.TargetObject as DependencyObject;
            var bindingProperty = valueProvider.TargetProperty as DependencyProperty;

            var multi = new MultiBinding { Converter = this, Mode = Mode, UpdateSourceTrigger = UpdateSourceTrigger };
            foreach (var binding in Bindings)
                multi.Bindings.Add(binding);
            multi.Bindings.Add(new Binding("ChangeCount") { Source = this, Mode = BindingMode.OneWay });

            var bindingExpression = BindingOperations.SetBinding(bindingTarget, bindingProperty, multi);

            return bindingTarget.GetValue(bindingProperty);
        }

        return null;
    }

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        object newValue =
          Converter.Convert(
            values.Take(values.Length - 1).ToArray(),
            targetType,
            ConverterParameter,
            ConverterCulture ?? culture);

        if (!object.Equals(newValue, _undelayedValue))
        {
            _undelayedValue = newValue;
            _timer.Stop();
            _timer.Start();
        }
        return _delayedValue;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        return
          Converter.ConvertBack(value, targetTypes, ConverterParameter, ConverterCulture ?? culture)
          .Concat(new object[] { ChangeCount }).ToArray();
    }

    private void _timer_Tick(object sender, EventArgs e)
    {
        _timer.Stop();
        _delayedValue = _undelayedValue;
        ChangeCount++;
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs("ChangeCount"));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

EDIT2:尽管我无法让 Ray 的代码工作,但我已将其标记为答案,因为它为我提供了一些可以工作的代码。有关我使用的代码,请参阅下面的答案。

最佳答案

您链接到的 DelayBinding 类仅延迟源更新,而不延迟目标更新。延迟目标更新(这正是您所要求的)要简单得多。像这样的事情应该可以解决问题:

public class DelayedMultiBindingExtension : MarkupExtension, IMultiValueConverter, INotifyPropertyChanged
{
  public Collection<Binding> Bindings { get; set; }
  public IMultiValueConverter Converter { get; set; }
  public object ConverterParameter { get; set; }
  public CultureInfo ConverterCulture { get; set; }
  public BindingMode Mode { get; set; }
  public UpdateSourceTrigger UpdateSourceTrigger { get; set; }

  public object CurrentValue { get { return _delayedValue; } set { _delayedValue = _undelayedValue = value; _timer.Stop(); } }

  object _undelayedValue;
  object _delayedValue;

  DispatcherTimer _timer;
  public int ChangeCount { get; set; }  // Public so Binding can bind to it

  public DelayedMultiBindingExtension()
  {
    _timer = new DispatcherTimer();
    _timer.Tick += _timer_Tick;
  }

  public override object ProvideValue(IServiceProvider serviceProvider)
  {
    var multi = new MultiBinding { Converter = this, Mode = Mode, UpdateSourceTrigger = UpdateSourceTrigger };
    foreach(var binding in Bindings)
      multi.Bindings.Add(binding);
    multi.Bindings.Add(new Binding("ChangeCount") { Source = this });
    return multi;
  }

  public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
  {
    object newValue =
      Converter.Convert(
        values.Take(values.Length-1).ToArray(),
        targetType,
        ConverterParameter,
        ConverterCulture ?? culture);

    if(!object.Equals(newValue, _undelayedValue))
    {
      _undelayedValue = newValue;
      _timer.Stop();
      _timer.Start();
    }
    return _delayedValue;
  }

  public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
  {
    return
      Converter.ConvertBack(value, targetTypes, ConverterParameter, ConverterCulture ?? culture)
      .Concat(new object[] { ChangeCount }).ToArray();
  }

  void _timer_Tick(object sender, EventArgs e)
  {
    _timer.Stop();
    _delayedValue = _undelayedValue;
    ChangeCount++;
    if(PropertyChanged!=null)
      PropertyChanged(this, new PropertyChangedEventArgs("ChangeCount"));
  }

  public event PropertyChangedEventHandler PropertyChanged;
}

工作原理:构造了一个 MultiBinding,它具有一个额外的绑定(bind),即标记扩展本身的 ChangeCount 属性。标记扩展本身也注册为转换器。每当源值更改时,绑定(bind)就会评估并调用转换器。这又调用“实际”转换器来计算该值。它不会立即更新值,而是将其存储在 _undelayedValue 中,并返回之前的值 (_delayedValue)。此外,如果该值已更改,则会启动(或重新启动)计时器。当计时器触发时,该值将被复制到 _delayedValue 中,并且 ChangeCount 会递增,从而强制重新评估绑定(bind)。这次返回了新的 _delayedValue。

关于WPF - 延迟多重绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4147966/

相关文章:

c# - GridViewColumn 中的 WPF 转换器 - 保留 ConvertBack-Method 的值并为每一行拥有自己的 Binding-Instance

c# - 如何手动更新多重绑定(bind)

wpf - 如何在 Expander Header 中设置多重绑定(bind)和图像

WPF MultiBinding - UnsetValue 问题

c# - 如何为我不拥有的类型和属性实现 TypeConverter?

c# - 如何制作非阻塞等待句柄?

wpf - 在 TextBlock (WPF) 中显示图像

wpf - MultiDataTrigger 与具有多重绑定(bind)的 DataTrigger

c# - 从 Storyboard中获取动画元素

.net - 我的列表框中的项目之间的差距