May 2016

Volume 31 Number 5

[.NET Compiler Platform]

Maximize your Model-View-ViewModel Experience with Roslyn

By Alessandro Del

Model-View-ViewModel (MVVM) is a very popular architectural pattern that works perfectly with XAML application platforms such as Windows Presentation Foundation (WPF) and the Universal Windows Platform (UWP). Architecting an application using MVVM provides, among many others, the benefit of clean separation between the data, the application logic and the UI. This makes applications easier to maintain and test, improves code re-use and enables designers to work against the UI without interacting with the logic or the data. Over the years, a number of libraries, project templates, and frameworks, such as Prism and the MVVM Light Toolkit, have been built to help developers implement MVVM more easily and efficiently. However, in some situations, you can’t rely on external libraries, or you might simply want to be able to implement the pattern quickly while keeping focus on your code.

Though there are a variety of MVVM implementations, most share a number of common objects whose generation can be easily automated with the Roslyn APIs. In this article, I’ll explain how to create custom Roslyn refactorings that make it easy to generate elements common to every MVVM implementation. Because giving you a complete summary about MVVM isn’t possible here, I’m assuming you already have basic knowledge of the MVVM pattern, of the related terminology and of the Roslyn code analysis APIs. If you need a refresher, you can read the following articles: “Patterns—WPF Apps with the Model-View-ViewModel Design Pattern”, “C# and Visual Basic: Use Roslyn to Write a Live Code Analyzer for Your API”, and “C#—Adding a Code Fix to Your Roslyn Analyzer”.

The accompanying code is available in both C# and Visual Basic versions. This version of the article includes both C# and Visual Basic listings.

Common MVVM Classes

Any typical MVVM implementation requires at least the following classes (in some cases with slightly different names, depending on the MVVM flavor you apply):

ViewModelBase - A base abstract class exposing members that are common to every ViewModel in the application. Common members may vary depending on the application’s architecture, but its most basic implementation brings change notification to any derived ViewModel.

RelayCommand - A class that represents a command through which ViewModels can invoke methods. There are typically two flavors of RelayCommand, one generic and one non-generic. In this article I’ll use the generic flavor (RelayCommand<T>).

I’m assuming you’re already familiar with both, so I won’t go into more detail. Figure 1a represents the C# code for ViewModelBase, and Figure 1b shows the Visual Basic code.

