基本的ViewModel类生成器
消除为您的 WPF 项目生成模型和视图模型类的繁琐工作,并在此过程中节省大量时间。
引言
(如果您想要一些真正令人印象深刻且功能强大得多的代码,请查看 Sacha Barber 的 Cinch 系列文章。)
最近,我因为没有所需的 Microsoft 认证(MCSA),而无法继续开发访问本地数据库的应用程序。别提我直到被录用后才被告知需要认证,也别提我所做的工作几乎是关键任务,更别提我已经在数据库中工作了六个月没有造成任何损害。不,先生,他们武断而毫无意义的认证要求优先于任务本身。
结果,我在这里已经坐了两个星期,无所事事,观看了无数小时的 YouTube 培训视频(我不想看),只为了参加三项必要的考试,以获得 MCSA 认证(我不想获得,请不要说这“对我的简历有好处”——我已经从事这项工作 35 年了,我看不到认证的实际价值)。嗯,这似乎与我们在这里的原因有很大的离题。
因为我有很多空闲时间,而且我对学习 SQL Server 感到非常厌倦,所以我决定编写这段代码。和我的大多数文章一样,这里展示的代码是因为它对我有用,并满足了我的需求而开发的。既然这里所有人都是程序员,我先声明——如果这段代码有点符合你的需求,但又不完全符合,请随意修改。如果你想扩展它,使其更通用,和/或将其制作成 Visual Studio 插件(这将非常有用),请务必这样做,但请以文章的形式在 CodeProject 上分享你的代码。
最后,请务必阅读兴趣点部分。它包含重要的信息、警告,以及对不熟悉“不法程序员”学派的人的直接暴力威胁。
背景
此代码是在考虑 WPF MVVM 范式的情况下开发的。老实说,开发模型和视图模型对象是繁琐的,并且充满了危险(拼写错误、遗漏的字段和属性等)。
我的目标是消除大部分的繁琐工作。为此,我从如何生成模型开始。幸运的是,Visual Studio(v2011 及更高版本)有一个方便的功能,叫做“编辑 | 粘贴特殊”。
编辑 | 粘贴特殊
此主菜单项允许您根据 Windows 剪贴板上当前保存的 XML 或 JSON 数据创建类。它将为复制的 XML/JSON 中的每个项目创建类,包括项目数组以及这些项目中的字段和属性。我的大部分代码都是从头开始的,所以我甚至没有 XML 数据可以开始,这意味着我必须自己创建架构。在这个例子中,我使用了以下 XML 数据
<?xml version="1.0" encoding="utf-8" ?>
<root>
<people>
<person name="Kathy" ismale="false" mydate="2014-01-22" mybyte="0" myint="-2147483648"
myuint="2147483647" mylong="-9223372036854775808" mydecimal="1.0"
myfloat="3.402823e38" mydouble="1.7976931348623157E+308" />
<person name="John" ismale="true" mydate="2014-02-05" mybyte="0" myint="-2147483648"
myuint="2147483647" mylong="-9223372036854775808" mydecimal="1.0"
myfloat="3.402823e38" mydouble="1.7976931348623157E+308" />
</people>
</root>
你们中的大多数人可能已经注意到了我使用的值。这不是任意选择的值——它对于强制“粘贴特殊”功能在生成的模型类中提供所需的类型是必需的。我没有包含几种类型,因为在确定所示字段的正确值后,它现在永久地植入我的大脑中,我必须这样做。规则很简单
- 布尔值 - 只需指定 true 或 false。
- DateTime - 经过有限时间的试验,我发现使用 yyyy-MM-dd 格式会导致“粘贴特殊”识别为 DateTime。其他格式可能也有效,但这种格式所需的输入量最少。
- 字符串 - 没有规则。只需输入任何旧文本值,它就会知道该值是字符串。“text”这个词会非常合适。
- 有符号整数 - 使用最小可能值。这会告诉“粘贴特殊”您需要一个有符号整数,并且您插入的值表示该整数的大小(8、16、32 或 64 位)。
- 无符号整数 - 使用最大可能值。与有符号整数一样,指定的值表示所需的整数大小。(这包括 byte 和 sbyte 类型)。
- 小数 - 这是一个奇怪的类型,因为尽管它是一个整数类型,但如果使用两个数字并用小数点分隔,它的值就能被“粘贴特殊”理解。因此,“0.0”是生成小数类型的合适方法。
- 浮点数 - 使用所需浮点类型的最大可能值。
注意:我无法找到让“粘贴特殊”将值识别为 TimeSpan 的方法。“00:00:00.000”被视为 DateTime,而“00:00”被视为字符串。
创建类
- 0) 在您的项目中创建一个新的类文件,并删除其内容。
- 1) 复制所需的 XML 数据——对于本例,只需复制整个 People 元素。
- 2) 选择您的(空)类文件
- 3) 在 VS 菜单中,点击 编辑 | 粘贴特殊 | 将 XML 粘贴为类
您应该会得到类似以下的结果
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
namespace WpfApplication1.Model
{
/// <remarks />
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class People
{
private PeoplePerson[] personField;
/// <remarks />
[System.Xml.Serialization.XmlElementAttribute("Person")]
public PeoplePerson[] Person
{
get
{
return this.personField;
}
set
{
this.personField = value;
}
}
}
/// <remarks />
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class PeoplePerson
{
private string nameField;
private bool isMaleField;
private System.DateTime myDateField;
private byte myByteField;
private int myIntField;
private uint myUintField;
private long myLongField;
private decimal myDecimalField;
private float myFloatField;
private double myDoubleField;
/// <remarks />
[System.Xml.Serialization.XmlAttributeAttribute()]
public string Name
{
get
{
return this.nameField;
}
set
{
this.nameField = value;
}
}
/// <remarks />
[System.Xml.Serialization.XmlAttributeAttribute()]
public bool IsMale
{
get
{
return this.isMaleField;
}
set
{
this.isMaleField = value;
}
}
/// <remarks />
[System.Xml.Serialization.XmlAttributeAttribute(DataType = "date")]
public System.DateTime MyDate
{
get
{
return this.myDateField;
}
set
{
this.myDateField = value;
}
}
/// <remarks />
[System.Xml.Serialization.XmlAttributeAttribute()]
public byte MyByte
{
get
{
return this.myByteField;
}
set
{
this.myByteField = value;
}
}
/// <remarks />
[System.Xml.Serialization.XmlAttributeAttribute()]
public int MyInt
{
get
{
return this.myIntField;
}
set
{
this.myIntField = value;
}
}
/// <remarks />
[System.Xml.Serialization.XmlAttributeAttribute()]
public uint MyUint
{
get
{
return this.myUintField;
}
set
{
this.myUintField = value;
}
}
/// <remarks />
[System.Xml.Serialization.XmlAttributeAttribute()]
public long MyLong
{
get
{
return this.myLongField;
}
set
{
this.myLongField = value;
}
}
/// <remarks />
[System.Xml.Serialization.XmlAttributeAttribute()]
public decimal MyDecimal
{
get
{
return this.myDecimalField;
}
set
{
this.myDecimalField = value;
}
}
/// <remarks />
[System.Xml.Serialization.XmlAttributeAttribute()]
public float MyFloat
{
get
{
return this.myFloatField;
}
set
{
this.myFloatField = value;
}
}
/// <remarks />
[System.Xml.Serialization.XmlAttributeAttribute()]
public double MyDouble
{
get
{
return this.myDoubleField;
}
set
{
this.myDoubleField = value;
}
}
}
}
就我个人而言,我不喜欢他们使用的字段名,所以我从中删除了“Field”,但您可以按照您内心的声音要求进行。完成此步骤后,我们将继续讨论我编写的代码。
代码 - ViewModelGenerator 类
ViewModelGenerator 类使用反射和大量关于您希望在生成代码中看到什么内容的假设来完成所有工作。因为我绝对不可能预见到每个程序员的每个需求,所以我将该类限制为足够完整,以消除编写视图模型类的许多琐碎细节——为了我的目的。同样,该类生成以 WPF 为中心的代码,您应该准备好修改生成的代码并微调该类以满足您的需求。
每个类源文件都由离散的部分组成
- 使用语句
- 命名空间声明
- 类声明
- 类字段
- 类属性
- 构造函数
ViewModelGenerator 类生成上述所有部分,并将它们存储在 StringBuilder 对象中
。具体如下
StringBuilder 对象
以下代码展示了定义的 StringBuilder 对象。其中可能突出(因为它们不包含在上面的列表中)的是 CodeNotify 和 CodeDataError 字段。
由于这是一个 WPF 应用程序,并且这是一个视图模型类,我预料到需要继承 INotifyPropertyChanged 和/或 IDataErrorInfo。这两个接口在几乎每个 WPF 应用程序中都扮演着重要的角色。这些对象将在稍后讨论。
public class ViewModelGenerator
{
private StringBuilder CodeUsings = new StringBuilder();
private StringBuilder CodeNameSpace = new StringBuilder();
private StringBuilder CodeClassDecl = new StringBuilder();
private StringBuilder CodeNotify = new StringBuilder();
private StringBuilder CodeDataError = new StringBuilder();
private StringBuilder CodeFields = new StringBuilder();
private StringBuilder CodeProperties = new StringBuilder();
private StringBuilder CodeConstructor = new StringBuilder();
ViewModelCode 属性
此代码返回合并的 StringBuilder 对象,并表示视图模型类定义的全部内容。创建此类的代码可以通过此方法检索生成的代码,但实际上没有必要,正如您稍后将看到的。
public StringBuilder ViewModelCode
{
get
{
StringBuilder text = new StringBuilder();
text.Append(this.CodeUsings);
text.AppendLine();
text.Append(this.CodeNameSpace);
text.AppendLine("{");
text.Append(this.CodeClassDecl);
text.AppendLine("\t{");
if (this.CodeNotify.Length > 0)
{
text.Append(this.CodeNotify);
text.AppendLine();
}
if (this.CodeDataError.Length > 0)
{
text.Append(this.CodeDataError);
text.AppendLine();
}
text.Append(this.CodeFields);
text.AppendLine();
text.Append(this.CodeProperties);
text.AppendLine();
text.Append(this.CodeConstructor);
text.AppendLine("\t}");
text.AppendLine("}");
return text;
}
}
构造函数
构造函数根据传入的参数实际执行填充各种 StringBuilder 对象的工作。只有组件对象被填充。
public ViewModelGenerator(Type objType, string namespaceName, string vmClassName,
string inheritChain, string[] moreUsings,
bool iNotify, bool iDataError)
{
this.CreateNamespace(namespaceName);
this.CreateUsings(moreUsings);
this.CreateClassDeclaration(vmClassName, inheritChain, iNotify, iDataError);
this.CreateProperties(objType);
this.CreateConstructors(vmClassName, objType);
if (iNotify)
{
this.CreateINotifyPropertyChanged();
}
if (iDataError)
{
this.CreateIDataErrorInfo();
}
}
参数
- Type objType - 用于生成相应视图模型类的模型对象
- string namespaceName - 生成类使用的命名空间
- string vmClassName - 生成类的名称
- string inheritanceChain - 此值允许程序员指定一个不包括已支持的 INotifyPropertyChanged 和 IDataErrorInfo 接口的继承链。在我的例子中,我所有的视图模型类都继承自一个我称之为 Notifier 的类,该类本身继承自 INotifyPropertyChanged 和 IDataErrorInfo。我相信大多数经验丰富的 WPF 程序员都做类似的事情。
- string[] moreUsings - 除了已包含在生成类中的命名空间之外,还要使用的命名空间数组。您可以在下一节中查看标准命名空间列表。
- bool iNotify - 指示是否继承并实现与 INotifyPropertyChanged 接口相关的代码。
- bool iDataError - 指示是否继承并实现与 IDataErrorInfo 接口相关的代码。
CreateUsings 方法
此方法填充 using 组件。出于代码考虑,而不是其他原因,每个项目都是逐一添加的。(这种模式在所有执行类似功能的其他方法中重复。)
protected virtual void CreateUsings(string[] moreUsings)
{
this.CodeUsings.Clear();
this.CodeUsings.AppendLine("using System;");
this.CodeUsings.AppendLine("using System.Collections.Generic;");
this.CodeUsings.AppendLine("using System.Collections.ObjectModel;");
this.CodeUsings.AppendLine("using System.ComponentModel;");
this.CodeUsings.AppendLine("using System.IO;");
this.CodeUsings.AppendLine("using System.Linq;");
this.CodeUsings.AppendLine("using System.Text;");
if (moreUsings != null)
{
foreach(string item in moreUsings)
{
this.CodeUsings.AppendFormat("using {0};\r\n", item);
}
}
}
CreateNamespace 方法
此方法填充命名空间组件。我知道,它只有两行,但我总是对将功能抽象成方法感兴趣,因为它使调用代码看起来更有条理。
protected virtual void CreateNamespace(string namespaceName)
{
this.CodeNameSpace.Clear();
this.CodeNameSpace.AppendFormat("namespace {0}\r\n", namespaceName);
}
CreateProperties 方法
此方法填充属性组件,并且可能是整个类中最有趣的方法。它使用反射来检查模型对象信息,并返回属性名称和类型。有了这些信息,就会为目标视图模型类生成字段和属性。
protected virtual void CreateProperties(Type objType)
{
this.CodeProperties.Clear();
this.CodeProperties.AppendFormat("\t\tpublic {0} Model {{ get; set; }}\r\n\r\n", objType.Name);
PropertyInfo[] properties= objType.GetProperties();
foreach (PropertyInfo property in properties)
{
string type = this.NormalizeType(property.PropertyType.Name);
string name = property.Name;
string field = Char.ToLowerInvariant(name[0]) + name.Substring(1);
this.CodeFields.AppendFormat("\t\tprivate {0} {1};\r\n", type, field);
this.MakeProperty(type, name, field);
}
}
NormalizeType 方法
此方法由 CreateProperties 为每个发现的属性类型调用。此方法的原因是尽可能生成内部类型定义,因为反射将返回 .Net 类名,而我们不一定想使用它们(不是说它会造成任何损害)。
protected virtual string NormalizeType(string type)
{
switch (type)
{
case "Boolean" : return "bool";
case "Int16" : return "short";
case "Int32" : return "int";
case "Int64" : return "long";
case "UInt16" : return "ushort";
case "UInt32" : return "uint";
case "UInt64" : return "ulong";
case "Single" : return "float";
case "Byte" :
case "Char" :
case "Decimal" :
case "Double" :
case "Enum" :
case "SByte" :
case "String" : return type.ToLower();
default: return type;
}
}
MakeProperty 方法
此方法实际上为视图模型创建属性代码。此时,值得注意的是,生成的代码包含制表符以美化代码。
生成的代码包括 get 方法(用于返回生成的字段)和 set 方法,后者包括调用 NotifyPropertyChanged。这可能是您需要更改以匹配您自己的代码(或基类,如果使用)的地方。
protected virtual void MakeProperty(string type, string name, string field)
{
this.CodeProperties.Clear();
field = "this." + field;
this.CodeProperties.AppendFormat("\t\tpublic {0} {1}\r\n", type, name);
this.CodeProperties.AppendLine ("\t\t{");
this.CodeProperties.AppendFormat("\t\t\tget {{ return {0}; }}\r\n", field);
this.CodeProperties.AppendLine ("\t\t\tset");
this.CodeProperties.AppendLine ("\t\t\t{");
this.CodeProperties.AppendFormat("\t\t\t\tif (value != {0})\r\n", field);
this.CodeProperties.AppendLine ("\t\t\t\t{");
this.CodeProperties.AppendFormat("\t\t\t\t\t{0} = value;\r\n", field);
this.CodeProperties.AppendLine ("\t\t\t\t\tthis.NotifyPropertyChanged();");
this.CodeProperties.AppendLine ("\t\t\t\t}");
this.CodeProperties.AppendLine ("\t\t\t}");
this.CodeProperties.AppendLine ("\t\t}");
this.CodeProperties.AppendLine ();
}
CreateINotifyPropertyChanged 方法
此方法为 INotifyPropertyChanged 接口生成固定的行为。如果您在 MakeProperty 方法中更改了 Notify 方法的名称,请务必在此处也进行更改。
可能的增强——将通知方法名称设为该类中的常量,以便您只需在一个地方更改它。
protected virtual void CreateINotifyPropertyChanged()
{
this.CodeNotify.Clear();
this.CodeNotify.AppendLine("\t\t#region INotifyPropertyChanged");
this.CodeNotify.AppendLine("\t");
this.CodeNotify.AppendLine("\t\t/// <summary>");
this.CodeNotify.AppendLine("\t\t/// Occurs when a property value changes.");
this.CodeNotify.AppendLine("\t\t/// </summary>");
this.CodeNotify.AppendLine("\t\tpublic event PropertyChangedEventHandler PropertyChanged;");
this.CodeNotify.AppendLine("\t");
this.CodeNotify.AppendLine("\t\t/// <summary>");
this.CodeNotify.AppendLine("\t\t/// Notifies that the property changed, and sets IsModified to true.");
this.CodeNotify.AppendLine("\t\t/// </summary>");
this.CodeNotify.AppendLine("\t\t/// <param name=\"propertyName\">Name of the property.</param>");
this.CodeNotify.AppendLine("\t\tprotected void NotifyPropertyChanged([CallerMemberName] String propertyName = \"\")");
this.CodeNotify.AppendLine("\t\t{");
this.CodeNotify.AppendLine("\t\t\tif (this.PropertyChanged != null)");
this.CodeNotify.AppendLine("\t\t\t{");
this.CodeNotify.AppendLine("\t\t\t\tthis.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));");
this.CodeNotify.AppendLine("\t\t\t\tif (propertyName != \"IsModified\")");
this.CodeNotify.AppendLine("\t\t\t\t{");
this.CodeNotify.AppendLine("\t\t\t\t\tthis.IsModified = true;");
this.CodeNotify.AppendLine("\t\t\t\t}");
this.CodeNotify.AppendLine("\t\t\t}");
this.CodeNotify.AppendLine("\t\t}");
this.CodeNotify.AppendLine("\t");
this.CodeNotify.AppendLine("\t}");
this.CodeNotify.AppendLine("\t\t#endregion INotifyPropertyChanged");
}
CreateIDataErrorInfo 方法
此方法为 IDataErrorInfo 接口生成预设行为。如果您的编码风格/要求与我的不同,这可能是您需要修改的地方。
protected virtual void CreateIDataErrorInfo()
{
this.CodeDataError.Clear();
this.CodeDataError.AppendLine("\t\t#region IDataErrorInfo Code");
this.CodeDataError.AppendLine("\t\t");
this.CodeDataError.AppendLine("\t\t/// <summary>");
this.CodeDataError.AppendLine("\t\t/// Gets an error message indicating what is wrong with this object.");
this.CodeDataError.AppendLine("\t\t/// </summary>");
this.CodeDataError.AppendLine("\t\tpublic string Error");
this.CodeDataError.AppendLine("\t\t{");
this.CodeDataError.AppendLine("\t\t\tget { return \"Error\"; } }");
this.CodeDataError.AppendLine("\t\t");
this.CodeDataError.AppendLine("\t\t/// <summary>");
this.CodeDataError.AppendLine("\t\t/// Gets the error message for the property with the given name.");
this.CodeDataError.AppendLine("\t\t/// </summary>");
this.CodeDataError.AppendLine("\t\t/// <param name=\"columnName\">Name of the column.</param>");
this.CodeDataError.AppendLine("\t\t/// <returns>The generated error message (if any).</returns>");
this.CodeDataError.AppendLine("\t\tpublic string this[string columnName]");
this.CodeDataError.AppendLine("\t\t{");
this.CodeDataError.AppendLine("\t\t\tget { return Validate(columnName); }");
this.CodeDataError.AppendLine("\t\t}");
this.CodeDataError.AppendLine("\t\t");
this.CodeDataError.AppendLine("\t\t/// <summary>");
this.CodeDataError.AppendLine("\t\t/// Validates the specified propery.");
this.CodeDataError.AppendLine("\t\t/// </summary>");
this.CodeDataError.AppendLine("\t\t/// <param name=\"properyName\">Name of the propery.</param>");
this.CodeDataError.AppendLine("\t\t/// <returns>Empty string if valid, otherwise, appropriate error message.</returns>");
this.CodeDataError.AppendLine("\t\tprotected virtual string Validate(string properyName)");
this.CodeDataError.AppendLine("\t\t{");
this.CodeDataError.AppendLine("\t\t\t//Retun error message if there is error, otherwise return empty string");
this.CodeDataError.AppendLine("\t\t\tstring validationMsg = string.Empty;");
this.CodeDataError.AppendLine("\t\t\treturn validationMsg;");
this.CodeDataError.AppendLine("\t\t}");
this.CodeDataError.AppendLine("\t\t");
this.CodeDataError.AppendLine("\t\t#endregion IDataErrorInfo Code");
}
CreateConstructors 方法
此方法创建一个默认构造函数和一个接受模型对象作为参数的构造函数。使用反射,非默认构造函数将填充代码,该代码使用视图模型中的值初始化视图模型对象。
protected virtual void CreateConstructors(string vmClassName, Type objType)
{
this.CodeConstructor.Clear();
this.CodeConstructor.AppendFormat("\t\tpublic {0}()\r\n", vmClassName);
this.CodeConstructor.AppendLine("\t\t{");
this.CodeConstructor.AppendLine("\t\t\t// TO-DO: Add initialization code here");
this.CodeConstructor.AppendLine("\t\t}");
this.CodeConstructor.AppendLine();
this.CodeConstructor.AppendFormat("\t\tpublic {0}({1} model)\r\n", vmClassName, objType.Name);
this.CodeConstructor.AppendLine("\t\t{");
this.CodeConstructor.AppendFormat("\t\t\tthis.Model = model;\r\n");
PropertyInfo[] properties= objType.GetProperties();
foreach (PropertyInfo property in properties)
{
this.CodeConstructor.AppendFormat("\t\t\tthis.{0} = model.{0};\r\n", property.Name);
}
this.CodeConstructor.AppendLine("\t\t}");
}
CreateClassDeclaration 方法
此方法填充类声明组件,根据需要添加继承链信息。
protected virtual void CreateClassDeclaration(string vmClassName, string inheritChain, bool iNotify, bool iDataError)
{
this.CodeClassDecl.Clear();
this.CodeClassDecl.AppendFormat("\tpublic class {0}", vmClassName);
if (iNotify || iDataError || !string.IsNullOrEmpty(inheritChain) )
{
this.CodeClassDecl.Append(" :");
this.CodeClassDecl.Append(inheritChain);
if (iNotify)
{
this.CodeClassDecl.Append(this.CodeClassDecl.ToString().EndsWith(":") ? " " : ", ");
this.CodeClassDecl.Append("INotifyPropertyChanged");
}
if (iDataError)
{
this.CodeClassDecl.Append(this.CodeClassDecl.ToString().EndsWith(":") ? " " : ", ");
this.CodeClassDecl.Append("IDataErrorInfo");
}
}
this.CodeClassDecl.AppendLine();
}
CopyToClipboard 方法
调用此方法以检索组合组件并将文本放入剪贴板。此时,程序员可以简单地将生成的代码粘贴到现有源文件中,并对粘贴的代码进行任何可能需要的修改(或在此生成器类中进行可能需要的微调)。
public void CopyToClipboard()
{
Clipboard.SetText(this.ViewModelCode.ToString());
}
Using the Code
只需使用适当的参数调用构造函数即可
public MainWindow()
{
InitializeComponent();
ViewModelGenerator generator = new ViewModelGenerator(typeof(PeoplePerson),
string.Format("{0}.ViewModel",
this.GetType().Namespace),
"VMPeoplePerson",
"Notifier",
null,
false,
false);
generator.CopyToClipboard();
}
当粘贴到一个空文件中时,生成的代码如下所示
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
namespace WpfApplication1.ViewModel
{
public class VMPeoplePerson :Notifier
{
private string name;
private bool isMale;
private DateTime myDate;
private byte myByte;
private int myInt;
private uint myUint;
private long myLong;
private decimal myDecimal;
private float myFloat;
private double myDouble;
public PeoplePerson Model { get; set; }
public string Name
{
get { return this.name; }
set
{
if (value != this.name)
{
this.name = value;
this.NotifyPropertyChanged();
}
}
}
public bool IsMale
{
get { return this.isMale; }
set
{
if (value != this.isMale)
{
this.isMale = value;
this.NotifyPropertyChanged();
}
}
}
public DateTime MyDate
{
get { return this.myDate; }
set
{
if (value != this.myDate)
{
this.myDate = value;
this.NotifyPropertyChanged();
}
}
}
public byte MyByte
{
get { return this.myByte; }
set
{
if (value != this.myByte)
{
this.myByte = value;
this.NotifyPropertyChanged();
}
}
}
public int MyInt
{
get { return this.myInt; }
set
{
if (value != this.myInt)
{
this.myInt = value;
this.NotifyPropertyChanged();
}
}
}
public uint MyUint
{
get { return this.myUint; }
set
{
if (value != this.myUint)
{
this.myUint = value;
this.NotifyPropertyChanged();
}
}
}
public long MyLong
{
get { return this.myLong; }
set
{
if (value != this.myLong)
{
this.myLong = value;
this.NotifyPropertyChanged();
}
}
}
public decimal MyDecimal
{
get { return this.myDecimal; }
set
{
if (value != this.myDecimal)
{
this.myDecimal = value;
this.NotifyPropertyChanged();
}
}
}
public float MyFloat
{
get { return this.myFloat; }
set
{
if (value != this.myFloat)
{
this.myFloat = value;
this.NotifyPropertyChanged();
}
}
}
public double MyDouble
{
get { return this.myDouble; }
set
{
if (value != this.myDouble)
{
this.myDouble = value;
this.NotifyPropertyChanged();
}
}
}
public VMPeoplePerson()
{
// TO-DO: Add initialization code here
}
public VMPeoplePerson(PeoplePerson model)
{
this.Model = model;
this.Name = model.Name;
this.IsMale = model.IsMale;
this.MyDate = model.MyDate;
this.MyByte = model.MyByte;
this.MyInt = model.MyInt;
this.MyUint = model.MyUint;
this.MyLong = model.MyLong;
this.MyDecimal = model.MyDecimal;
this.MyFloat = model.MyFloat;
this.MyDouble = model.MyDouble;
}
}
}
如果需要(并且可能是一个明智之举),该类可以转换为静态类。
关注点
了解 Visual Studio 中包含的便利功能非常重要。“粘贴特殊”解释 XML/JSON 中值的方式是一个有趣的陷阱,我很高兴我发现了这个功能的这一方面。
这段代码可以成为一个很棒的 VS 插件,但我还没有足够的动力将其提升到那个级别,至少目前还没有。因此,我将其留作读者的练习。如果您这样做了,请务必发布一篇关于该过程的短文,以便我们其他人也能受益。
我再次提醒您,我编写这段代码是为了满足我自己的需求。如果您需要其他东西,请像一个程序员一样自己修改这段代码。我不会接受“你如何做这个”之类的问题。如果我能在不提问的情况下找到如何做到这一点,您当然也能找到如何让代码按您自己的意愿工作。毕竟,Google 是一个巨大的知识宝库。
示例项目包含一个空文件 - Class2.cs。您可以在运行应用程序后将生成的代码复制到此文件中。示例项目不包含已编译的二进制文件。
如果你想玩转“粘贴特殊”功能,文件 Class1.cs 包含了该功能生成的代码。删除命名空间声明内的所有内容,然后将光标放在那里,再点击菜单中的“粘贴特殊”。玩得开心。
历史
- 2014 年 12 月 16 日 - 首次发布。