Click here to Skip to main content
15,886,362 members
Articles / Programming Languages / C#

Wrapi, a Visual Studio add-in for automatic wrapper implementation

Rate me:
Please Sign up or sign in to vote.
4.73/5 (17 votes)
23 Jan 2007CPOL5 min read 48.3K   603   24   9
Wrapi, a visual studio add-in for automatic wrapping of class fields or properties in c#, vb.net and c++

Introduction

Some time ago I published some controls to easily manage options in .net applications providing an automatic GUI for the Settings values and some related facilities (more info in the article [^]). When developing that code, I needed a list which let me be notified of when items (OptionsPanels in that case) were added or removed from the collection. Because of generics collections in .net don't have nor such notification nor virtual methods, the only solution I found was to wrap an instance of a generic List<> in my class and then implement the generic IList<> interface and wrap all public methods of the IList<> interface to the public methods of the variable whose instance was previously created. Then I found the need to do this type of wrapping in another couple of projects. So I asked myself how difficult would has been to automate this action to avoid manually redoing it each time. And here's the answer to my question, an add-in that does exactly what I was thinking about.

Using the add-in

Download the installer, unzip and install it. Start visual studio, open any kind of project (C#, VB or C++) and go to the declaration of a member variable (or property) inside a class. Right click on the variable name and select “Wrap variable”, if all is ok a form will open (exactly the image above). In the form there's a tree view in which you can select what properties, methods or events to wrap in the class. You have two additional options, “Surround code with Region”, which as the name suggests put a #region outlined to the generated code (or a comment for C++), and Prefix, which let you specify an optional prefix for generated elements (methods, properties and events), to avoid name collision.

Modifying the add-in

Download the project and open it with visual studio. I've hugely commented the code to allow easy modification of the features of the add-in, so I don't think there's the need to repeat in the article the same information. Ill give only a general overview of what is editable and “where”. Mainly you can do three things easily:

Apply a different filter to select what functions to load (and take in consideration) and which others not. For example, to allow friends classes (for C++) and internal ones (for C#), I fill the tree view with publics, protecteds and privates. If you only code in C# for instance, you can remove the private ones. You can do this in the FillFunctions method of the Connect class. See the code below:

C#
private void FillFunctions(Dictionary<String, CodeElement> 
                           funcs, CodeElement func, 
                           CodeElement elm, 
                           bool overloading)
{
  if (
      // Function name must not be null or empty
      //
      !String.IsNullOrEmpty(func.Name) &&
      ( 
        // If function is a member function
        //
        func.Kind == vsCMElement.vsCMElementFunction &&
        ( 
          // The function must not be a constructor
          //
            ( _CurrentLanguage != Language.VB && 
              func.Name != elm.Name ) ||
            ( _CurrentLanguage == Language.VB && 
              func.Name != "New" )
        ) &&
        (
          // The function must not be a destructor 
          //
            (_CurrentLanguage != Language.VB && 
              !func.Name.StartsWith("~")) ||
            ( _CurrentLanguage == Language.VB && 
              func.Name != "Finalize" )
        ) 
      //
      // End: If function is a member function
      ) ||

        // If function is a property
        //
          func.Kind == vsCMElement.vsCMElementProperty ||

          // If function is an event
          //
          func.Kind == vsCMElement.vsCMElementEvent
      )
    {
      [...]

      if (!funcs.ContainsKey(name))
      {
        funcs.Add(name, func);
      }

    [...]
  }
}

I thinks the comments are clear. Anyway as you can see there are some checks on the function and if all its ok, the function is added to the collection.

Change the appearance of elements (methods, properties and events) to be loaded in the three view; this is achievable by modifying the code of the LoadToTreeNodes method, inside the Connect class. See the code below:

C#
private void LoadToTreeNodes(Dictionary<String, CodeElement> 
                             funcs, TreeNodeCollection tnc)
{
  [...]

  TreeNode node = nodes.Add(name, cf.Name + "( " +
  parmString.ToString() + " )");
  node.ToolTipText = (cf.IsShared ? "static " : (cf
    .CanOverride ? "virtual " : "")) + 
    cf.Type.AsString + " " + cf.Name + "( " + 
    parmStringComplete.ToString() + " )";

  [...]
}

I've removed useless code to highlight the important one. As you can see a TreeNode variable is created whose name is node. This is a single node appearing in the tree view and representing an element (method, property, event). You can put all your logics here and edit the node as you wish (colors, images, tooltip text, ecc...).

Edit the generated code. In fact there's a method called for each language generated. If the language of the document is C# the method CreateCodeCSharp is called; if the language is mc++ the method CreateCodeMC is called; if the language is vb the method CreateCodeVB is called; finally if the code is native c++ the method CreateCodeVC is called. All four methods are hugely commented so there's no need to report here the same information. Look through the comments, each defines a new element added (for example the visibility, return type and name of a property, etc...). A note: because I personally (and I think a lot of people like me) don't use mc++, I've not included a full implementation of the mc++ code generation. Instead, the CreateCodeMC calls CreateCodeVC. This means that for mc++ only methods are supported, and aren't supported properties and events. In addition, because of I don't use (and don't know well so) the visual basic language, there could be probably some errors in the code generation of this language; if you found some of them report these in the comment section of the article providing the correct code to help other coders who could need it.

Points of Interest (problems)

When developing this add-in I encountered a problem, that is, it seems that visual studio doesn't represent the code model the same way in .net or in native c++. In fact while in .net its easy to step through types, accessing base as well, in c++ this is not possible. In fact, the element type returned is a special VC type and is not QueryInterfaceable for the classic DOM elements of the code model. See the code to understand what I mean:

C#
private void FillTypeFunctions(Dictionary<String, CodeElement> 
                               funcs, CodeElement elm)
{
  if (elm != null)
  {
    if (!_Types.ContainsKey(elm.FullName))
    {
      _Types.Add(elm.FullName, elm);
    }

    switch (elm.Kind)
    {
      case vsCMElement.vsCMElementStruct:
      {
        FillStructFunctions(funcs, (CodeStruct)elm);
        break;
      }
      case vsCMElement.vsCMElementClass:
      {
        FillClassFunctions(funcs, (CodeClass)elm);
        break;
      }
      case vsCMElement.vsCMElementVCBase:
      {
        //FillClassFunctions(funcs, (CodeClass)elm);
        break;
      }
      case vsCMElement.vsCMElementInterface:
      {
        FillInterfaceFunctions(funcs, (CodeInterface)elm);
        break;
      }
      case vsCMElement.vsCMElementIDLCoClass:
      {
        //FillInterfaceFunctions(funcs,(CodeInterface)elm);
        break;
      }
    }
  }
}

As you can see, in the switch statement there is a case vsCMElement.vsCMElementVCBase which represents a base type element of another type and is not castable to CodeClass or CodeInterface or CodeStruct. Probably theirs is some special element in the VCCodeModel namespace, but to not loose too much time, I ignored it. In fact is not so common to need the base classes methods of the type of the variable, and if it is really needed the trick is to change directly (temporally) the type of the variable itself to one of his base types and then select “Wrap variable”. For example if you have a variable whose class is C which inherits from B which again inherits from A, if the name of the variable is _Variable, you can:

  • Declare the variable as C _Variable; right-click on it, select the “Wrap variable” command and select the methods you need.
  • Change the type from C to B and re-do all the steps.
  • Change the type from B to A and re-do.
  • Reset the type of the variable from A to C.

Remember that this is needed only if you are coding in native c++.

Conclusion

So, that's all, I think. I hope you'll find it useful, bye!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Synved Ltd.
Ireland Ireland

Comments and Discussions

 
GeneralVS2008 Pin
Yasin753-Jun-10 0:06
Yasin753-Jun-10 0:06 
GeneralRe: VS2008 Pin
Elia Sarti3-Jun-10 0:33
Elia Sarti3-Jun-10 0:33 
GeneralCorrection for missing overloaded methods Pin
Elia Sarti8-Feb-07 7:24
Elia Sarti8-Feb-07 7:24 
GeneralCollections events ... Pin
Pop Catalin24-Jan-07 9:32
Pop Catalin24-Jan-07 9:32 
GeneralRe: Collections events ... Pin
Elia Sarti24-Jan-07 13:34
Elia Sarti24-Jan-07 13:34 
QuestionWhy C++ ? Pin
gxdata23-Jan-07 20:57
gxdata23-Jan-07 20:57 
AnswerRe: Why C++ ? Pin
Elia Sarti23-Jan-07 21:01
Elia Sarti23-Jan-07 21:01 
GeneralRe: Why C++ ? Pin
gxdata23-Jan-07 22:51
gxdata23-Jan-07 22:51 
GeneralRe: Why C++ ? Pin
Elia Sarti24-Jan-07 4:20
Elia Sarti24-Jan-07 4:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.