Click here to Skip to main content
15,880,967 members
Articles / Desktop Programming / WPF

Rudimentary ViewModel Class Generator

Rate me:
Please Sign up or sign in to vote.
4.77/5 (30 votes)
16 Dec 2014CPOL10 min read 59.9K   21   74
Eliminate the tedium of generating model and viewmodel classes for your WPF projects, and save a butt-load of time in the process.

Introduction

(If you want some truly impressive code that does much, much more, check out Sacha Barber's Cinch series of articles.)

Recently, I was stopped from working on an app that accesses our local database because I don't have the required Microsoft certification (MCSA). Nevermind the fact that I wasn't informed that I even needed the certification until AFTER they hired me, or the fact that the work I was performing was pretty well mission critical, or even the fact that I'd been working in the database for six months without causing any harm. No sir, their arbitrary and pointless certification requirements come first, before the mission itself.

As a result, I've been sitting here for two weeks now slappin' around ol' Mr. One-Eye, watching endless hours of training videos on YouTube (that I don't want to watch) in order to take the three tests necessary to acquire the MCSA certification (that I don't want, and please don't spout off about how it's "good for my resume" - I've been doing this crap for 35 years, and I don't see the TANGIBLE value in certifications). Hmmm, it seems as if this is a significant digression from why we're all here.

Because I had so much free time on my hands, and I was thoroughly bored with learning about SQL server, I decided to write this code. As with most of my articles, the code presented here was developed because it was useful to *me*, and served *my* needs. Since everyone here is a programmer, I'll say this up front - if this code kinda meets your needs, but not exactly, feel free to change it. If you want to extend it, make it more generic, and/or make a Visual Studio add-in out of it (which would be HIGHLY useful), by all means do so, BUT SHARE YOUR CODE ON CODEPROJECT IN THE FORM OF AN ARTICLE.

Finally, make sure you read the Points of Interest section. It contains important info, warnings, and outright threats of violence for those unfamiliar with the school of Outlaw Programmer.

Background

This code was developed with the WPF MVVM paradigm in mind. Honestly, developing the model and viewmodel objects is tedious and frought with danger (typos, forgotten fields and properties, etc).

My goal here was to remove a good amount of that tedium. Toward that end, I started with how to generate the model. Fortunately, Visual Studio (v2011 and later) has a handy feature called "EDIT | Paste Special".

EDIT | Paste Special

This main menu item allows you to create a class from XML or JSON data that is currently held on the Windows clipboard. It will create a class for each item in the copied XML/JSON, including arrays of items, and fields and properties within those items. Most of my code is start-from-scratch, so I don't even have XML data to start from, which means I have to create a schema myself. For this example, I used the following XML data:

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>

Most of you have probably noticed the values I used. This isn't some arbitrary selection of values - it's neccessary to coerce the Paste Special feature into giving you the desired types in the resulting model class. There are several types I didn't include because after determining the correct values for the fields shown, it's now permenantly implanted in my brain that I have to do this. The rules are simple:

  • Booleans - Simply specify true or false.
     
  • DateTime - After playing around with this for a limited ammount of time, I found that using the format yyyy-MM-dd causes Paste Special to see a DateTime. Other formats may work, but this one required the least amount of typying.
     
  • Strings - there are no rules. Just put in any old text value, and it will know the value is a string. The word "text" would be entirely appropriate.
     
  • Signed Integers - Use the MINIMUM possible value. This tells Paste Special you want a signed integer, and the value you insert indicates the size of that integer 8, 16, 32, or 64-bit).
     
  • Unsigned Integers - Use the MAXIMUM possible value. As with signed integers, the value specified indicates the desired integer size. (This includes the byte and sbyte types).
     
  • Decimal - This is a weird one, because even though it's an integer type, its values are understood by Paste Special if you use two digits separated by a decimal point. So, "0.0" is the appropriate merthod for generating a decimal type.
     
  • Floating point - Use the MAXIMUM possible value for the desired floating point type.
     

