3

私のWPFプロジェクトの1つでは、アニメーションを開始する前に動的に変更できるタイミングプロパティを備えたアニメーションストーリーボードをXAMLで作成していました。コード内の値を変更する方法が必要だったので、それらをクラスのプロパティにバインドしました。

基本的な考え方は、アニメーションには2つのフェーズがあり、ストーリーボードでは、合計アニメーション時間を要するObjectAnimationUsingKeyFramesを使用するため、次のようなプロパティがあります。

public TimeSpan RaiseTime { get; set }

public TimeSpan FallTime  { get; set; }

public TimeSpan TotalTime
{ 
    get { return RaiseTime + FallTime; }
}

アニメーションが最初に作成されるとき、これらのプロパティから値が正しく取得されますが、動的に変更できるため、値が変更されたことをXAMLに通知する方法が必要です。

RaiseTimeとFallTimeをDependencyPropertyに変換して、それらの変更がXAMLバインディングに反映されるようにするのは簡単ですが、TotalTimeについてはどうでしょうか。それ自体には値がないので、DPに変えることはできません。

昨日、ランダムなものを検索/試行してこれを機能させ、最終的にはRaiseTimeとFallTimeの両方へのMultiBindingとIMultiValueConverterを使用して機能するようになりました。これは、いくつかのSOの質問とブログ投稿のおかげです 。 2つのソース http://blog.wpfwonderland.com/2010/04/15/simplify-your-binding-converter-with-a-custom-markup-extension/

私の質問は:これは本当にそれを行うための最良の方法ですか?(少なくとも私には)そのような単純な作業のように見えますが、それでも機能するためには非常に多くの(ほとんど定型的な)コードが必要です。TotalTimeをバインドし、更新をXAMLにプッシュするための、より単純で冗長性の低い方法があるはずだと思いましたが、まだ見つけていません。ありますか、それとも私はただ夢を見ていますか?

4

4 に答える 4

6

INotifyPropertyChangedを使用して確実に機能させてから、クラスにバインドするだけです。

コードは次のようになります:(テストされていないコード)

public class PleaseChangeTheNameOfThisClass : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    private TimeSpan _raiseTime;
    public TimeSpan RaiseTime
    { 
        get { return _raiseTime; }
        set 
        {
            if (_raiseTime != value)
            {
                _fallTime = value;
                RaisePropertyChanged("RaiseTime");
                RaisePropertyChanged("TotalTime");
            }
        }
    }

    private TimeSpan _fallTime;
    public TimeSpan FallTime  
    { 
        get { return _fallTime; }
        set 
        {
            if (_fallTime != value)
            {
                _fallTime = value;
                RaisePropertyChanged("FallTime");
                RaisePropertyChanged("TotalTime");
            }
        }
    }

    public TimeSpan TotalTime
    { 
        get { return RaiseTime + FallTime; }
    }
}
于 2013-01-10T18:04:58.380 に答える
1

それでもかなり冗長かもしれませんが、実際にはDependencyPropertiesを使用してこれを行うことができます。

「OnChanged」コールバックをRaiseTimeDPをFallTime更新するTotalTimeDPにアタッチし、TotalTimeを読み取り専用にします(異なるDP登録構文、およびプライベートセッターのみ)。

public TimeSpan RaiseTime
{
    get { return (TimeSpan)GetValue(RaiseTimeProperty); }
    set { SetValue(RaiseTimeProperty, value); }
}
public static readonly DependencyProperty RaiseTimeProperty =
    DependencyProperty.Register("RaiseTime", typeof(TimeSpan), typeof(MainWindow),
                                new PropertyMetadata(TimeSpan.Zero, OnRaiseTimeChanged));

private static void OnRaiseTimeChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    var owner = sender as MainWindow;
    owner.TotalTime = owner.RaiseTime + owner.FallTime;
}


public TimeSpan FallTime
{
    get { return (TimeSpan)GetValue(FallTimeProperty); }
    set { SetValue(FallTimeProperty, value); }
}
public static readonly DependencyProperty FallTimeProperty =
    DependencyProperty.Register("FallTime", typeof(TimeSpan), typeof(MainWindow),
                                new PropertyMetadata(TimeSpan.Zero, OnFallTimeChanged));

private static void OnFallTimeChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    var owner = sender as MainWindow;
    owner.TotalTime = owner.RaiseTime + owner.FallTime;
}


/// <summary>
/// Read-only DP:
/// http://msdn.microsoft.com/en-us/library/ms754044.aspx
/// http://www.wpftutorial.net/dependencyproperties.html
/// </summary>
public TimeSpan TotalTime
{
    get { return (TimeSpan)GetValue(TotalTimeProperty); }
    private set { SetValue(TotalTimePropertyKey, value); }
}
public static readonly DependencyPropertyKey TotalTimePropertyKey =
    DependencyProperty.RegisterReadOnly("TotalTime", typeof(TimeSpan), typeof(MainWindow),
                                        new PropertyMetadata(TimeSpan.Zero));

public static readonly DependencyProperty TotalTimeProperty = TotalTimePropertyKey.DependencyProperty;

デフォルト値を合計する必要があります(ここでは、0 + 0 = 0)。その後、更新OnRaiseTimeChangedOnFallTimeChanged続けます。TotalTime

于 2013-01-10T22:55:33.760 に答える
0

あなたのリンクが示唆しているように、MultiBindingはこれの標準的な方法です。

あいまいな解決策が必要な場合は、カスタムマークアップ拡張機能を作成できます。

于 2013-01-10T17:52:18.437 に答える
0

これももう少し簡潔だと思います。最新のC#で使用可能な「CallerMemberName」を参照してください。

public sealed class ViewModel : INotifyPropertyChanged
{
    private TimeSpan _raiseTime;
    private TimeSpan _fallTime;

    public TimeSpan RaiseTime
    {
        get { return _raiseTime; }
        private set
        {
            if (SetProperty(ref _raiseTime, value))
            {
                OnPropertyChanged("TotalTime");
            }
        }
    }

    public TimeSpan FallTime
    {
        get { return _fallTime; }
        private set
        {
            if (SetProperty(ref _fallTime, value))
            {
                OnPropertyChanged("TotalTime");
            }
        }
    }

    public TimeSpan TotalTime
    {
        get { return RaiseTime + FallTime; }
    }

    #region Put these in a base class...

    private bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
    {
        if (Equals(storage, value))
        {
            return false;
        }

        storage = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    #endregion
}
于 2013-01-30T13:59:34.477 に答える