Figure 1a ViewModelBase Class (C#)

abstract class ViewModelBase : System.ComponentModel.INotifyPropertyChanged
{
  public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
  // Raise a property change notification
  protected virtual void OnPropertyChanged(string propertyName)
  {
    PropertyChanged?.Invoke(this, new System.ComponentModel.
PropertyChangedEventArgs(propertyName));
  }
}

Figure 1b ViewModelBase Class (Visual Basic)

Public MustInherit Class ViewModelBase
   Implements System.ComponentModel.INotifyPropertyChanged
   Public Event PropertyChanged(sender As Object,
                                e As System.ComponentModel.PropertyChangedEventArgs) _
                                Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
   Protected Sub OnPropertyChanged(propertyName As String)
     RaiseEvent PropertyChanged(Me, New System.ComponentModel.
                                PropertyChangedEventArgs(propertyName))
   End Sub
 End Class

This is the most basic implementation of ViewModelBase; it just provides property change notification based on the INotifyPropertyChanged interface. Of course, you might have additional members depending on your specific needs. Figure 2a shows the C# code for RelayCommand<T>, and Figure 2b shows the Visual Basic code.

Figure 2a RelayCommand<T> Class (C#)

class RelayCommand<T> : System.Windows.Input.ICommand
{
  readonly Action<T> _execute = null;
  readonly Predicate<T> _canExecute = null;
  public RelayCommand(Action<T> execute)
    : this(execute, null)
  {
  }
  public RelayCommand(Action<T> execute, Predicate<T> canExecute)
  {
    if (execute == null)
        throw new ArgumentNullException(nameof(execute));
    _execute = execute;
    _canExecute = canExecute;
  }
  [System.Diagnostics.DebuggerStepThrough]
  public bool CanExecute(object parameter)
  {
    return _canExecute == null ? true : _canExecute((T)parameter);
  }
  public event EventHandler CanExecuteChanged;
  public void RaiseCanExecuteChanged()
  {
    var handler = CanExecuteChanged;
    if (handler != null)
    {
      handler(this, EventArgs.Empty);
    }
  }
  public void Execute(object parameter)
  {
    _execute((T)parameter);
  }
}

Figure 2b RelayCommand(Of T) Class (Visual Basic)

Class RelayCommand(Of T)
   Implements System.Windows.Input.ICommand
   Private ReadOnly _execute As Action(Of T)
   Private ReadOnly _canExecute As Predicate(Of T)
   Public Sub New(execute As Action(Of T))
     Me.New(execute, Nothing)
   End Sub
   Public Sub New(execute As Action(Of T), canExecute As Predicate(Of T))
     If execute Is Nothing Then
       Throw New ArgumentNullException(NameOf(execute))
     End If
     _execute = execute
     _canExecute = canExecute
   End Sub
   <System.Diagnostics.DebuggerStepThrough>
   Public Function CanExecute(parameter As Object) As Boolean _
     Implements System.Windows.Input.ICommand.CanExecute
     Return If(_canExecute Is Nothing, True, _canExecute(parameter))
   End Function
   Public Event CanExecuteChanged As EventHandler Implements _
     System.Windows.Input.ICommand.CanExecuteChanged
   Public Sub RaiseCanExecuteChanged()
     RaiseEvent CanExecuteChanged(Me, EventArgs.Empty)
   End Sub
   Public Sub Execute(parameter As Object) Implements ICommand.Execute
     _execute(parameter)
   End Sub
 End Class

This is the most common implementation of RelayCommand<T> and it’s appropriate for most MVVM scenarios. It’s worth mentioning that this class implements the System.Windows.Input.ICommand interface, which requires implementing a method called CanExecute, whose goal is telling the caller if a command is available for execution.

How Roslyn Can Simplify Your Life

If you don’t work with external frameworks, Roslyn can be a real life-saver: You can create custom code refactorings that replace a class definition and automatically implement a required object, and you can easily automate the generation of ViewModel classes based on the model properties. Figure 3 shows an example of what you’ll achieve by the end of the article.

Implementing MVVM Objects with a Custom Roslyn Refactoring
Figure 3  Implementing MVVM Objects with a Custom Roslyn Refactoring

The benefit of this approach is that you always keep your focus on the code editor and you can implement the required objects very quickly. Moreover, you can generate a custom ViewModel based on the model class, as demonstrated later in the article. Let’s start by creating a refactoring project.

Creating a Project for Roslyn Refactorings

The first step is to create a new Roslyn refactoring. To accomplish this, you use the Code Refactoring (VSIX) project template, available in the Extensibility node under the language of your choice in the New Project dialog. Call the new project MVVM_Refactoring, as shown in Figure 4.

Creating a Roslyn Refactoring Project
Figure 4  Creating a Roslyn Refactoring Project

Click OK when ready. When Visual Studio 2015 generates the project, it automatically adds a class called MVVMRefactoringCodeRefactoringProvider, defined inside the CodeRefactoringProvider.cs (or .vb for Visual Basic) file. Rename the class and the file to MakeViewModelBaseRefactoring and MakeViewModelBaseRefactoring.cs, respectively. For the sake of clarity, remove both the auto-generated ComputeRefactoringsAsync and ReverseTypeNameAsync methods (the latter is auto-generated for demonstration purposes).

Investigating a Syntax Node

As you might know, the main entry point for a code refactoring is the ComputeRefactoringsAsync method, which is responsible for creating a so-called quick action that will be plugged into the code editor’s light bulb, if code analysis of a syntax node satisfies the required rules. In this particular case, the ComputeRefactoringsAsync method must detect if the developer is invoking the light bulb over a class declaration. With the help of the Syntax Visualizer tool window, you can easily understand the syntax elements with which you need to work. More specifically, in C# you must detect whether the syntax node is a ClassDeclaration, represented by an object of type Microsoft.CodeAnalysis.CSharp.Syntax.ClassDeclarationSyntax (see Figure 5), while in Visual Basic you determine if the syntax node is a ClassStatement, represented by an object of type Microsoft.CodeAnalysis.VisualBasic.Syntax.ClassStatementSyntax. Actually, in Visual Basic, ClassStatement is a child node of ClassBlock, which represents the entire code for a class. The reason C# and Visual Basic have different objects is because of the way each represents a class definition: C# uses the class keyword with curly braces as delimiters, and Visual Basic uses the Class keyword with the End Class statement as a delimiter.

Understanding a Class Declaration
Figure 5  Understanding a Class Declaration

Creating an Action

The first code refactoring I’ll discuss relates to the ViewModelBase class. The first step is to write the ComputeRefactoringsAsync method in the MakeViewModelBaseRefactoring class. With this method, you check if the syntax node represents a class declaration; if so, you can create and register an action that will be available in the light bulb. Figure 6a shows how to accomplish this in C#, and Figure 6b shows the Visual Basic code (see inline comments).

Figure 6a The Main Entry Point: ComputeRefactoringsAsync Method (C#)

private string Title = "Make ViewModelBase class";
public async sealed override Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
  // Get the root node of the syntax tree
  var root = await context.Document.
    GetSyntaxRootAsync(context.CancellationToken).
    ConfigureAwait(false);
  // Find the node at the selection.
  var node = root.FindNode(context.Span);
  // Is this a class statement node?
  var classDecl = node as ClassDeclarationSyntax;
  if (classDecl == null)
  {
    return;
  }
  // If so, create an action to offer a refactoring
  var action = CodeAction.Create(title: Title,
    createChangedDocument: c =>
    MakeViewModelBaseAsync(context.Document,
      classDecl, c), equivalenceKey: Title);
  // Register this code action.
  context.RegisterRefactoring(action);
}

Figure 6b The Main Entry Point: ComputeRefactoringsAsync Method (Visual Basic)

Private Title As String = "Make ViewModelBase class"
 Public NotOverridable Overrides Async Function _
   ComputeRefactoringsAsync(context As CodeRefactoringContext) As Task
   ' Get the root node of the syntax tree
   Dim root = Await context.Document.
     GetSyntaxRootAsync(context.CancellationToken).
     ConfigureAwait(False)
   ' Find the node at the selection.
   Dim node = root.FindNode(context.Span)
   ' Is this a class statement node?
   Dim classDecl = TryCast(node, ClassStatementSyntax)
   If classDecl Is Nothing Then Return
   ' If so, create an action to offer a refactoring
   Dim action = CodeAction.Create(title:=Title,
                                  createChangedDocument:=Function(c) _
                                  MakeViewModelBaseAsync(context.
                                  Document, classDecl, c),
                                  equivalenceKey:=Title)
   ' Register this code action.
   context.RegisterRefactoring(action)
 End Function

With this code, you’ve registered an action that can be invoked on the syntax node if this is a class declaration. The action is performed by the MakeViewModelBaseAsync method, which implements the refactoring logic and provides a brand new class.

Code Generation

Roslyn not only provides an object-oriented, structured way to represent source code, it also allows parsing source text and generating a syntax tree with full fidelity. To generate a new syntax tree from pure text, you invoke the SyntaxFactory.ParseSyntaxTree method. It takes an argument of type System.String that contains the source code from which you want to generate a SyntaxTree.

Roslyn also offers the VisualBasicSyntaxTree.ParseText and CSharpSyntaxTree.ParseText methods to accomplish the same result; however, in this case it makes sense to use SyntaxFactory.ParseSyntaxTree because the code invokes other Parse methods from SyntaxFactory, as you’ll see shortly.

Once you have a new SyntaxTree instance, you can perform code analysis and other code-related operations on it. For example, you can parse the source code of a whole class, generate a syntax tree from it, replace a syntax node in the class and return a new class. In the case of the MVVM pattern, common classes have a fixed structure, so the process of parsing source text and replacing a class definition with a new one is fast and easy. By taking advantage of so-called multi-line string literals, you can paste a whole class definition into an object of type System.String, then get a SyntaxTree from it, retrieve the SyntaxNode that corresponds to the class definition and replace the original class in the tree with the new one. I’ll first demonstrate how to accomplish this with regard to the ViewModelBase class. More specifically, Figure 7a shows the code for C#, and Figure 7b shows the code for Visual Basic.

Figure 7a MakeViewModelBaseAsync: Generating a New Syntax Tree from Source Text (C#)

private async Task<Document> MakeViewModelBaseAsync(Document document,
  ClassDeclarationSyntax classDeclaration, CancellationToken cancellationToken)
{
  // The class definition represented as source text
  string newImplementation = @"abstract class ViewModelBase : INotifyPropertyChanged
{
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
// Raise a property change notification
protected virtual void OnPropertyChanged(string propertyName)
{
  PropertyChanged?.Invoke(this, new System.ComponentModel.
    PropertyChangedEventArgs(propertyName));
}
}
";
  // 1. ParseSyntaxTree() gets a new SyntaxTree from the source text
  // 2. GetRoot() gets the root node of the tree
  // 3. OfType<ClassDeclarationSyntax>().FirstOrDefault()
  //    retrieves the only class definition in the tree
  // 4. WithAdditionalAnnotations() is invoked for code formatting
  var newClassNode = SyntaxFactory.ParseSyntaxTree(newImplementation).
    GetRoot().DescendantNodes().
    OfType<ClassDeclarationSyntax>().
    FirstOrDefault().
    WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation);
  // Get the root SyntaxNode of the document
  var root = await document.GetSyntaxRootAsync();
  // Generate a new CompilationUnitSyntax (which represents a code file)
  // replacing the old class with the new one
  CompilationUnitSyntax newRoot = (CompilationUnitSyntax)root.
    ReplaceNode(classDeclaration, newClassNode).NormalizeWhitespace();
  // Detect if a using System.ComponentModel directive already exists.
  if ((newRoot.Usings.Any(u => u.Name.ToFullString() ==
    "System.ComponentModel"))== false)
  {
    // If not, add one
    newRoot = newRoot.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.
      QualifiedName(SyntaxFactory.IdentifierName("System"),
                    SyntaxFactory.IdentifierName("ComponentModel"))));
  }
  // Generate a new document based on the new SyntaxNode
  var newDocument = document.WithSyntaxRoot(newRoot);
  // Return the new document
  return newDocument;
}

