Manipulating Addin Code Files

Latest post 04-29-2008 5:19 PM by Gary. 8 replies.
  • 03-28-2008 8:38 AM

    • Terry
    • Top 10 Contributor
    • Joined on 05-10-2006
    • Posts 90

    Manipulating Addin Code Files

    Now that I have the basics working, I need to hone in on controlling the code file(s) in the Addin Project.  Some of our to-be user groups are not technical at all and it will be important to tightly control what happens in the code.  Specifically I would like to do some of the following:

     - Insert code into existing code blocks

     - Add Event handling code

     - Add methods with comments and code already inside

     - Read code blocks, modify them and put them back into the code file

     - Create new property definitions

     - Create new code blockss (regions, functions, subs)

    We are starting to work with Autodesk's Intent product and they really seem to do all this type of thing very well in their soon-to-be-released version of Intent that uses VSTA 2.0.  Where can I learn more about how these types of things are being done?

    Thanks.

     

  • 03-28-2008 9:00 AM In reply to

    • Gary
    • Top 10 Contributor
    • Joined on 07-13-2006
    • Posts 320

    Re: Manipulating Addin Code Files

    You can get good info from VSX forums (Visual Studio Extensibility).

    The DTE can be used to add, disable, and remove elements of the IDE to tightly control the user experience, which is an excellent objective.

     

    Here's some quick info from my tinkering...

     

    DTE can be used to insert source directly into the project, compile and run.  With VSTA’s support for WPF, XAML forms, even the UI can be inserted as source text.  It could be as simple as something like this (context: inside the VstaDesignTimeIntegration class of sample):

     

    //1. Insert source code

    EnvDTE.ProjectItem appSrc = this.macroProject.ProjectItems[0];

    If (appSrc.Name.Equals("AppAddIn.cs"))

    {

     

                EnvDTE.TextSelection sel = (EnvDTE.TextSelection)appSrc.Document.Selection;

                sel.StartOfDocument(false);

                sel.Text = "// pass in contents of project as text here\n";

    }

     

     

    //2. Save and build…           

    IVstaHostAdapter hostAdapter = (IVstaHostAdapter)this.macroProject.get_Extender("VSTAHostAdapter2007");

    string hostItemFileName = hostAdapter.ProjectHostItems[0].CodeFilePath;

    string hostItemClassName = hostAdapter.ProjectHostItems[0].ProgrammingModelHostItem.Identifier;

    EnvDTE.ProjectItem hostItemProjectItem =

                    this.macroProject.ProjectItems.Item(Path.GetFileName(hostItemFileName));

     

    hostItemProjectItem.Save(null);

    this.macroProject.DTE.Solution.SolutionBuild.Build(true);

     

    ====

     

    To give some more context and let you try this out yourself, below is some drop in code to ‘spit’ the following class source code into ‘AppAddin.cs’ in the Addin project:

     

       public class Chess

        {

            // This is the move function

            public int Move(bool IsOK)

            {

                int a = 1;

                int b = 3;

     

                return a + b; //return default(int);

            }

        }

     

    ==

    You can use the EnvDTE.DTE dte instance in VstaDesignTimeIntegration.cs file, to add this code to an addin project.

    Here’s example code that does this.

     

            public void SpitEventCode()

            {

                EnsureIDE();

                System.Diagnostics.Debug.Assert(this.dte != null);

                System.Diagnostics.Debug.Assert(macroProject != null);

     

                // Add a comment to AppAddin.cs.

                EnvDTE.ProjectItem appSrc = null;

                foreach (EnvDTE.ProjectItem pitem in macroProject.ProjectItems)

                {

                    if (pitem.Name.Equals("AppAddIn.cs"))

                    {

                        appSrc = pitem;

                        break;

                    }

                }

     

                EnvDTE80.FileCodeModel2 model = (EnvDTE80.FileCodeModel2)appSrc.FileCodeModel;

                foreach (EnvDTE.CodeElement codeElement in model.CodeElements)

                {

                    ExamineCodeElement(codeElement, 3);

                }

     

                EnvDTE.TextSelection sel = (EnvDTE.TextSelection)appSrc.Document.Selection;

                sel.StartOfDocument(false);

                sel.NewLine(1);

                sel.LineUp(false, 1);

                sel.Text = "// New comment\n";

     

            }

     

            // recursively examine code elements       

            private void ExamineCodeElement(EnvDTE.CodeElement codeElement, int tabs)

            {

                tabs++;

                try

                {

                    Console.WriteLine(new string('\t', tabs) + "{0} {1}", codeElement.Name, codeElement.Kind.ToString());

                    // if this is a namespace, add a class to it.               

                    if (codeElement.Kind == EnvDTE.vsCMElement.vsCMElementNamespace)

                    {

                        AddClassToNamespace((EnvDTE.CodeNamespace)codeElement);

                    }

                    foreach (EnvDTE.CodeElement childElement in codeElement.Children)

                    {

                        ExamineCodeElement(childElement, tabs);

                    }

                }

                catch

                {

                    Console.WriteLine(new string('\t', tabs) + "codeElement without name: {0}", codeElement.Kind.ToString());

                }

            }

     

            // add a class to the given namespace       

            private void AddClassToNamespace(EnvDTE.CodeNamespace ns)

            {

                // add a class           

                EnvDTE80.CodeClass2 chess = (EnvDTE80.CodeClass2)ns.AddClass("Chess", -1, null, null, EnvDTE.vsCMAccess.vsCMAccessPublic);

                // add a function with a parameter and a comment           

                EnvDTE80.CodeFunction2 move = (EnvDTE80.CodeFunction2)chess.AddFunction("Move", EnvDTE.vsCMFunction.vsCMFunctionFunction, "int", -1, EnvDTE.vsCMAccess.vsCMAccessPublic, null);

                move.AddParameter("IsOK", "bool", -1);

                move.Comment = "This is the move function";

                // add some text to the body of the function           

                EnvDTE80.EditPoint2 editPoint = (EnvDTE80.EditPoint2)move.GetStartPoint(EnvDTE.vsCMPart.vsCMPartBody).CreateEditPoint();

                editPoint.Indent(null, 0);

                editPoint.Insert("int a = 1;");

                editPoint.InsertNewLine(1);

                editPoint.Indent(null, 3);

                editPoint.Insert("int b = 3;");

                editPoint.InsertNewLine(2);

                editPoint.Indent(null, 3);

                editPoint.Insert("return a + b; //");

            }

  • 03-28-2008 9:12 AM In reply to

    • Terry
    • Top 10 Contributor
    • Joined on 05-10-2006
    • Posts 90

    Re: Manipulating Addin Code Files

    Very cool.  I'll definately make good use of this.  Looks like we will be able to manipulate things very well from the looks of the sample code.

    I'm assuming there aren't any limitations based on using VB?

    Thanks.

  • 03-28-2008 9:58 AM In reply to

    • Gary
    • Top 10 Contributor
    • Joined on 07-13-2006
    • Posts 320

    Re: Manipulating Addin Code Files

    >>using VB?

    None that I know of.

  • 03-28-2008 12:55 PM In reply to

    • Terry
    • Top 10 Contributor
    • Joined on 05-10-2006
    • Posts 90

    Re: Manipulating Addin Code Files

    Great.  When it comes to non-technical end-users VB is much more readable, especially when they've never written code before.
  • 04-05-2008 6:00 PM In reply to

    Re: Manipulating Addin Code Files

    Hi Gary,

    Is it also possible to get access to the designer code file (the other partial class of the host item) an add code to this one. These are not located in the ProjectItem list (macroProject.ProjectItems).

     

    Thanks

    /Stine     

  • 04-07-2008 10:45 PM In reply to

    • Gary
    • Top 10 Contributor
    • Joined on 07-13-2006
    • Posts 320

    Re: Manipulating Addin Code Files

    Look in the childrens' children:

        foreach (EnvDTE.ProjectItem pitem in macroProject.ProjectItems)

        { 

            String itemName = Path.GetFileNameWithoutExtension(pitem.Name);

            String fileName = pitem.get_FileNames(1);

            String designerXMLItemName = itemName + ".designer";

            EnvDTE.ProjectItems childItems = pitem.ProjectItems;

            Boolean designerXMLFound = false;

            foreach (EnvDTE.ProjectItem childItem in childItems)

            {

                if (Path.GetFileNameWithoutExtension(childItem.Name) == designerXMLItemName)

                {

                    designerXMLFound = true;

                    String designerItemName = itemName + ".designer";

                    EnvDTE.ProjectItems childchildItems = childItem.ProjectItems;

                    Boolean designerFound = false;

                    foreach (EnvDTE.ProjectItem childchildItem in childchildItems)

                    {

                        if (Path.GetFileNameWithoutExtension(childchildItem.Name) == designerItemName)

                        {

                            designerFound = true;

                            EnvDTE.TextSelection sel = (EnvDTE.TextSelection)childchildItem.Document.Selection;

                            sel.StartOfDocument(false);

                            sel.NewLine(1);

                            sel.LineUp(false, 1);

                            sel.Text = "// New comment\n";

                        }

                    }

                }

            }

        }

     

  • 04-29-2008 10:25 AM In reply to

    • Terry
    • Top 10 Contributor
    • Joined on 05-10-2006
    • Posts 90

    Re: Manipulating Addin Code Files

    As I'm trying to manipulate code files I find myself regularly wondering about the DTE object.  Specifically, which one I should be using.   DTE, DTE80 or DTE90?  I'm assumig there is a reason why results are being regularly cast to a different namespace and type?

    I guess I should as the same question for my VSTA-related code.  When I'm creating and loading projects it seems I can use any of these as well.

    Thx.

  • 04-29-2008 5:19 PM In reply to

    • Gary
    • Top 10 Contributor
    • Joined on 07-13-2006
    • Posts 320

    Re: Manipulating Addin Code Files

    See Referencing Automation Assemblies and the DTE2 Object

    To create automation applications, you must perform steps to gain access to the automation members. First, you must reference the requisite automation assemblies, and second, you must get a reference to the top-level object, DTE2.

    In Visual Studio .NET 2002 and Visual Studio .NET 2003, all core automation items were located in an assembly named EnvDTE, and its highest, hierarchical object is the DTE object. It is the parent object for all core automation objects, collections, and their members. DTE2 derives from DTE.

    In Visual Studio 2005 and Visual Studio 2008, additions and updates were made to some of these objects, collections, and members. Rather than update the existing assembly and compromise backwards-compatibility for existing add-ins and automation projects, all new, updated functionality is in an assembly named EnvDTE80 (EnvDTE version 8.0) and EnvDTE90 (EnvDTE version 9.0). Most updated functions in EnvDTE80 and EnvDTE90 maintain the same names as previous versions but append a number to the end of the function name. For example, the newer version of the TaskItems collection is named TaskItems2, and the newer version of the Solution object is named Solution2. Because the new members are more robust than previous versions and contain the latest functionality, the recommended approach is to use the new objects and collections when writing new automation applications. 

    Although new items are in EnvDTE80 and EnvDTE90, most of the core automation functionality is still in EnvDTE. So, when you write new automation applications (such as add-ins), be sure to reference EnvDTE, EnvDTE80, and EnvDTE90. You can do this in one step by referencing the COM libraries "Microsoft Development Environment 8.0" and "Microsoft Development Environment 9.0." Also, if you use members from the EnvDTE assembly, you must also set a reference to both the DTE object and the DTE2 object. This gives you access to all of the items.

    For information about gaining access to the project-specific object models, see Functional Automation Groups.

Page 1 of 1 (9 items) | RSS
Copyright Summit Software Company, 2008. All rights reserved.