NOTE: I couldn't find a way to have Paste Special recognize a value as a TimeSpan. Using "00:00:00.000" was seen as a DateTime, and "00:00" was seen as a string.

To create the class:

  • 0) Create a new class file in your project, and delete its contents.
     
  • 1) Copy the desired XML data - for this example, just copy the entire People element.
     
  • 2) Select your (empty) class file
     
  • 3) In the VS menu, click EDIT | Paste Special | Paste XML as Classes
     

You should end up with something like the following:

C#
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;
            }
        }
    }
}

Personally, I don't like the field names they use, so I remove "Field" from them, but you may proceed as the voices in your head demand. After completing this step, we move on to the code I wrote.

The Code - ViewModelGenerator Class

The ViewModelGenerator class performs all of the work, using reflection and and a heavy dose of assumptions regarding what you want to see in your generated code. Because there is absolutely no way I can anticipate every need of every programmer, I limited the class to be as complete as is neccessary to get a lot of the minutia out of the way with regards to writing a viewmodel class - for my purposes. Again, this class generates WPF-centric code, and you should be ready to modify the generated code and fine-tune this class to meet your needs.

Every class source file is comprised of discrete parts:

  • Using statements
     
  • Namespace declaration
     
  • Class declaration
     
  • Class fields
     
  • Class properties
     
  • Constructors
     

The ViewModelGenerator class generates all of the above sections, and stores them in StringBuilder objects

. Here are the specifics:

StringBuilder Objects

The following code illustrates the defined StringBuilder objects. The two that probably stand out (due to their exclusion from the list above) are the CodeNotify and CodeDataError fields.

Since this is a WPF application, and since this is a viewmodel class, I anticipated the need to inherit from INotifyPropertyChanged and/or IDataErrorInfo. These two interfaces play an important part in pretty much every WPF application. These objects will be discussed a little later.

C#
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();

The ViewModelCode Property

This code returns the combined StringBuilder objects, and represents the entire contents of the viewmodel class definition. The code that creates this class can retrieve the generated code via this method, but it's not really necessary, as you will see later.

C#
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;
    }
}

The Constructor

The constructor actually performs the work of populating various StringBuilder objects, based on the parameters passed. Only the component objects are populated.

C#
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();
    }
}

Parameters:

  • Type objType - the model object being used to generate the appropriate viewmodel class
     
  • string namespaceName - the namespace to use for the generated class
     
  • string vmClassName - the name of the generated class
     
  • string inheritanceChain - this value allows the programmer to specify an inheritance chain that DOES NOT include the already supported INotifyPropertyChanged and IDataErrorInfo interfaces. In my case, all of my viewmodel classes inherit from a class I call Notifier, which itself inherits from INotifyPropertyChanged and IDataErrorInfo. I'm sure most of the experienced WPF programmers do something similar.
     
  • string[] moreUsings - an array of namespaces to use IN ADDITION to the ones that are already included in the generated class. You can see that list of standard namespaces in the next section.
     
  • bool iNotify - indicates whether or not to inherit from and implement the code assocated with the INotifyPropertyChanged interface.
     
  • bool iDataError - indicates whether or not to inherit from and implement the code assocated with the IDataErrorInfo interface.
     

CreateUsings Method

This method populates the usings component. Each item is added one at a time, more for code sake that for any other reason. (This pattern is repeated in all other methods that perform similar functionality.)

C#
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 Method

This method populates the namespace component. I know, it's only two lines, but I'm always interested in abstracting functionality into methods, because it makes the calling code seem more organized.

C#
protected virtual void CreateNamespace(string namespaceName)
{
    this.CodeNameSpace.Clear();
    this.CodeNameSpace.AppendFormat("namespace {0}\r\n", namespaceName);
}

CreateProperties Method