Figure 7b MakeViewModelBaseAsync: Generating a New Syntax Tree from Source Text (Visual Basic)

Private Async Function MakeViewModelBaseAsync(document As Document,
   classDeclaration As ClassStatementSyntax,
   cancellationToken As CancellationToken) As Task(Of Document)
   ' The class definition represented as source text
   Dim newImplementation = "Public MustInherit Class ViewModelBase
 Implements INotifyPropertyChanged
 Public Event PropertyChanged(sender As Object,
                              e As PropertyChangedEventArgs) _
                              Implements INotifyPropertyChanged.PropertyChanged
 Protected Sub OnPropertyChanged(propertyName As String)
   RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
 End Sub
 End Class
 "
   ' 1. ParseSyntaxTree() gets a New SyntaxTree from the source text
   ' 2. GetRoot() gets the root node of the tree
   ' 3. OfType(Of ClassDeclarationSyntax)().FirstOrDefault()
   '    retrieves the only class definition in the tree
   ' 4. WithAdditionalAnnotations() Is invoked for code formatting
   Dim newClassNode = SyntaxFactory.ParseSyntaxTree(newImplementation).
     GetRoot().DescendantNodes().
     OfType(Of ClassBlockSyntax)().
     FirstOrDefault().
     WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation)
   Dim parentBlock = CType(classDeclaration.Parent, ClassBlockSyntax)
   ' Get the root SyntaxNode of the document
   Dim root = Await document.GetSyntaxRootAsync(cancellationToken)
   ' Generate a new CompilationUnitSyntax (which represents a code file)
   ' replacing the old class with the new one
   Dim newRoot As CompilationUnitSyntax = root.ReplaceNode(parentBlock,
                                                           newClassNode).
                                                           NormalizeWhitespace()
   ' Detect if an Imports System.ComponentModel directive already exists
  ' If Not newRoot.Imports.Any(Function(i) i.ImportsClauses.
    Where(Function(f) f.ToString = "System.ComponentModel").Any) Then            
  ' If not, add one
     Dim newImp = SyntaxFactory.
       ImportsStatement(SyntaxFactory.
       SingletonSeparatedList(Of ImportsClauseSyntax)(SyntaxFactory.
       SimpleImportsClause(SyntaxFactory.
       ParseName("System.ComponentModel"))))
     newRoot = newRoot.AddImports(newImp)
   End If
   ' Generate a new document based on the new SyntaxNode
   Dim newDocument = document.WithSyntaxRoot(newRoot)
 ' Return the new document
   Return newDocument
 End Function

