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 事件总是发生在 Setter 中(如果我错了,请纠正我)。

现在请注意,我无法真正尝试这个,因为在工作中(我可以从事更大的项目)我仍在使用 .NET 2.0,因此 WPF/MVVM 未来还有很长的路要走但我正在自己的时间学习。

所以,我的问题来自那些使用过它的人,与删除错误选项相比,有一种方法来提醒用户错误真的更好吗(或者你觉得我错过了什么)?问题是,乔什·史密斯是公认的这一领域的专家,所以如果他建议这种方法,那么通常我会盲目地遵循,但在这种情况下,我不禁要问它,觉得有必要了解更多。

4

3 回答 3

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 回答
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 线程调度功能 - 对通知的属性进行调试检查 - 多属性通知功能

第二部分是集中类,它重新组合了我的应用程序中使用的所有通知事件参数。如果一个属性被多次使用,这主要是避免在整个应用程序中使用多个相同的硬编码字符串。但是对于代码重构来说,一般来说,可维护性也更容易。

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 回答
1

使用 StackTrace 的问题是,它没有在发布版本中正确填充。为了克服这个问题,有几种方法可以修复它并使开发人员更容易引发 PropertyChanged 事件。

看到这个问题:Implementing 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 的 .NET 4.5 方法,但它似乎在可移植类库中不起作用。

于 2013-02-26T11:46:18.680 回答