This method populates the properties component, and is probably the most interesting method in the entire class. It uses reflection to examine the model objects information, and return property names and types. With this information, the fields and properties are generated for the target viewmodel class.

C#
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 Method

This method is called by CreateProperties for every propertys discovered type. The reason for this method is to generate intrinsic type definitions whenever possible, because reflection will return the .Net class name, and we don't necessarily want to use those (not that it should hurt anything).

C#
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 Method

This method actually creates the property code for the viewmodel. At this point, it's worth noting that the generated code includes tab characters to pretty up the code.

The generated code includes the get method (to return the generate field), and the set method, which includes calling NotifyPropertyChanged. This may be something you need to change to match your own code (or base class, if used).

C#
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 Method

This method generates the canned behavior for the INotifyPropertyChanged interface. If you change the name of the Notify methedo in the MakeProperty method, be sure to change it here as well.

Possible enhancement - Make the notify method name a constant in this class so that you only have to change it in one place.

C#
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 Method

This method generates the canned behavior for the IDataErrorInfo interface. This may be a pointy of modification for you if your coding style/requirements are different from mine.

C#
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 Method

This method creates both a default constructor, and a constructor that accepts the model object as a parameter. Using reflection, the non-default constructor will be populated with code which initializes the viewmodel object with values from the viewmodel.

C#
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 Method

This method populates the class declaration component, adding inheritance chain information as necessary.

C#
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 Method

This method is called to retrieve the combined components and put the text onto the clipboard. At that point, the programmer can simply paste the generated code into an existing source file, and make any modifications that might be required in the pasted code (or perform fine tuning that might be necessary in this generator class).

C#
public void CopyToClipboard()
{
    Clipboard.SetText(this.ViewModelCode.ToString());
}

Using the Code

Simply call the constructor with the appropriate parameters:

C#
public MainWindow()
{
    InitializeComponent();
    ViewModelGenerator generator = new ViewModelGenerator(typeof(PeoplePerson), 
                                                          string.Format("{0}.ViewModel", 
                                                          this.GetType().Namespace), 
                                                          "VMPeoplePerson", 
                                                          "Notifier", 
                                                          null, 
                                                          false, 
                                                          false);
    generator.CopyToClipboard();
}

When pasted into an empty file, the generated code looks like this:

C#
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;
        }
    }
}

The class can be converted to be a static class if desired (and is probably a wise move).

 

Points of Interest

It is important to understand the convenience features included in Visual Studio. The way Paste Special interprets values in the XML/JSON was an interesting gotcha, and I'm certainly glad I discovered this aspect of the feature.

This code would make a great VS add-in, but I'm not motivated enough to take it to that level, at east not at this time. As such, I leave it as an exercise for the reader. If you do this, PLEASE post a short article on the process, so the rest of us can benefit.

Once again, I remind you that I wrote this code for me to serve MY needs. If you need something else, be a programmer and change this code yourself. I will not be receptive to "how do you do this" kinds of questions. If I can find out how to do this without asking a question, you can certainly find out how to bend the code to your will. Afterall, Google is a vast resource of knowledge.

The sample project includes an empty file - Class2.cs. You can copy the generated code into this file after running the app. The sample project DOES NOT include a compiled binary file.

If you want to play with the Paste Special feature, the file Class1.cs contains the code generated by that feature. Delete everything inside the namespace declaration and place your cursor there before clicking Paste Special in the menu. Have fun.

History

  • 16 Dec 2014 - Initial posting.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Paddedwall Software
United States United States
I've been paid as a programmer since 1982 with experience in Pascal, and C++ (both self-taught), and began writing Windows programs in 1991 using Visual C++ and MFC. In the 2nd half of 2007, I started writing C# Windows Forms and ASP.Net applications, and have since done WPF, Silverlight, WCF, web services, and Windows services.

My weakest point is that my moments of clarity are too brief to hold a meaningful conversation that requires more than 30 seconds to complete. Thankfully, grunts of agreement are all that is required to conduct most discussions without committing to any particular belief system.

