8

阅读本文后,我的PersonViewModel课堂上有以下代码:

public Jurisdiction CountryResidence
{
    get
    {
        return Model.CountryResidence;
    }
    set
    {
        if (Model.CountryResidence == value)
            return;
        else
        {
            Model.CountryResidence = value;
            base.OnPropertyChanged("CountryResidence");
        }
    }
}

public Jurisdiction CountryBirth
{
    get
    {
        return Model.CountryBirth;
    }
    set
    {
        if (Model.CountryBirth == value)
            return;
        else
        {
            Model.CountryBirth = value;
            base.OnPropertyChanged("CountryBirth");
        }
    }
}

我也有CountryDomiciled,CountryPassportLegalJurisdiction, 都具有相同的格式。同样,我有很多String属性,它们都共享它们的格式。

这会导致大量相同的代码!但是,我不知道如何使它更简洁。

有没有更好的方法来生成这些保持强类型的属性?

4

8 回答 8

11

我使用 Visual Studio 的片段,它为我生成带有支持存储和事件引发的属性。只需使用名称propchanged(或其他名称,如果您愿意)和以下内容创建 xml 文件:

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <Title>propchanged</Title>
      <Shortcut>propchanged</Shortcut>
      <Description>Code snippet for property (with call to OnPropertyChanged) and backing field</Description>
      <Author>lazyberezovsky</Author>
      <SnippetTypes>
        <SnippetType>Expansion</SnippetType>
      </SnippetTypes>
    </Header>
    <Snippet>
      <Declarations>
        <Literal>
          <ID>type</ID>
          <ToolTip>Property type</ToolTip>
          <Default>string</Default>
        </Literal>
        <Literal>
          <ID>property</ID>
          <ToolTip>Property name</ToolTip>
          <Default>MyProperty</Default>
        </Literal>
        <Literal>
          <ID>field</ID>
          <ToolTip>The variable backing this property</ToolTip>
          <Default>myVar</Default>
        </Literal>
      </Declarations>
      <Code Language="csharp">
        <![CDATA[private $type$ $field$;

    public $type$ $property$
    {
        get { return $field$;}
        set 
    {
       if ($field$ == value)
          return;

       $field$ = value;
       OnPropertyChanged("$property$");
    }
    }
    $end$]]>
      </Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>

并将其放入文件夹C:\Users\YourName\Documents\Visual Studio 2010\Code Snippets\Visual C#\My Code Snippets\

接下来,我从一些基本 ViewModel 继承我的 ViewModel,它实现了接口并提供了用于生成“PropertyChanged”事件的INotifyPropertyChanged受保护方法。OnPropertyChanged

public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName);
    }
}

现在,当您propchanged在 Visual Studio 中键入时,它会询问您属性类型和名称,并为您生成代码。

public class PersonViewModel : ViewModel
{
    // type here 'propchanged' (or other shortcut assigned for snippet)
}

更新:

另一种选择是通过PostSharp等 AOP 框架生成代码。在这种情况下,代码将在编译期间生成和添加(因此您的类将保持干净)。下面是实现通过 PostSharp 属性更改的 INotifyProperty 的示例:

[Notify]
public class PersonViewModel
{
    public Jurisdiction CountryResidence { get; set; }
    public Jurisdiction CountryBirth { get; set; }
}
于 2012-04-16T13:22:55.877 回答
4

更新: NotifyPropertyWeaver 已被弃用,并作为PropertyChanged.Fody继续其生命。这是解决此问题的绝对超酷方法。这是一个仅编译时的解决方案。

这里有一些可以为您节省代码和麻烦的东西:NotifyPropertyWeaver

使用上述内容,您可以在没有任何INotifyPropertyChanged相关代码的情况下实现您的属性,并且构建步骤将为您处理接线。

这是一个简单的项目 include(也可通过 Nuget 获得),它将自动将OnPropertyChanged回调注入到任何实现INotifyPropertyChanged. 它在编译时执行(因此没有运行时命中),并且您的代码只能具有自动实现的属性(在您的情况下,您使用单独的支持对象除外)。

它甚至包括值相等检查,因此它涵盖了完整的逻辑。我没有使用手动实现的属性对其进行测试,但值得一试。

编辑:我现在已经对其进行了测试,它工作正常:手动实现的属性将“正常工作”。

于 2012-04-16T13:32:30.607 回答
2

不是您真正想要的,但顺便说一句,您可以通过反转逻辑测试来为每个属性节省两行:

public Jurisdiction CountryResidence
{
    get
    {
        return Model.CountryResidence;
    }
    set
    {
        if (Model.CountryResidence != value)
        {
            Model.CountryResidence = value;
            base.OnPropertyChanged("CountryResidence");
        }
    }
}
于 2012-04-16T13:14:47.420 回答
1

我不知道任何内置方式,但您可以录制一个将为您生成代码的宏。我发现最简单的方法是开始录制宏,然后仅使用键盘创建您想要的任何内容(您可以在此处找到方便的键盘快捷键列表)

例如,我有一个生成公共版本的属性,所以我只需键入private string _someValue;并点击我的宏,它将生成公共属性以及属性更改通知。

也就是说,请记住,为了简单和方便,在 MVVM 中将整个模型暴露给视图是完全有效的。因此,您不必单独公开模型的每个属性,而只需为模型对象创建一个属性。

public Model SomeModel
{
    get
    {
        return Model;
    }
    set
    {
        if (Model == value)
            return;
        else
        {
            Model= value;
            base.OnPropertyChanged("SomeModel");
        }
    }
}

并像这样绑定到模型的属性:

<TextBox Text="{Binding SomeModel.SomeProperty}" />
于 2012-04-16T13:14:44.557 回答
1

请尝试使用XAML PowerToys快速生成 ViewModel;

于 2012-04-16T13:19:07.123 回答
1

要获得对属性名称的“强类型”引用(启用重构所需的),您可以使用表达式。将以下方法放在视图模型的基类中:

protected void RaisePropertyChanged<T>(Expression<Func<T>> property)
{
    var handler = PropertyChanged;
    if (handler == null) return;

    var propertyName = NotifyPropertyChangedHelper.GetPropertyNameFromExpression(property);
    handler(sender, new PropertyChangedEventArgs(propertyName));
}

在您的视图模型中,您将能够执行以下操作:

public Jurisdiction CountryResidence
{
    get { return Model.CountryResidence; }
    set 
    {         
        if (Model.CountryResidence == value)
            return;

        Model.CountryResidence = value;
        OnPropertyChanged(() => CountryResidence);
    }
}

现在,由于引用与实际属性相反,因此会自动选择重构属性名称。

这解决了硬编码属性名称的痛点,尽管它仍然需要 4-5 行样板代码。notifypropertyweaverPostSharp等面向方面的方法确实消除了视图模型中的所有手动编码。

于 2012-04-16T14:50:38.333 回答
0

您没有尝试过使用 Visual Studio 代码片段,例如proppropfull吗?只需键入它并按两次 Tab 键。问题是propfull代码片段不会引发PropertyChanged事件。

但实际上应该有此类任务的第三方片段。这是我发现的:带有 INPC 的属性片段

于 2012-04-16T13:11:36.147 回答
0

为此,我编写了一个小型 T4 模板。

http://software.the-architect.info/2014/08/12/a-quick-way-to-generate-view-model-properties-in-wpf

于 2014-08-12T12:49:39.333 回答