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.
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!
Okay. Dumb question, but what exactly is VisualStudioHelper. Is that from Tangible? I'm using Tangible T4, but I borrowed the code from Colin Eberhardt (see link in question) to enumerate through EnvDTE.
I should note that when I look at the tangible Visual Studio Automation Helper in the gallery, I am seeing a DTEHelper class, but NOT a VisualStudioHelper class. DTEHelper doesn't have a GetAllCodeElementsOfType function.
Okay. Found it. Apparently, there is a "Visual Studio Automation Helper" and a "Tangible Visual Studio Automation Helper". I was looking at the former. Okay, I'll give this a shot and see what I come up with. If it works, then I'll mark this as the answer.
Okay, so now another question. Will this also capture instances where I am implementing InterfaceA which inherits from InterfaceB in my base class? i.e.
public interface InterfaceA:INotifyPropertyChanged, SomeOtherInterface { } public class vmBase : InterfaceA { ... } public class vm : vmBase { ... }
Also found a bug in Tangible Visual Studio Automation Helper. Try transforming the template multiple times and see what happens to your partial classes that were generated from Custom Attributes. I'll have to dive deep into the template to see if I can identify the bug and how to fix it. Having said that, I am new to EnvDTE, so an up-vote to anyone that can show me where exactly tangible went wrong and how to fix it.