Because the SyntaxFactory type is used many times, you could consider doing a static import and, thus, simplify your code by adding an Imports Microsoft.CodeAnalisys.VisualBasic.SyntaxFactory directive in Visual Basic and a using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory directive in C#. There’s no static import here to make it easier to discover some of the methods SyntaxFactory offers.

Notice that the MakeViewModelBaseAsync method takes three arguments:

  • A Document, which represents the current source code file
  • A ClassDeclarationSyntax (in Visual Basic, it’s ClassStatementSyntax), which represents the class declaration over which code analysis is executed
  • A CancellationToken, which is used in case the operation must be canceled

The code first invokes SyntaxFactory.ParseSyntaxTree to get a new SyntaxTree instance based on the source text that represents the ViewModelBase class. The invocation of GetRoot is required to get the root SyntaxNode instance for the syntax tree. In this particular scenario, you know in advance that the parsed source text has only one class definition, so the code invokes FirstOrDefault<T> over OfType<T> to retrieve the one descendant node of the required type, which is ClassDeclarationSyntax in C# and ClassBlockSyntax in Visual Basic. At this point, you need to replace the original class definition with the ViewModelBase class. To accomplish this, the code first invokes Document.GetSyntaxRootAsync to asynchronously retrieve the root node for the document’s syntax tree, then it invokes ReplaceNode to replace the old class definition with the new ViewModelBase class. Notice how the code detects if a using (C#) or Imports (Visual Basic) directive exists for the System.ComponentModel namespace by investigating the CompilationUnitSyntax.Usings and the CompilationUnitSyntax.Imports collections, respectively. If not, a proper directive is added. This is useful for adding a directive at the code file level if not already available.

