2013-02-26 2 views
2

В моем понимании, мы можем использовать INofityProperty в приложении MVVM стиля с кодом, аналогичным следующимиMVVM INotifyPropertyChanged с автоматическими свойствами реализации Названия

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

Теперь я видел Josh Smith's excellent example где он реализует дополнительным код, чтобы зафиксировать, что произойдет, если разработчик вводит имя свойства, которое не распознается, например, опечатка!

Скажите, пожалуйста, если вы ненавидите это, но есть способ получить имя метода из трассировки стека. Таким образом, мы могли бы вместо того, чтобы осуществить что-то вроде

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 - это длинный путь в будущем, но я учусь в свое время.

Итак, мой вопрос от тех, кто его использовал, действительно ли лучше иметь подход, который предупреждает пользователя об ошибке по сравнению с удалением опции для ошибки (или вы чувствуете, что я пропущен что нибудь); Дело в том, что Джош Смит признан экспертом в этой области, и поэтому, если он предложит этот подход, то обычно я буду слепо, но в этом случае я не могу помочь, но викторину и чувствую необходимость понимать больше.

ответ

1

Проблема с использованием StackTrace заключается в том, что он неправильно заполнен в сборках релизов. Чтобы преодолеть эту проблему, есть несколько подходов к ее устранению и облегчить создание события PropertyChanged для разработчиков.

Смотрите этот вопрос: Implementing INotifyPropertyChanged - does a better way exist? и выбрать решение, которое подходит вам :)

Лично я предпочитаю следующее:

// 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, то накладные расходы, возникающие при проверке дерева выражений, ограничены. Это решение является перестраховочным фактором, поэтому у вас нет магических строк.

Мне также нравится подход .NET 4.5 с использованием CallerMemberNameAttribute, но кажется, что он не работает в портативных библиотеках классов.

+0

Это хорошо , но я не получаю ваш комментарий о «неправильно заполненном в сборках релизов». У вас есть что-нибудь, что я могу прочитать об этом (или даже фраза, которую я могу сам Google) +1 – Dave

+0

Благодарим вас за редактирование, извините, что ограничено только 1x +1 – Dave

+0

Я имел в виду, t доверять StackTrace в сборках релизов, поскольку код может быть оптимизирован, а методы, возможно, встроены. В случае свойств вы можете быть в порядке, но я бы вообще не использовал StackTrace. Есть несколько дискуссий по этой теме, например: http://stackoverflow.com/questions/7585010/exception-stack-trace-difference-between-debug-and-release-mode –

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

Я сделал свой собственный внедрение для управления уведомлениями об изменении свойств.

Первая часть представляет собой классический класс 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)); 
    } 
} 

В основном, этот класс обеспечивает: - Простой пользовательский интерфейс нить функцию диспетчерская - проверка отладки на имущество, уведомляются - мульти уведомление недвижимости function

Вторая часть - это централизованный класс, который перегруппирует все аргументы событий уведомления, используемые в моем приложении. В основном это исключает использование нескольких одинаковых жестко закодированных строк во всем приложении, если свойство используется несколько раз. Но также проще для реорганизации кода ремонтировать в целом.

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

Надеется, что это поможет

- Брю