2

私の理解では、次のようなコードを使用して、MVVM スタイルのアプリケーションで INofityProperty を使用できます。

    object _SelectedPerson;
    public object SelectedPerson
    {
        get
        {
            return _SelectedPerson;
        }
        set
        {
            if (_SelectedPerson != value)
            {
                _SelectedPerson = value;
                RaisePropertyChanged("SelectedPerson");
            }
        }
    }

ここで、 Josh Smith の優れた例を見てきました。ここでは、タイプミスなど、認識されないプロパティ名を開発者が入力した場合に何が起こるかをキャプチャする追加のコードを実装しています。

これが嫌なら教えてください、スタックトレースからメソッド名を取得する方法があります。したがって、代わりに次のようなものを実装できます

    object _SelectedPerson;
    public object SelectedPerson
    {
        get
        {
            return _SelectedPerson;
        }
        set
        {
            if (_SelectedPerson != value)
            {
                _SelectedPerson = value;
                RaisePropertyChanged(Current.Method);
            }
        }
    }

static class Current
{
    public static string Method()
    {
        StackTrace st = new StackTrace();
        return (st.GetFrame(1).GetMethod().Name.Split('_')[1]);            
    }
}

RaisePropertyChanged イベントは常にセッターで発生するため、これが常に機能すると想定することしかできません (間違っている場合は、修正してください)。

ここで注意してください、私は実際にこれを試す立場にありません.職場(より大きなプロジェクトに取り組むことができる場所)では、私はまだ.NET 2.0を使用しているため、WPF/MVVMは将来的には遠い先です.しかし、私は自分の時間で学んでいます。

したがって、私の質問は、それを使用したことのある人からのものです。エラーのオプションを削除するよりも、ユーザーにエラーを警告するアプローチを使用する方が本当に良いのでしょうか (または、私が何かを誤解していると思いますか); 問題は、Josh Smith がこの分野の専門家として認められていることです。彼がそのアプローチを提案した場合、通常はやみくもに従うことになりますが、今回の場合はクイズをして、もっと理解する必要があると感じずにはいられません。

4

3 に答える 3

2

propertychanged 通知を管理するために、独自の実装を微調整しました。

最初の部分は古典的な NotifierBase クラスです:

/// <summary>
///   Base class for all notifying objects (model adapters, view models, etc.)
/// </summary>
public abstract class NotifierBase : INotifyPropertyChanged
{
    /// <summary>
    ///   Private reference to UI thread
    /// </summary>
    private readonly System.Windows.Threading.Dispatcher _uiThread;

    /// <summary>
    ///   Default Constructor
    /// </summary>
    protected NotifierBase()
    {
        _uiThread = Application.Current != null ? Application.Current.Dispatcher : System.Windows.Threading.Dispatcher.CurrentDispatcher;
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion

    /// <summary>
    ///   Explicit raise of a property changed notification
    /// </summary>
    /// <param name="e"> </param>
    protected void Notify(PropertyChangedEventArgs e)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            //Debug method used to verify that the property we are about to raise is really a member of the current class instance
            CheckProperty(e.PropertyName);

            //raises the notification
            ToUiThread(() => handler(this, e));
        }
    }

    protected void Notify(params PropertyChangedEventArgs[] e)
    {
        foreach (var pcea in e)
        {
            Notify(pcea);
        }
    }

    /// <summary>
    ///   Dispatch an action to the ui thread
    /// </summary>
    /// <param name="action"> Action to dispatch </param>
    protected void ToUiThread(Action action)
    {
        if (_uiThread.CheckAccess()) //if we are already in the UI thread, invoke action
            action();
        else
        {
            //otherwise dispatch in the ui thread
            _uiThread.Invoke(action);
        }
    }

    /// <summary>
    ///   Check if the raised property is a valid property for the current instance type
    /// </summary>
    /// <param name="propertyName"> Name of the raised property </param>
    [DebuggerStepThrough]
    private void CheckProperty(string propertyName)
    {
        Type type = GetType();
        PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
        if (properties.Any(pi => pi.Name == propertyName)) return;

        throw new InvalidOperationException(
            string.Format("Trying to raise notification on property \"{0}\" which does not exists on type \"{1}\"",
                          propertyName, type.Name));
    }
}

