在C# WPF中定义MVVM数据绑定属性的简明方法

4

在C# WPF中,有没有一种简洁的方式来定义ViewModel中用于数据绑定的属性?特别是当有很多属性时,以下属性定义非常冗长:

private bool mSomeProperty;

public bool SomeProperty
{
    get { return this.mSomeProperty; }
    set
    {
        if (value != this.mSomeProperty)
        {
            this.mSomeProperty = value;
            OnPropertyChanged(new PropertyChangedEventArgs("SomeProperty"));
        }
    }
}
7个回答

3
在C#中,我喜欢创建一个基类并在其中放置一些辅助方法。然后让我的ViewModels从它继承。这是我记忆中的内容,大概是这样的:
public class Observable : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void SetProperty<T>(ref T backingField, T newValue,
        string propertyName)
    {
        if (Equals(backingField, newValue))
            return;
        backingField = newValue;
        if (PropertyChanged != null)
        {
            PropertyChanged(this,
                new PropertyChangedEventArgs(propertyName));
        }
    }
}

同时,在使用中:

public class MyClass : Observable
{
    private bool m_someProperty;

    public bool SomeProperty
    {
        get { return m_someProperty; }
        set { SetProperty(ref m_someProperty, value, "SomeProperty"); }
    }
}

这实际上与我已经实现的一个解决方案完全相同,但对我来说仍然感觉笨拙。不过,这是朝着正确方向迈出的一步。 - sourcenouveau
@emddudley - 实际上,他在基类中实现INotifyPropertyChanged的方法比你问题中的代码要重要得多。如果你使用你的代码,并尝试创建一个派生类,你无法从派生类中引发基类的事件。你几乎必须在你的基类中创建一个NotifyPropertyChanged()方法才能使其工作。 - Scott Whitlock

2

1

你可以随心所欲地使用DependencyProperties和propdp...


2
我不会在ViewModel中使用DependencyProperties。更多信息请参见https://dev59.com/ZXVC5IYBdhLWcg3wcgud。 - jbe
这真的取决于你。你链接的帖子没有给出任何不使用 DP 的好理由,除了它们比 POCO 更重量级。除非你确实遇到了使用它们的问题,否则过早地优化它们是...过早的优化。我正在处理的一个项目有大约40个 VM 和 M,所有这些都扩展了 DP,但我没有注意到应用程序运行缓慢。 - user1228

0

您可以考虑使用PostSharp,它允许您装饰属性(然后可以重新定义为自动属性),并将触发NotifyPropertyChanged事件的行注入到setter中。另一个好处是,您可以摆脱与属性名称相同的字符串。

这不是理想的解决方案,因为您需要该工具,并且如果您稍后查看代码,它不会立即显而易见,但它确实使代码更整洁。

我希望微软能在C# 4中添加这样的属性,但如果有的话,我们可能需要等待C#5。


0
如果您正在使用Delphi Prism语言(基于Pascal的.NET语言),只需添加{{link1:notify}}关键字,编译器会自动实现INotifyPropertyChanged并为您编写所有样板代码:
property SomeProperty: bool; notify;

0

我认为你不会通过“正常”的方式使属性声明更小。你可以使用一个Castle拦截器来处理事件的触发。

public class MyViewModel : INotifyPropertyChanged
{
    public event PropertyChanged;
    public void FirePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null) PropertyChanged(propertyName);
    }

    public virtual string MyProperty { get; set; }
    public virtual string MyProperty2 { get; set; }
}

拦截器需要触发属性更改事件:

public class PropertyChangedInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        //Pseudo code
        if (invocation.Method is Property Setter)
        {
            Call FirePropertyChanged of invocation.InvocationTarget ;
        }
    }
}

你只需要确保你的视图模型属性是虚拟的,并且你的视图模型始终是代理而不是视图模型本身。

最好的问候


0

这个答案与Joe White上面的答案非常相似,但代码更加简洁,可能更易于维护,因为属性名称不必作为引号字符串传递。

通过使用CallerMemberName功能,有两种方法可以实现此目的。一种是创建一个包含IPropertyNotifyChanged代码的抽象类,另一种方法是在实际类中包含IPropertyNotifyChanged支持代码。我通常根据上下文使用两种方法。

以下是使用抽象类时的结果:

public abstract class PropertyNotifier: INotifyPropertyChanged
{
    #region INotifyPropertyChanged support code
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    protected bool SetProperty<T>(ref T storage, T value,
        [CallerMemberName] String propertyName = null)
    {
        bool result = false;

        if (!Object.Equals(storage, value))
        {
            storage = value;
            OnPropertyChanged(propertyName);
            result = true;
        }
        return result;
    }
    #endregion INotifyPropertyChanged support code
}

在使用中:

public interface IPerson
{
    String FirstName { get; set }
    String LastName { get; set }
}

public class Person : PropertyNotifier, IPerson
{
    private string _FirstName;
    private string _LastName;

    public String FirstName
    {
        get => _FirstName;
        set => SetProperty(ref _FirstName, value);
    }

    public String LastName
    {
        get => _LastName;
        set => SetProperty(ref _LastName, value);
    }
}

希望这能帮到你!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接