Search code examples
wpfsilverlightt4envdteboilerplate

Is it possible to test for an interface implementation using EnvDTE.ClassInfo objects


All,

I have a T4 template that generates boiler-plate code that handles my property-changed notification and automatically registers dependancyproperties for me based on attributes I have assigned to the class. I accomplish this using EnvDTE to walk up and down the project and retrieve an IEnumerable of ClassInfo objects. I then enumerate through the ClassInfo.Attributes to retrieve ClassInfo objects that have certain custom attributes I created (i.e. INotifyPropertyChangedAttributeAttribute:System.Attribute) with all the relavent information I need to have the template write the boiler-plate code for me.

Now, my question is, is it possible to (using EnvDTE) check for an Interface implementation (such as INotifyPropertyChanged) which might be inheritied from a base class so that I don't end up with two PropertyChanged events in my class (one in the inherited class and one in the code-generated partial class)?

For examle:

public class vmBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged

    protected override void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (PropertyChanged != null) PropertyChanged(this, e);
    }
}

[INotifyPropertyChangedAttribute(Test1, typeof(string))] //NOTE: By including this attribute, T4 template will automatically generate properties.  What I need to know, though, is if the EnvDTE.ClassInfo can show Internface implementations as well so that I don't recreate the INotifyPropertyChanged Event
public partial class vm: vmBase //Implements INotifyPropertyChanged
{
    //....
}

[INotifyPropertyChangedAttribute(Test2, typeof(string))]
public partial class SomeClassThatDoesNotImplementInotifyPropertyChangedAlready
{
    //....
}

Hopefully that makes some sense.

See http://www.scottlogic.co.uk/blog/colin/2009/08/declarative-dependency-property-definition-with-t4-dte/ for an example of using envDTE and T4 to take care of dependancyproperty registrations. Concepts in my project are the same, only I'm adapting it to handle INotifyPropertyChanged boiler-plate code.

Thanks in advance.


Solution

  • It took me a little, but yes - there is a way to find out via EnvDTE if a given class somehow inherits a given interface.

    This code fragment only detects classes that inherit directly from another class that implements INotifyPropertyChanged. So before using it, one would add some recursive logic here...

    <#
    // get a reference to the project of this t4 template
    var project = VisualStudioHelper.CurrentProject;
    // get all class items from the code model
    var allClasses = VisualStudioHelper.GetAllCodeElementsOfType(project.CodeModel.CodeElements, EnvDTE.vsCMElement.vsCMElementClass, false);
    
    // iterate all classes
    foreach(EnvDTE.CodeClass codeClass in allClasses)
    {
        // get all interfaces implemented by this class
    
        var allInterfaces = VisualStudioHelper.GetAllCodeElementsOfType(codeClass.ImplementedInterfaces, EnvDTE.vsCMElement.vsCMElementInterface, true);
        if (allInterfaces.OfType<EnvDTE.CodeInterface>()
                         .Any(i => i.Name == "INotifyPropertyChanged"))
        {
            #>Implements Interface Directly: <#= codeClass.FullName #>
            <#
    
            // find classes that derive from this code class
            foreach(EnvDTE.CodeClass potentialDerivingClass in allClasses)
            {
                IEnumerable<string> theBases = VisualStudioHelper.GetAllCodeElementsOfType(potentialDerivingClass.Bases, EnvDTE.vsCMElement.vsCMElementClass, true).OfType<EnvDTE.CodeClass>().Select(cc => cc.FullName);
                if (theBases.Any(b => b == codeClass.FullName))
                {
                    #>Derives from implementing class: <#= potentialDerivingClass.FullName #>
                    <#
                }
            }
        }
    }
    #>
    

    So given a class A that implements INotifyProperty changed, a class B deriving from A and another class C deriving from B, this code would come up with classes A and B that implement INotifyPropertyChanged.

    Note: Because using the EnvDTE classes is not that nice, I used a reusable template from tangible T4 Editor's free template gallery named "tangible Visual Studio Automation Helper" - that makes it much easier to use!