基本的に、このクラスは以下を提供します: - 簡単な UI スレッド ディスパッチ機能 - 通知されたプロパティのデバッグ チェック - 複数プロパティ通知機能

2 番目の部分は、アプリケーションで使用されるすべての通知イベント引数を再グループ化する集中クラスです。これにより、主に、プロパティが複数回使用される場合に、複数の同じハードコードされた文字列がアプリケーション全体に存在することを回避できます。しかし、コードのリファクタリングと一般的な保守性も容易です。

public class Npcea 
{

    public static readonly PropertyChangedEventArgs BarCode = new PropertyChangedEventArgs("BarCode");
    public static readonly PropertyChangedEventArgs Cap = new PropertyChangedEventArgs("Cap");
    public static readonly PropertyChangedEventArgs Code = new PropertyChangedEventArgs("Code");
    public static readonly PropertyChangedEventArgs Status = new PropertyChangedEventArgs("Status");
    public static readonly PropertyChangedEventArgs Comments = new PropertyChangedEventArgs("Comments");
}

したがって、基本的に、ビューモデル(またはアダプターなど)では、そのようなプロパティに通知するだけです

    public ResourceStatus Status
    {
        get { return _status; }
        set
        {
            _status = value;
            Notify(Npcea.Status,Npcea.Comments);
        }
    }

これが役立つことを願っています

-- ブルーノ

于 2013-03-19T16:02:06.873 に答える
2

抽象基本クラスを介して INotifyPropertyChanged を実行できます。これは次のようになります。

        public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Event, fired when the Property has changed
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="propertyExpression">() => this.Param</param>
    protected virtual void OnPropertyChanged<T>(Expression<Func<T>> propertyExpression)
    {
        var propertyName = ExtractPropertyName(propertyExpression);
        OnPropertyChanged(propertyName);
    }

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;

        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    /// <summary>
    /// Extracts the propertyname out of the Expression given
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="propertyExpression"></param>
    /// <returns></returns>
    private static string ExtractPropertyName<T>(Expression<Func<T>> propertyExpression)
    {
        var memberExpression = propertyExpression.Body as MemberExpression;
        return memberExpression == null ? null : memberExpression.Member.Name;
    }
}

.Net 4.5 では、次のようなクラスを作成できます。

 public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

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

そこに電話するだけです

OnPropertyChanged();
于 2013-02-26T12:01:55.243 に答える
1

StackTraceを使用する際の問題は、リリースビルドで適切に設定されていないことです。この問題を克服するために、それを修正し、開発者がPropertyChangedイベントを簡単に発生させるためのいくつかのアプローチがあります。

この質問を参照してください:INotifyPropertyChangedの実装-より良い方法はありますか?そしてあなたに合った解決策を選んでください:)

個人的に私は次のことを好みます:

// Helper method
public static PropertyChangedEventArgs CreateArguments<TOwner>(Expression<Func<TOwner, object>> Expression) {
    // determine the Name of the property using the Expression
}

// Within the view-model implementations:
private static readonly PropertyChangedEventArgs TitleProperty = CreateArguments<MyViewModel>(m => m.Title);

private string title;

public string Title {
    get { return this.title; }
    set {
        if (!string.Equals(this.title, value) {
            this.title = value;
            this.OnPropertyChanged(TitleProperty);
        }
    }
}

静的メンバーを使用してPropertyChangedEventArgsを事前生成することにより、式ツリーの検査によって発生するオーバーヘッドが制限されます。このソリューションはリファクタリングセーフであるため、マジックストリングはありません。

CallerMemberNameAttributeを使用した.NET4.5アプローチも気に入っていますが、ポータブルクラスライブラリでは機能しないようです。

于 2013-02-26T11:46:18.680 に答える