7

私は次のようなマルチバインディングを持っています:

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

また、両方のバインディングでIsMouseOverがfalseになり、VisibilityがCollapsedに設定されるまでの間に遅延を追加できるようにしたいと思います。

このDelayBindingの実装を見つけました:http: //www.paulstovell.com/wpf-delaybinding

しかし、それはMultiBindingでは機能せず、MultiBindingで機能するものを作成する方法を理解できませんでした。

コードビハインドのイベントでVisibilityに変更を加えるオプションはありますが、それは機能しますが、バインディングシステムを介してこれを行う方法があれば便利です。

マルチバインディングに遅延を追加する方法はありますか?

編集:レイ、あなたのクラスをコンパイルして実行させるために、私はいくつかの修正をしなければなりませんでした。ただし、更新が伝播されていないため、まだ問題があります。ターゲットプロパティを更新するのは1回だけのようです。

[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のコードを機能させることができませんでしたが、機能するコードにつながるため、回答としてマークしました。私が使用したコードについては、以下の私の答えを参照してください。

4

4 に答える 4

7

リンクした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;
}

仕組み:マークアップ拡張機能自体のChangeCountプロパティへの追加のバインディングが1つあるMultiBindingが構築されます。また、マークアップ拡張機能自体がコンバーターとして登録されます。ソース値が変更されるたびに、バインディングが評価され、コンバーターが呼び出されます。これにより、「実際の」コンバーターが呼び出され、値が計算されます。値をすぐに更新する代わりに、値を_undelayedValueに格納し、前の値(_delayedValue)を返します。また、値が変更された場合、タイマーが開始(または再起動)します。タイマーが起動すると、値が_delayedValueにコピーされ、ChangeCountがインクリメントされ、バインディングが強制的に再評価されます。今回は、新しい_delayedValueが返されます。

于 2010-11-12T22:16:11.810 に答える
3

これは、質問の「マルチバインディングで機能するものを作成する方法を理解できませんでした」の部分にのみ、その方法を説明することで答えます。他の人はこの情報が役に立つと思うかもしれないので、私はそれをここに残して、主要な質問に答える別の答えを追加します。


リンクしたDelayBindingマークアップ拡張機能を、同じように機能するがMultiBindingを使用するDelayMultiBindingクラスに変更するのは非常に簡単です。

マークアップ拡張機能:

  1. DelayMultiBindingExtensionに名前を変更
  2. Bindingsタイプのプロパティを追加しますCollection<BindingBase>
  3. Converterプロパティのタイプを変更する
  4. で、の代わりにProvideValue構築し、すべてのバインディングを渡します。DelayMultiBindingDelayBinding

遅延バインディングクラスの場合:

  1. DelayMultiBindingに名前を変更
  2. 単一のバインディングではなく、バインディングの配列を取得します
  3. 各プロパティに値変更ハンドラーを追加します
  4. バインディングを作成したのと同じように、マルチバインディングを作成します

今書く代わりにMultiBinding、書くDelayMultiBindingExtension

<UserControl.Visibility> 
  <my:DelayMultiBindingExtension Delay="0:0:1" Converter="{StaticResource isMouseOverToVisibiltyConverter}">
    <Binding ElementName="otherElement" Path="IsMouseOver" /> 
    <Binding RelativeSource="{RelativeSource Self}" Path="IsMouseOver" /> 
  </my:DelayMultiBindingExtension> 
</UserControl.Visibility> 

個人的には、2つのクラスをMarkupExtensionであり、タイマーも処理する1つのクラスに変換することでクリーンアップします。

DelayBindingクラスとこのクラスはどちらも、ターゲットへの更新ではなく、ソースへの更新を遅らせることに注意してください。ターゲットへの更新を遅らせたい場合(これはあなたが行います)、私の他の答えを参照してください。

于 2010-11-10T21:10:34.170 に答える
3

しばらく前のプロジェクトでも同様の要件があったため、とという2つのマークアップ拡張機能を作成DelayBindingExtensionしましDelayMultiBindingExtensionた。

これらは通常のように機能しますが、どちらもプロパティであると指定したり、Bindings指定したりできます。あなたの場合、あなたはそれをこのように使うことができますUpdateSourceDelayUpdateTargetDelayTimeSpan

<UserControl.Visibility>
    <db:DelayMultiBinding Converter="{StaticResource yourConverter}"
                          UpdateTargetDelay="00:00:01">
        <Binding ElementName="otherElement" Path="IsMouseOver" />
        <Binding RelativeSource="{RelativeSource Self}" Path="IsMouseOver" />
    </db:DelayMultiBinding>
</UserControl.Visibility>

のソースコードとサンプルの使用法はDelayBindingここからDelayMultiBindingダウンロードできます。 実装の詳細に興味がある場合は、ここで私のブログ投稿をチェックしてください:ソースとターゲットの遅延を伴うDelayBindingとDelayMultiBinding

于 2012-05-02T23:32:08.890 に答える
2

Rayのコードを出発点として使用して、機能するコードをいくつか作成しましたが、完全にエレガントではありません。

XAML:

<local:IsMouseOverToVisibilityConverter x:Key="isMouseOverToVisibiltyConverter" />
<local:DelayingMultiConverter x:Key="delayedIsMouseOverToVisibiltyConverter" Delay="00:00:00.500" Converter="{StaticResource isMouseOverToVisibiltyConverter}" />

...

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

DelayingMultiConverter:

internal class DelayingMultiConverter : IMultiValueConverter, INotifyPropertyChanged
{
    private object undelayedValue;
    private object delayedValue;
    private DispatcherTimer timer;

    private int changeCount;
    public int ChangeCount
    {
        get { return this.changeCount; }
        private set
        {
            this.changeCount = value;
            this.NotifyPropertyChanged("ChangeCount");
        }
    }

    public IMultiValueConverter Converter { get; set; }
    public CultureInfo ConverterCulture { get; set; }
    public object ConverterParameter { get; set; }

    public TimeSpan Delay
    {
        get { return this.timer.Interval; }
        set { this.timer.Interval = value; }
    }

    public DelayingMultiConverter()
    {
        this.timer = new DispatcherTimer();
        this.timer.Tick += Timer_Tick;
    }

    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();
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged(string info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }

    private void Timer_Tick(object sender, EventArgs e)
    {
        timer.Stop();
        delayedValue = undelayedValue;
        ChangeCount++;
    }
}
于 2010-11-15T20:41:17.660 に答える