Remember, in Roslyn, objects are immutable. This is the same concept that applies to the String class: You actually never modify a string, so when you edit a string or invoke methods like Replace, Trim or Substring, you get a new string with the specified changes. For this reason, every time you need to edit a syntax node, you actually create a new syntax node with updated properties.

In Visual Basic, the code also needs to retrieve the parent ClassBlockSyntax for the current syntax node, which is instead of type ClassStatementSyntax. This is required to retrieve the instance of the actual SyntaxNode that will be replaced. Providing a common implementation of the RelayCommand<T> class works exactly the same, but you need to add a new code refactoring. To accomplish this, right-click the project name in Solution Explorer, then select Add | New Item. In the Add New Item dialog, select the Refactoring template and name the new file MakeRelayCommandRefactoring.cs (or .vb, for Visual Basic). The refactoring logic is the same as for the ViewModelBase class (of course, with different source text). Figure 8a shows the full C# code for the new refactoring, which includes the ComputeRefactoringsAsync and MakeRelayCommandAsync methods, and Figure 8b shows the Visual Basic code. 

Figure 8a Code Refactoring That Implements the RelayCommand<T> Class (C#)

[ExportCodeRefactoringProvider(LanguageNames.CSharp,
  Name = nameof(MakeRelayCommandRefactoring)), Shared]
internal class MakeRelayCommandRefactoring : CodeRefactoringProvider
{
  private string Title = "Make RelayCommand<T> class";
  public async sealed override Task
    ComputeRefactoringsAsync(CodeRefactoringContext context)
  {
    var root = await context.Document.GetSyntaxRootAsync(context.
      CancellationToken).
        ConfigureAwait(false);
    // Find the node at the selection.
    var node = root.FindNode(context.Span);
    // Only offer a refactoring if the selected node is
    // a class statement node.
    var classDecl = node as ClassDeclarationSyntax;
    if (classDecl == null)
    {
      return;
    }
    var action = CodeAction.Create(title: Title,
      createChangedDocument: c =>
      MakeRelayCommandAsync(context.Document,
      classDecl, c), equivalenceKey: Title);
    // Register this code action.
    context.RegisterRefactoring(action);
  }
  private async Task<Document>
    MakeRelayCommandAsync(Document document,
    ClassDeclarationSyntax classDeclaration, CancellationToken cancellationToken)
  {
    // The class definition represented as source text
    string newImplementation = @"
class RelayCommand<T> : ICommand
{
  readonly Action<T> _execute = null;
  readonly Predicate<T> _canExecute = null;
  public RelayCommand(Action<T> execute)
    : this(execute, null)
  {
  }
  public RelayCommand(Action<T> execute, Predicate<T> canExecute)
  {
    if (execute == null)
      throw new ArgumentNullException(""execute"");
      _execute = execute;
        _canExecute = canExecute;
  }
  [System.Diagnostics.DebuggerStepThrough]
  public bool CanExecute(object parameter)
  {
    return _canExecute == null ? true : _canExecute((T)parameter);
  }
  public event EventHandler CanExecuteChanged;
  public void RaiseCanExecuteChanged()
  {
    var handler = CanExecuteChanged;
    if (handler != null)
    {
      handler(this, EventArgs.Empty);
    }
  }
  public void Execute(object parameter)
  {
    _execute((T)parameter);
  }
}
";
    // 1. ParseSyntaxTree() gets a new SyntaxTree from the source text
    // 2. GetRoot() gets the root node of the tree
    // 3. OfType<ClassDeclarationSyntax>().FirstOrDefault() retrieves the only class
    //      definition in the tree
    // 4. WithAdditionalAnnotations() is invoked for code formatting
    var newClassNode = SyntaxFactory.ParseSyntaxTree(newImplementation).
      GetRoot().DescendantNodes().
      OfType<ClassDeclarationSyntax>().
      FirstOrDefault().
      WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation);
    // Get the root SyntaxNode of the document
    var root = await document.GetSyntaxRootAsync(cancellationToken);
    // Generate a new CompilationUnitSyntax (which represents a code file)
    // replacing the old class with the new one
    CompilationUnitSyntax newRoot = (CompilationUnitSyntax) root.
      ReplaceNode(classDeclaration,
      newClassNode).NormalizeWhitespace();
    if ((newRoot.Usings.Any(u => u.Name.ToFullString() == "System.Windows.Input"))
      == false)
    {
      newRoot = newRoot.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.
        QualifiedName(SyntaxFactory.IdentifierName("System"),
          SyntaxFactory.IdentifierName("Windows.Input"))));
    }
    // Generate a new document based on the new SyntaxNode
    var newDocument = document.WithSyntaxRoot(newRoot);
    // Return the new document
    return newDocument;
  }
}

