Tuesday 4 December 2012

Enabling MVVM INotifiyPropertyChanged in EF 5 POCO Entities created using the EF 5.x DbContext Generator

While combining EF5 with WPF MVVM, I noticed that changes to individual properties within my bound DataContext weren’t updating the UI.

This was of course because the the actual DataContext itself isn’t changing, but only the POCO Properties housed within in. However, each of the POCO Properties don’t individually raise an INotifyPropertyChanged event.

In order to implement this, one must modify the T4 Template provided as part of the EF 5.x DbContext Generator, to insert the extra lines which add the relevant code to Implement the Interface, and raise the INotifiyPropertyChanged Event.

This is performed by making the following modifications to the T4 Template;

(Note, I work in vb.net here, the code will be different for a c'# implementation!)

  1. Open you’re .tt file
  2. Find the Section which loops through all of the Complex Types in your model, which begins with the line “For Each loopComplex As ComplexType…”.
  3. Below the Line saying “Partial <#=Accessibility.ForType(complex)#> Class <#=code.Escape(complex)#>”, add the following two lines of code;

    Implements INotifyPropertyChanged
    Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

    This Implements the INotifiyPropertyChanged Interface, and adds the Public Events to each of the POCO’s for your Complex Types.

  4. Find the Write Header Sub, which starts with “Public Sub WriteHeader(ByVal fileManager As EntityFrameworkTemplateFileManager)”
  5. Below the “<#” and the line saying “fileManager.StartHeader()”, add the following line of code;



    Imports System.ComponentModel

    This adds a reference to “System.ComponentModel” , which is where INotifiyPropertyChanged lives.
  6. Find the AnyProperty Function, which starts with “Public Function AnyProperty(accessibility As String, type As String….
  7. Replace the whole function with the following code;



    Public Function AnyProperty(accessibility As String, type As String, name As String, getterAccessibility As String, setterAccessibility As String, defaultValue As String)
            Return String.Format( _
                    CultureInfo.InvariantCulture, _
                    "{6}    Private _{0} As {1}{2}{6}" & _
                    "    {3} Property {0} As {1}{6}" & _
                    "        {4}Get{6}" & _
                    "            Return _{0}{6}" & _
                    "        End Get{6}" & _
                    "        {5}Set(ByVal value As {1}){6}" & _
                    "            _{0} = value{6}" & _
                    "            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(""{0}"")){6}" & _
                    "        End Set{6}" & _
                    "    End Property",  _
                    name, _
                    type, _
                    defaultValue, _
                    accessibility, _
                    getterAccessibility, _
                    setterAccessibility, _
                    Environment.NewLine)
        End Function

    This enforces that all Properties are created with their full Getter’s and Setter’s, and adds the code to raise the PropertyChanged Event<

  8. Find the “EntityClassOpening” Function, which starts with the Line “Public Function EntityClassOpening(entity As EntityType) As String
  9. Replace the entire Function with the following Code;



    Public Function EntityClassOpening(entity As EntityType) As String
            Return String.Format( _
                CultureInfo.InvariantCulture, _
                "Partial {0} {1}Class {2}{3}{4}{5}{4}{6}", _
                Accessibility.ForType(entity), _
                _code.SpaceAfter(_code.MustInheritOption(entity)), _
                _code.Escape(entity), _
                _code.StringBefore(Environment.NewLine & "         Inherits ", _typeMapper.GetTypeName(entity.BaseType)), _
                Environment.NewLine, _
                "Implements INotifyPropertyChanged", _
                "Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged")
        End Function

    This adds the Implementation and Public Event for the INotifyPropertyChanged Interface

  10. Save your template, you may need to open your Model and save this also. Finall build your project, and you should have some fully declared Properties along with the INotifiyPropertyChanged code!


I hope this helps!

3 comments:

  1. HY
    Many, many thank for this article.
    You saved me a lot of time. Thanks!!!!!!!!

    ReplyDelete
  2. So I've tried this and the property getter/setter gets modified correctly. But the lines to implement the interfaces aren't being written at all. It's strange. I even modified the partial public class line and it doesn't modify either. What am I missed?

    ReplyDelete
  3. Thank you thank you thank you thank you a million times!

    How this isn't already a part of the template or an option to include it is BEYOND me.

    ReplyDelete