Comments and Discussions

 
GeneralRe: My vote of 2 Pin
#realJSOP19-Dec-14 6:54
mve#realJSOP19-Dec-14 6:54 
GeneralRe: My vote of 2 Pin
  Forogar  19-Dec-14 9:01
professional  Forogar  19-Dec-14 9:01 
GeneralRe: My vote of 2 Pin
#realJSOP23-Dec-14 9:06
mve#realJSOP23-Dec-14 9:06 
GeneralRe: My vote of 2 Pin
jeron119-Dec-14 4:31
jeron119-Dec-14 4:31 
GeneralRe: My vote of 2 Pin
MahBulgaria19-Dec-14 4:36
MahBulgaria19-Dec-14 4:36 
GeneralRe: My vote of 2 Pin
jeron119-Dec-14 4:46
jeron119-Dec-14 4:46 
GeneralRe: My vote of 2 Pin
#realJSOP19-Dec-14 6:21
mve#realJSOP19-Dec-14 6:21 
GeneralRe: My vote of 2 Pin
Sitary19-Dec-14 6:24
professionalSitary19-Dec-14 6:24 
>There are some inappropriate comments in first message chain with some not good words. When those stuffs are removed, I will explain my vote.<

Geez, grow up.

OK, first you said you'd explain your vote once your score was replaced. Your score was replaced - twice. Now you won't explain your vote because there's "some not good words". Really? Wait until you see the rest of the internet. OMG | :OMG:
GeneralRe: My vote of 2 Pin
MahBulgaria19-Dec-14 6:26
MahBulgaria19-Dec-14 6:26 
GeneralRe: My vote of 2 Pin
#realJSOP19-Dec-14 7:29
mve#realJSOP19-Dec-14 7:29 
GeneralMessage Closed Pin
18-Dec-14 21:14
MahBulgaria18-Dec-14 21:14 
GeneralRe: My vote of 2 Pin
#realJSOP19-Dec-14 2:16
mve#realJSOP19-Dec-14 2:16 
GeneralRe: My vote of 2 Pin
DaveX8619-Dec-14 2:27
DaveX8619-Dec-14 2:27 
GeneralRe: My vote of 2 Pin
#realJSOP19-Dec-14 2:37
mve#realJSOP19-Dec-14 2:37 
GeneralRe: My vote of 2 Pin
Slacker00719-Dec-14 2:42
professionalSlacker00719-Dec-14 2:42 
GeneralRe: My vote of 2 Pin
MahBulgaria19-Dec-14 3:37
MahBulgaria19-Dec-14 3:37 
GeneralRe: My vote of 2 Pin
Nelek19-Dec-14 3:50
protectorNelek19-Dec-14 3:50 
GeneralRe: My vote of 2 Pin
MahBulgaria19-Dec-14 4:00
MahBulgaria19-Dec-14 4:00 
GeneralRe: My vote of 2 Pin
  Forogar  19-Dec-14 4:39
professional  Forogar  19-Dec-14 4:39 
GeneralRe: My vote of 2 Pin
MahBulgaria19-Dec-14 4:51
MahBulgaria19-Dec-14 4:51 
GeneralRe: My vote of 2 Pin
jeron119-Dec-14 5:24
jeron119-Dec-14 5:24 
GeneralRe: My vote of 2 Pin
  Forogar  19-Dec-14 8:54
professional  Forogar  19-Dec-14 8:54 
GeneralRe: My vote of 2 Pin
Nelek19-Dec-14 10:18
protectorNelek19-Dec-14 10:18 
GeneralRe: My vote of 2 Pin
Pete O'Hanlon19-Dec-14 2:37
mvePete O'Hanlon19-Dec-14 2:37 
GeneralRe: My vote of 2 Pin
MahBulgaria19-Dec-14 3:38
MahBulgaria19-Dec-14 3:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.