Figure 8b Code Refactoring That Implements the RelayCommand(Of T) Class (Visual Basic)

<ExportCodeRefactoringProvider(LanguageNames.VisualBasic,
                                Name:=NameOf(MakeRelayCommandRefactoring)), [Shared]>
 Friend Class MakeRelayCommandRefactoring
   Inherits CodeRefactoringProvider
   Public NotOverridable Overrides Async Function _
     ComputeRefactoringsAsync(context As CodeRefactoringContext) As Task
     Dim root = Await context.Document.
       GetSyntaxRootAsync(context.CancellationToken).
       ConfigureAwait(False)
     ' Find the node at the selection.
     Dim node = root.FindNode(context.Span)
     ' Only offer a refactoring if the selected node is a class statement node.
     Dim classDecl = TryCast(node, ClassStatementSyntax)
     If classDecl Is Nothing Then Return
     Dim action = CodeAction.Create("Make RelayCommand(Of T) class",
                                    Function(c) _
                                    MakeRelayCommandAsync(context.Document,
                                                          classDecl, c))
     ' Register this code action.
     context.RegisterRefactoring(action)
  End Function
   Private Async Function MakeRelayCommandAsync(document As Document, _
    classDeclaration As ClassStatementSyntax, cancellationToken As CancellationToken) _
    As Task(Of Document)
     ' The class definition represented as source text
     Dim newImplementation = "Class RelayCommand(Of T)
   Implements ICommand
   Private ReadOnly _execute As Action(Of T)
   Private ReadOnly _canExecute As Predicate(Of T)
   Public Sub New(ByVal execute As Action(Of T))
     Me.New(execute, Nothing)
   End Sub
   Public Sub New(ByVal execute As Action(Of T), ByVal canExecute As Predicate(Of T))
     If execute Is Nothing Then
       Throw New ArgumentNullException(""execute"")
     End If
     _execute = execute
     _canExecute = canExecute
   End Sub
   <DebuggerStepThrough> _
   Public Function CanExecute(ByVal parameter As Object) As Boolean Implements ICommand.CanExecute
     Return If(_canExecute Is Nothing, True, _canExecute(CType(parameter, T)))
   End Function
   Public Event CanExecuteChanged As EventHandler Implements ICommand.CanExecuteChanged
   Public Sub RaiseCanExecuteChanged()
     RaiseEvent CanExecuteChanged(Me, EventArgs.Empty)
   End Sub
   Public Sub Execute(ByVal parameter As Object) Implements ICommand.Execute
     _execute(CType(parameter, T))
   End Sub
 End Class"
   ' 1. ParseSyntaxTree() gets a new SyntaxTree from the source text
   ' 2. GetRoot() gets the root node of the tree
   ' 3. OfType(Of ClassDeclarationSyntax)().FirstOrDefault()
   '    retrieves the only class definition in the tree
   ' 4. WithAdditionalAnnotations() Is invoked for code formatting
   Dim newClassNode = SyntaxFactory.ParseSyntaxTree(newImplementation).
   GetRoot().DescendantNodes().
     OfType(Of ClassBlockSyntax)().
     FirstOrDefault().
     WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation)
   Dim parentBlock = CType(classDeclaration.Parent, ClassBlockSyntax)
   Dim root = Await document.GetSyntaxRootAsync(cancellationToken)
   ' Generate a new CompilationUnitSyntax (which represents a code file)
   ' replacing the old class with the new one
   Dim newRoot As CompilationUnitSyntax =
     root.ReplaceNode(parentBlock, newClassNode)
   'Detect if an Imports System.Windows.Input directive already exists
   If Not newRoot.Imports.Any(Function(i) i.ToFullString.
     Contains("System.Windows.Input")) Then
       'If not, add one
       Dim newImp = SyntaxFactory.
         ImportsStatement(SyntaxFactory.
         SingletonSeparatedList(Of ImportsClauseSyntax) _
         (SyntaxFactory.
         SimpleImportsClause(SyntaxFactory.
         ParseName("System.Windows.Input"))))
       newRoot = newRoot.AddImports(newImp)
     End If
     Dim newDocument = document.WithSyntaxRoot(newRoot)
     Return newDocument
   End Function
 End Class

