다음을 통해 공유


Generating C# from Visual Studio Code Model

In my recent project I had had the chance to use T4 (text template transformation toolkit) and learned to like it. While trying to come up with ideas for demonstrating in-product guidance I discovered that it is fairly easy to let Visual Studio generate code based on code that is in a different file in VS. This might sound confusing but I think this has a lot of potential and is fairly easy to write.

Here is a scenario: For WPF and Silverlight it is pretty much standard these days to write a ViewModel class that encapsulates a Model class in a way that makes WPF/Silverlight databinding easier and adds additional properties that are only relevant to the view. One way is to implement INotifyPropertyChanged in the ViewModel. For a class:

    public class Customer
    {
        public string Email { get; set; }
        // ...
    }

you write a corresponding ViewModel class

    public partial class CustomerViewModel : INotifyPropertyChanged
    {
        public CustomerViewModel(Customer modelElement)
        {
            this._modelElement = modelElement;
        }

        private readonly Customer _modelElement;
        public string Email
        {
            get { return _modelElement.Email; }
            set
            {
                if(value != Email)
                {
                    Email = value;
                    OnPropertyChanged("Email");
         }
            }
        }
        // ...
    }

Pretty tedious.

With T4 and the Visual Studio Code Model you can read the properties of the Model class (Customer in our example) and generate the ViewModel wrapper. Here is roughly what I do, a running Visual Studio 2010 RC project is attached.

To get to the data (= the code in the other file) you write a small code block in the T4 file.

    ProjectItem inputProjectItem = DTE.Solution.FindProjectItem(inputFileName);
    FileCodeModel codeModel = inputProjectItem.FileCodeModel;
    CodeClass modelClass = FindCodeElement<CodeClass>(codeModel.CodeElements);

All the methods are in VS libraries except FindCodeElement<T> . It basically traverses the hierarchy that is created by the CodeElement.Children property and finds the first type that matches. In our case it is a CodeClass . CodeClass has a property called Members that returns a list that contains CodeProperty instances.

<#  foreach(CodeProperty prop in modelClass.Members)
{ #>
public <#= prop.Type.AsString #> <#= prop.Name #>
{
get { return _modelElement.<#= prop.Name #>; }
            set
{
  if(value != _modelElement.<#= prop.Name #>)
                {
_modelElement.<#= prop.Name #> = value;
OnPropertyChanged("<#= prop.Name #>");
                }
            }
}
<# } #>

This is the core of the idea. In the attached demo Solution I also did a bit more around formatting and you will see the necessary references and imports .

This is obviously not complete. To make this ready for real use one would need to think about references between Model classes, access modifiers, methods, naming, namespaces and more.

Some more background info and inspiration can be found on Gareth Jones' blog. And Colin Eberhardt has a sophisticated solution and more complete that uses the same ideas.