写MVVM样板代码的更好方法?

7

最近我发现自己写了很多样板式的MVVM代码,想知道是否有一种高端的方法可以避免重复编写?我已经使用了一个实现了INotifyPropertyChanged接口的ViewModelBase类,但这并不能解决编写所有访问器代码的问题。也许可以通过编写一个自定义属性来解决,或者通过模板系统来解决?

public MyClass : ViewModelBase
{
    private int someVariable;

    public int SomeVariable
    {
        get
        {
            return this.someVariable;
        }

        set
        {
            this.someVariable = value;
            this.NotifyPropertyChanged("SomeVariable");
        }
    }
}

2
这个答案应该会有帮助:http://stackoverflow.com/a/16619858/389966。 - Adi Lester
1
小改进:去掉 this. - 根据 ReSharper,这是多余的。小改进 #2:使用基于表达式的强类型 NotifyPropertyChange() 并避免魔术字符串。 - Federico Berasategui
2
你也可以查看这个答案,特别是PropertyChanged.Fody,它可以自动实现INPC。 - Viv
如果你还不知道,MVVM Light 可以帮助你解决这个问题。 - JMK
1
@Chris 看起来 Fody 的属性更改插件(https://github.com/Fody/PropertyChanged)支持 .Net 4。它似乎也解决了属性之间的依赖关系,但不确定是否可以按属性禁用。 - rikkit
显示剩余4条评论
5个回答

5

我有一个片段,用于创建我的视图模型属性。这个特定的片段使用了其他评论者指出的Expression<Func<T>>符号。

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <SnippetTypes>
        <SnippetType>Expansion</SnippetType>
      </SnippetTypes>
      <Title>View Model Property</Title>
      <Description>
          Declares a property and member suitable for Viewmodel implementation.
      </Description>
      <HelpUrl>
      </HelpUrl>
      <Shortcut>propvm</Shortcut>
    </Header>
    <Snippet>
      <Declarations>
        <Literal Editable="true">
          <ID>propname</ID>
          <ToolTip>Property Name</ToolTip>
          <Default>Name</Default>
          <Function>
          </Function>
        </Literal>
        <Literal Editable="true">
          <ID>type</ID>
          <ToolTip>Property type.</ToolTip>
          <Default>Type</Default>
          <Function>
          </Function>
        </Literal>
        <Literal Editable="true">
          <ID>init</ID>
          <ToolTip>Member initialisation</ToolTip>
          <Default>null</Default>
          <Function>
          </Function>
        </Literal>
      </Declarations>
      <Code Language="csharp" Kind="type decl"><![CDATA[public $type$ $propname$
{
    get { return m_$propname$; }
    set 
    { 
        m_$propname$ = value;
        base.OnPropertyChanged(() => $propname$);
    }
} $type$ m_$propname$ = default($type$);$end$]]></Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>

请注意对base.PropertyChanged()的调用。我有一个ViewModelBase类来为我完成属性通知和验证的繁重工作。
使用方法如下:
  1. 输入 propvm
  2. 按两次TAB
  3. 填写突出显示的字段并按Tab键翻转到下一个字段!
请参见:演练:创建代码段

我同意这个观点。我过去三年一直在使用类似的代码片段。 - Eugene S.

1

面向切面编程(AOP)是减少样板代码的一种方式。一个广为人知的框架是PostSharp,也有免费的Express版本。
您可以使用属性(直接在类上或作为多播应用于满足特定条件的所有代码点)来标记应该集成代码的位置,PostSharp会在构建期间织入实现。您可以在此处找到INotifyPropertyChanged实现的示例。
基于AOP的方法(无论使用哪个框架)的优点是您可以随后更改实现,并且这些更改会反映在现有代码库中。还可以将方面应用于大量已经存在的类。


0
首先,如已提到的,使用片段来为您创建代码。 然后有一些库可以帮助您,或使用AOP。
以下是我在应用程序中使用了一段时间的内容,其中简单控件的原始UI性能无关紧要:带有Dictionary<string,object>的辅助类用于保存实际属性后端,以及获取/设置任何类型属性的方法,采用表达式作为参数,以避免使用字符串字面值。 当使用此功能时,属性可转换为
public int SomeProperty
{
  get { return properties.Get( model => model.SomeProperty ); }
  set { properties.Set( model => model.SomeProperty, value ); }
}

此外,当值真正更改时,Set 调用返回 true,因为这通常很有用。

这里是一些代码,带有通常的“自行承担风险”等警告。您只需要一个NotifyPropertyChangedHelper实现,但可以很容易地找到它(例如,在网络上搜索“propertychanged helper”,我很确定它也发布在SO上)

public class NotifyPropertyChangedMap<T> where T : INotifyPropertyChanged
{
  #region Fields
  private readonly T propertyContainer;
  private readonly Dictionary<string, object> properties;
  #endregion

  #region Constructors
  public NotifyPropertyChangedMap( T propertyContainer )
  {
    Contract.Requires<ArgumentNullException>( propertyContainer != null, "propertyContainer" );

    this.propertyContainer = propertyContainer;
    this.properties = new Dictionary<string, object>();
  }
  #endregion

  #region Get and Set
  public Property Get<Property>( Expression<Func<T, Property>> expression )
  {
    var propName = NotifyPropertyChangedHelper.GetPropertyName( expression );
    if( !properties.ContainsKey( propName ) )
      properties.Add( propName, GetDefault<Property>() );
    return (Property) properties[ propName ];
  }

  public bool Set<Property>( Expression<Func<T, Property>> expression, Property newValue )
  {
    var propName = NotifyPropertyChangedHelper.GetPropertyName( expression );
    if( !properties.ContainsKey( propName ) )
    {
      properties.Add( propName, newValue );
      propertyContainer.RaisePropertyChangedEvent( propName );
    }
    else
    {
      if( EqualityComparer<Property>.Default.Equals( (Property) properties[ propName ], newValue ) )
        return false;
      properties[ propName ] = newValue;
      propertyContainer.RaisePropertyChangedEvent( propName );
    }
    return true;
  }
  #endregion

  #region Implementation
  private static Property GetDefault<Property>()
  {
    var type = typeof( Property );
    return (Property) ( type.IsValueType ? Activator.CreateInstance( type ) : null );
  }
  #endregion
}

0

哇...评论区回答的很多,但是实际上并没有太多的答案。作为CallerMemberNameAttribute属性的替代选择,如何考虑使用Visual Studio宏?我有很多这样的宏,可以在点击鼠标按钮时完全实现我所有的接口(包括自定义和.NET)。

使用宏的缺点:

你需要使用Visual Basic编写它们
编写长宏可能需要一些时间
它们可能像任何代码一样包含错误

使用宏的优点:

您可以“记录”简单的宏在您输入时
您可以构建能够与当前上下文一起工作的复杂宏
它们可以在点击鼠标按钮时写入数千个字

例如,我可以创建一个类文件,仅定义类名、基类和/或接口。在声明私有成员变量之后,我可以运行自定义宏,它将读取变量的名称和类型,并生成构造函数、属性以及所使用的基类和/或接口所需的所有方法。然而,这个特定的宏几乎有600行代码。

0

使用类似“mvvmprop”的代码片段。已经有很多这样的代码片段可供使用,包括MVVM Lite的实现。


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