You’ve successfully completed two custom refactorings and you now have the basics to implement additional refactorings, depending on your implementation of the MVVM pattern (such as a message broker, a service locator and service classes).

As an alternative, you could use the SyntaxGenerator class. This offers language-agnostic APIs, which means the code you write results in a refactoring that targets both Visual Basic and C#. However, this approach requires generating every single syntax element for the source text. By using SyntaxFactory.ParseSyntaxTree, you can parse any source text. This is especially useful if you write developer tools that need to manipulate source text that you don’t know in advance.

Availability: UWP Apps and WPF

Instead of making custom refactorings available universally, it makes sense to restrict their availability in the light bulb only to those platforms you actually use MVVM with, such as WPF and UWP. In the ComputeRefactoringsAsync method, you can get an instance of the semantic model for the current document and then invoke the GetTypeByMetadataName method from the Compilation property. For example, the following code demonstrates how to make the refactoring available only to UWP apps:

// Restrict refactoring availability to Windows 10 apps only
var semanticModel = await context.Document.GetSemanticModelAsync();
var properPlatform = semanticModel.Compilation.
  GetTypeByMetadataName("Windows.UI.Xaml.AdaptiveTrigger");
if (properPlatform != null)
{
  var root = await context.Document.
    GetSyntaxRootAsync(context.CancellationToken).
    ConfigureAwait(false);
  // ...
}

Because the Windows.UI.Xaml.AdaptiveTrigger type exists only in UWP apps, the refactoring will be available if the code analysis engine detects that this type has been referenced. If you wanted to make the refactoring available to WPF, you could write the following check:

// Restrict refactoring availability to WPF apps only
var semanticModel = await context.Document.GetSemanticModelAsync();
var properPlatform = semanticModel.Compilation.
  GetTypeByMetadataName("System.Windows.Navigation.JournalEntry");

Similarly, the System.Windows.Navigation.JournalEntry uniquely exists in WPF, so a non-null result from GetTypeByMetadataName means that code analysis is running against a WPF project. Of course, you could combine both checks to make the refactorings available to both platforms.

Testing the Code

You can test the work you’ve done so far in the Visual Studio experimental instance by pressing F5. For example, create a WPF project and add this very simple class:

class EmptyClass
{
  string aProperty { get; set; }
}

Right-click the class declaration, then select Quick Actions from the context menu. At this point, the light bulb shows the two new refactorings as expected and provides the proper suggestion (see Figure 3 for reference).

If you want to publish custom refactorings, the Code Refactoring (VSIX) project template automates the generation of a VSIX package that can be published to the Visual Studio Gallery. If you’d rather publish your work as a NuGet package, the trick is creating an Analyzer with the Code Fix project and then adding Code Fix item templates.

Generating a Custom ViewModel with Roslyn

If you’re wondering why using Roslyn might be a better approach just to add static text to a class, imagine you want to automate the generation of a ViewModel from a business class, which is the model. In this case, you can generate a new ViewModel class and add the necessary properties based on the data exposed by the model. Just to give you an idea, Figure 9 shows how to produce a refactoring called Make ViewModel class, which demonstrates how to create a simplified version of a ViewModel.

Figure 9 Generating a ViewModel Based on a Model

private async Task<Document> MakeViewModelAsync(Document document,
  ClassDeclarationSyntax classDeclaration, CancellationToken cancellationToken)
{
  // Get the name of the model class
  var modelClassName = classDeclaration.Identifier.Text;
  // The name of the ViewModel class
  var viewModelClassName = $"{modelClassName}ViewModel";
  // Only for demo purposes, pluralizing an object is done by
  // simply adding the "s" letter. Consider proper algorithms
  string newImplementation = $@"class {viewModelClassName} : INotifyPropertyChanged
{{
public event PropertyChangedEventHandler PropertyChanged;
// Raise a property change notification
protected virtual void OnPropertyChanged(string propname)
{{
  PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname));
}}
private ObservableCollection<{modelClassName}> _{modelClassName}s;
public ObservableCollection<{modelClassName}> {modelClassName}s
{{
  get {{ return _{modelClassName}s; }}
  set
  {{
    _{modelClassName}s = value;
    OnPropertyChanged(nameof({modelClassName}s));
  }}
}}
public {viewModelClassName}() {{
// Implement your logic to load a collection of items
}}
}}
";
    var newClassNode = SyntaxFactory.ParseSyntaxTree(newImplementation).
      GetRoot().DescendantNodes().
      OfType<ClassDeclarationSyntax>().
      FirstOrDefault().
      WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation);
    // Retrieve the parent namespace declaration
    var parentNamespace = (NamespaceDeclarationSyntax) classDeclaration.Parent;
    //Add the new class to the namespace
    var newParentNamespace =
      parentNamespace.AddMembers(newClassNode).NormalizeWhitespace();
    var root = await document.GetSyntaxRootAsync(cancellationToken);
    CompilationUnitSyntax newRoot = (CompilationUnitSyntax)root;
    newRoot = newRoot.
              ReplaceNode(parentNamespace, newParentNamespace).NormalizeWhitespace();
    newRoot = newRoot.AddUsings(SyntaxFactory.UsingDirective(SyntaxFactory.
      QualifiedName(SyntaxFactory.IdentifierName("System"),
        SyntaxFactory.IdentifierName("Collections.ObjectModel"))),
        SyntaxFactory.UsingDirective(SyntaxFactory.
      QualifiedName(SyntaxFactory.IdentifierName("System"),
        SyntaxFactory.IdentifierName("ComponentModel"))));
    // Generate a new document based on the new SyntaxNode
    var newDocument = document.WithSyntaxRoot(newRoot);
    // Return the new document
    return newDocument;
  }

This code generates a ViewModel class that exposes an ObservableCollection of the model type, plus an empty constructor where you should implement your logic, as shown in Figure 10. Of course, this code should be extended with any additional members you might need, such as data properties and commands, and should be improved with a more efficient pluralization algorithm.

Automating the Generation of a ViewModel Class
Figure 10  Automating the Generation of a ViewModel Class

Learn from Microsoft: The INotifyPropertyChanged Refactoring

One of the repetitive tasks you do with MVVM is implementing change notification to classes in your data model via the System.ComponentModel.INotifyPropertyChanged interface. For instance, if you had the following Customer class:

class Customer 
{
  string CompanyName { get; set; }
}

You should implement INotifyPropertyChanged so that bound objects are notified of any changes to data:

class Customer : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;
  string _companyName;
  string CompanyName
  {
    get { return _companyName; }
    set {
      _companyName = value;
      PropertyChanged?.Invoke(this,
        new PropertyChangedEventArgs(nameof(CompanyName))); }
  }
}

As you can imagine, in a data model made of many classes with dozens of properties, this task can take a lot of time. Among the companion samples for Roslyn, Microsoft shipped a code refactoring that automates the implementation of the INotifyPropertyChanged interface with a simple click. It’s called ImplementNotifyPropertyChanged and is available for both C# and Visual Basic in the Src/Samples subfolder of the Roslyn repository at github.com/dotnet/roslyn. If you compile and test the example, you’ll see how fast and efficient it makes implementing the INotifyPropertyChanged interface, as shown in Figure 11.

Implementing the INotifyPropertyChanged Interface
Figure 11  Implementing the INotifyPropertyChanged Interface

This example is particularly useful because it shows how to use the Roslyn APIs to walk through an object definition, how to parse specific members and how to make edits to existing properties without supplying a completely new class definition. Studying the source code for this example is definitely recommended in order to understand more complex code generation scenarios.

Wrapping Up

Among its almost infinite number of possible uses, Roslyn also makes it incredibly easy to support the Model-View-ViewModel pattern. As I showed in this article, you can leverage the Roslyn APIs to parse the source code of certain classes required in any MVVM implementation, such as ViewModelBase and RelayCommand<T>, and generate a new syntax node that can replace an existing class definition. And Visual Studio 2015 will show a preview in the light bulb, providing another amazing coding experience.


Alessandro del Sole has been a Microsoft MVP since 2008. Awarded MVP of the Year five times, he has authored many books, eBooks, instructional videos and articles about .NET development with Visual Studio. Del Sole works as a Solution Developer Expert for Brain-Sys (brain-sys.it), focusing on .NET development, training and consulting. You can follow him on Twitter: @progalex.

Thanks to the following technical experts for reviewing this article: Jason Bock (Magenic) and Anthony Green (Microsoft)

Jason Bock is a Practice Lead for Magenic (magenic.com), author and speaker. Visit his web site at jasonbock.net.


Discuss this article in the MSDN Magazine forum