Click here to Skip to main content
Click here to Skip to main content

Strongly Typed Collection Builder Addin for VS.NET 2003

By , 4 Jul 2003
 
Prize winner in Competition "C# May 2003"

Sample Image - CollectionBuilderAddin1.jpg

Introduction

This article does not aim to introduce any complexity in writing addins. It merely uses simple approaches to create an addin in VS.NET IDE that can be useful for code writing. I believe that most addins for VS.NET are very useful but they are even better if they can speed up the code writing process.

Getting Started

I was recently working in a project that involved a series of independent classes that needed to be stored in strongly typed collections in different parts of the program. From the wise words of Phil Wright (the creator of dotnetmagic library) Although creating a collection class is not difficult it can be time consuming. Indeed! I decided to create a VS.NET Addin to speed up that part of development.

The Addin consisted in adding a Menu Item when the user right clicks a class in the "Class View Window" in VS.NET, exposing an option to create a Strongly Typed Collection based on the selected class.

How to insert a Menu Item in the VS.NET Context Menu?

Firstly we need to work out how to add a Menu Item in the Context Menu but only in the "Class View Window". In order to get this done, we need to understand how the context Menus in VS.NET are correlated with the addins themselves. All context menus and Menus in VS.NET are office CommandBars. So we can find out the indexes by looping throughout the collection (this can be done on the OnConnnection method from a dummy addin). Here is part of the dump I wrote to the disk (the full listing is included with the source files :-p).

...
Text Editor
XML Data
XML Schema
Device
Debug Location
Debug
Layout
Dialog Editor
Image Editor
Source Control
Analyzer
Analyzer Windows
TestContextMenu
ResultContextMenu
Project
...

Taking in consideration that we can add the menu item anywhere from the above list as a test I tried to add one on the "Code Window" which describes the context menu that pops up when we right click on the Text Editor. I am sure someone can come up with good ideas for addins using this, such as adding quick code snippets or else :-)

Sample Image - maximum width is 600 pixels

After trying out (and having fun) placing different Menu Items around VS.NET, I came to realise that in the class view window there are a few different Context menus, but the one I wanted is called "Class View Item" which is the one that pops up when we select the Item in the Class view tree. This is how we add a menu item in that specific Context menu:

 // Create a new command
 Command command = commands.AddNamedCommand(addInInstance, 
   "CollectionCommand", "Create Collection", 
   "Adds a collection based on a type", true, 137, 
   ref contextGUIDS, 
   (int)vsCommandStatus.vsCommandStatusSupported + 
    (int)vsCommandStatus.vsCommandStatusEnabled);

 // Create a new commandBar from the "Class View Item".
 CommandBar commandBar = (CommandBar)commandBars["Class View Item"];

 // Just add to the command control.
 _commandBarControl = command.AddControl(commandBar, 1);

Notice that I have (for simplicity) kept the same code produced by the Wizard. Yes, what you need to do is to replace the menubar name given by the wizard with any of the indexes from the above list, automatically you will have that very same menu item in the belonging command bar (whatever it may be either a menu bar or context menu).

How to check if we are selecting a Class and not other elements?

Worst case scenario I did not want to create a strongly typed collection based on a namespace name or class member. The addin works on the premises that the user right clicks on the top of the class that he/she wishes to create a collection from. *The answer for this is to use the QueryStatus which is called every time the context menu pop up, notice that in VS.NET 2002 was a bug and this behaviour was not consistent however it's been fixed for VS.NET 2003 and it works perfectly!

So, when we the Class View Item pops up the QueryStatus fires and we can verify if the selected item is a class by trying to cast it to a CodeClass type, if yes we enable the adding making it visible (showing the menu item) otherwise we hide it.

...            
    // Get current selected item
    object _object = this.VSNET.SelectedItems.SelectionContainer.Item(1);
    // try to safely cast it to CodeClass
    if(_object is CodeClass)
    {
        // Show MenuItem
        status = (vsCommandStatus) vsCommandStatus.vsCommandStatusSupported |
                                   vsCommandStatus.vsCommandStatusEnabled; 

    }
    else
    {
        // Hide MenuItem.
        status = (vsCommandStatus)vsCommandStatus.vsCommandStatusInvisible; 
    }
 ...

Well, this is the presentation of the addin now we need to work on the guts of it!

Generating a Strongly Typed Collection - Design decisions

Once we have the reference from the ProjectItem instance where we want to insert the code into, we need to decide how we are going to generate the code. There are a few option available:

  1. Create a template and replace tagged strings
  2. Create the Class using the FileCodeModel exposed by the DTE object
  3. Use Microsoft's CodeDom

For simplicity, I've decided to use a template based solution. The other options are have your advantages (as well as disadvantages) however they also would introduce more complexity to the code. Thus, from the Magic articles written by Phil Wright I have introduced his Strongly Typed Collections with events as a template. Here is the template:

using System.Collections;
 
namespace #namespace#
{
         public class CollectionWithEvents : CollectionBase
         {
                 ...
                 
            // Declare the event signatures
            public delegate void CollectionClear();
            public delegate void CollectionChange(int index, object value);
 
            // Collection change events
            public event CollectionClear Clearing;
            public event CollectionClear Cleared;
            public event CollectionChange Inserting;
            public event CollectionChange Inserted;
            public event CollectionChange Removing;
            public event CollectionChange Removed;
                 ...
         }
}
        

Then we have the template that will inherit from the CollectionWithEvents (as per dotnetmagic tips and tricks documents):

using System.Collections;
namespace #namespace#
{
         public class #myTypeCollection# : CollectionWithEvents
         {
                 public int Add(#myType# value)
                 {
                          return base.List.Add(value as object);
                 }
 
                 public void Remove(#myType# value)
                 {
                          base.List.Remove(value as object);
                 }
 
                 public void Insert(int index, #myType# value)
                 {
                          base.List.Insert(index, value as object);
                 }
 
                 public bool Contains(#myType# value)
                 {
                          return base.List.Contains(value as object);
                 }
 
                 public #myType# this[int index]
                 {
                          get { return (base.List[index] as #myType#); }
                 }
         }
}

Note that we have already introduced some stubs such as #namespace# and #myType# that will be replaced by whatever string value that the selected class uses as Name identifier. Furthermore we store these templates in the C:\Program Files\Microsoft Visual Studio .NET 2003\VC#\CSharpProjectItems; the setup already deploys these templates there :-)

In a nutshell we created a method called AddCollection() which is called when clicking on the addin menu item. The method firstly gets the reference of the current loaded project:

...              
  // Get the current selected class in the "Class View"
  CodeClass codeClass = 
               (CodeClass)this.VSNET.SelectedItems.SelectionContainer.Item(1);
  // Get the current project (topmost)
  Project prj = 
              (Project)((Array)this.VSNET.ActiveSolutionProjects).GetValue(0);
...

Then, it stores the template(s) full filename path into a string:

...
// Templates path
// Custom collection
string typeCollectionTemplate
            = applicationObject.Solution.ProjectItemsTemplatePath(
                                     VSLangProj.PrjKind.prjKindCSharpProject )
              + "\\MyTypeCollection.cs";
...

Finally, we do some work. Note that we use the ReplaceText three times for the custom type so we can change the namespace, class name and item type that is stored in the collection:

...
try
{
    // Add new ProjectItem from template
    itemCollection = prj.ProjectItems.AddFromFileCopy(typeCollectionTemplate);
    // Assign new file name, if fileExists here it would fail.
    itemCollection.Properties.Item("Filename").Value = codeClass.Name +
                                                              "Collection.cs";
    // Must open as ViewKindCode otherwise it will fail on saving
    string s = itemCollection.Properties.Item("Filename").Value.ToString();

    itemCollection.Open(Constants.vsViewKindCode);
    // Save File.
    itemCollection.Save("");
    // Replace tags. 
    itemCollection.Document.ReplaceText("#namespace#",
                                 codeClass.Namespace.Name + ".Collections",0);
    itemCollection.Document.ReplaceText("#myTypeCollection#",codeClass.Name + 
                                                              "Collection",0);
    itemCollection.Document.ReplaceText("#myType#",codeClass.FullName,0);
}
catch(Exception e)
{
    // Delete File
    itemCollection.Delete();
    // Show Message...Most likely it will be file already included..
    System.Windows.Forms.MessageBox.Show(e.Message);
}
...

Important to notice that this addin will create the collection in a Collections relative namespace i.e.: in a project such as TestProject, the collection will be created in the TestProject.Collections, this can be further customized for your own need. The most important thing is that there is no need to code a custom collection from scratch :-)

Using the code

After installing the addin and writting a class that needs be represented by a collection of its on, you can merely right click on the class and press "Create Collection". Here is an example:

public class AccountRecord
 {
  private decimal _credit = decimal.Zero;
  private decimal _debit = decimal.Zero;
 private string _description = string.Empty;
  public AccountRecord(string description, decimal credit, decimal debit)
  {
   _credit = credit;
   _debit = debit;
   _description = description;
  }

  public decimal Credit
  {
   get{return _credit;}
  }
  public decimal Debit
  {
   get{return _debit;}
  }
  public string Description
  {
   get{return _description;}
  }
 }

After creating the collection we are going to be able to instantiate a AccountRecordCollection (Generated by the addin) and procede:

//Instantiate Collection
AccountRecordCollection recordCollection = new AccountRecordCollection();

//Inicialize it
for(int iCount = 0;iCount < 100 ; iCount++)
recordCollection.Add(new AccountRecord("test" + iCount.ToString(),
                     new Decimal(iCount),new Decimal(iCount)));
//Do Work!
foreach(AccountRecord record in recordCollection)
{
   Console.WriteLine(record.Description);
   Console.WriteLine(record.Credit.ToString("C"));
   Console.WriteLine(record.Debit.ToString("C"));
}
...

Looking forward

As we can see it isnt very hard to create a addin to speed up code writting and no new custom class were introduced here. As I understand this is a simple addin that has helped me quite a bit, however we could make some improvements such as:

  1. Add a VB.NET templates support (just detect the language and add templates in the VB.NET project path)
  2. Customize namespace target (instead of Root.Collections)
  3. Add other templates or Smart code building using dynamic code generation (CodeDom or FileCodeModel). This can be very interesting!

License

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

About the Author

Erick Sgarbi
Web Developer
Australia Australia
Member
Erick Sgarbi has several years in software development and in the consulting commerce. He has being working with languages such as Java, Visual C++, Visual Basic and others. Now Erick is focusing on .NET Technologies by designing applications for Mobile and Smart devices in distributed environment emphasizing his time on web enabled solutions.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionVisual Studio 2005?memberFinishedOnTime25 Mar '06 - 10:54 
This is an awesome plug in. I <3 it. I would marry it. But now I can't use it with Visual Studio 2005. Is there an update? Can the file be somehow copied into a folder and magic it works with VS 2005? I can't figure out how to get it to work with VS 2005. Please update my favorite...
AnswerRe: Visual Studio 2005?memberErick Sgarbi26 Mar '06 - 0:59 
No, there is no update for 2005 and these days you can get much better tools out there anyway. If you are into developers productivity I would advise you to have a look at DXCore and CodeRush from the guys at www.devexpress.com.   Cheers, Erick
GeneralGreat toolstaffPaul Watson11 Jan '05 - 9:07 
Thanks for the great tool. Works well and it twigged me onto having that very handy base collection class to keep the specific collection implementations clean.   regards, Paul Watson South Africa   The Code Project   South-East Asia Disaster: How you can help  ...
GeneralRe: Great toolmemberErick Sgarbi11 Jan '05 - 10:34 
No problems.
GeneralFeedbackmembermdockerty15 Jun '04 - 1:18 
It does it's job, although I would call naming a parameter 'value' questionable practice. Sadly the demo installer borks Visual Studio, seemingly by removing extensibility.dll and vslangproj.dll, if you uninstall it. Dude, sort your installer out, you cost me some time there.   -- Matt
GeneralRe: Feedback (Uninstaller fix)memberkevingreiner2 Jul '04 - 8:23 
Here's a link that tells you how fix the install yourself (by marking these DLLs as excluded from the install dependencies) or manually fix the problem afterwards.   http://www.petedavis.net/dotnet_articles/wizardfix.html   Good luck!
GeneralCool - but a couple of problemsmemberaBrookland15 Mar '04 - 23:48 
Hi,
 
Firstly thanx, this is cool
 
however I had the same problem as the first poster - I had installed VS.Net into D:\programing\Visual Studio... and so your installer put the two .cs files into C:\Program Files\... etc and I had to move them.
 
2nd I use namespace "[CompanyName].[ProductName]" and your collections are created in "[ProductName].Collections".
 
No big deal but just thought you might like to know Big Grin | :-D
GeneralDoes it work on vb.netmembervbnetuk5 Mar '04 - 5:56 
Hi   Does it work on vb.net?     Thanks A lot vbnetuk@yahoo.co.uk
GeneralRe: Does it work on vb.netmemberlopperman23 Apr '04 - 4:24 
you don't need it in vb.net. You actually have a collection object type that can be declared.
GeneralRe: Does it work on vb.netmemberErick Sgarbi24 Apr '04 - 12:14 
I believe that VB.NET does not have a strong collection type or at least until we have generics does it? I wouldn't have a clue but anyway, this addin would need to undergo some changes including the template to cater for other languages.   Cheers, Erick
GeneralRe: Does it work on vb.netmemberJuan Gabriel18 May '04 - 7:06 
This only works when Option Strict is OFF. With Option Strict ON, you do need a strongly typed collection. It's a really good practice to keep Option Strict ON, by the way.   -JG
GeneralChanging File LocationmemberTom Bean29 Jan '04 - 15:55 
Erick,   Even though you have recommended CodeObject, I'm still using your add-in because it does a great job for me.   I'm trying to change the location the files are stored in and wanted to see if you have done this. Instead of storing the files in the project directory, I would...
GeneralRe: Changing File LocationmemberErick Sgarbi29 Jan '04 - 21:10 
Hi Tom,   I am glad the addin is helping you out. I am not sure but I think you can use the project.Item(0).Delete(); in order to delete that specific item within a project. I would have to look it up in the code.     What are the changes you made?   Cheers, Erick
GeneralRe: Changing File LocationmemberTom Bean30 Jan '04 - 2:39 
Erick,   I changed the namespace and have saved the file in the 'Collections' directory as I wanted. The only thing remaining is removing the original file that was copied into the project's directory.   I tried using the Delete() method and the problem is when you delete the...
GeneralRe: Changing File LocationmemberTom Bean30 Jan '04 - 4:32 
Erick,   I figured out a way to remove the original files in the project directory. After AddFromFileCopy() returns, I save the item in new variable that I change and save to the 'Collections' directory using SaveAs(). After saving the modified copy, I Delete() the original.   I...
GeneralNot WorkingsussAnonymous29 Nov '03 - 8:20 
I installed the add-in. In VS.NET, the Add-in appears checked in the Add-in Manager. When I go to Class View and right-click on a class, the CreateCollection menu item does not appear.   Does anyone know what I have done wrong?
GeneralRe: Not Workingsussherbman29 Nov '03 - 8:53 
Never mind, works ok.
Generalvalue as objectmemberTom Bean24 Nov '03 - 13:15 
Erick,   I can't find any C# documentation regarding the "value as object" parameter type you used in your collections class. I was under the impression that it would add a new object to the collection, however, I just discovered that it when adding an object that contains with a member...
GeneralRe: value as objectmemberErick Sgarbi24 Nov '03 - 13:28 
"value" is the parameter which the methods are accepting and the "as" keyword is used to safely cast an instance into another for example:   MyObject o = anotherInstance as MyObject;   if o == null then it means that "anotherInstance" can not be cast into a MyObject type. this is a...
General---Anouncement--- STC + CodeObjectmemberErick Sgarbi9 Aug '03 - 5:29 
Hi all,   I just wanted to thank you all for the votes and the ideas that never stopped coming in after i published this article. Well, even though it is only on Unmanged VSIP is free now which means lots of improvements in dealing with VS.NET and slowly i am stopping using straight...
GeneralProblemmemberAtirez9 Aug '03 - 5:06 
Hi, I have been messing around with this idea after reading this article. And after seeing that it does not work for VB or J# I thought it should not be that difficult to get it working for these other languages. I started implementing this using CodeDom but found that the following lines of...
GeneralRe: ProblemmemberErick Sgarbi9 Aug '03 - 5:16 
Hi there,   I know it can be very frustating dealing with VS.NET automation. Here is my answer, recently a friend of mine introduced me to a very cool Strongly Typed collection generator here it is:   http://weblogs.asp.net/pgreborio/posts/10040.aspx   it works for vb.net...
GeneralFeature request: Interface collectionmemberThomas Freudenberg8 Aug '03 - 3:47 
It'd be nice, if your addd-in would support creating collections for interfaces, too.   Regards, Thomas   Regards Thomas   Disclaimer:Because of heavy processing requirements, we are currently using some of your unused brain capacity for backup processing. Please ignore...
GeneralRe: Feature request: Interface collectionmemberErick Sgarbi9 Aug '03 - 5:30 
You can easily enter that in the 'if' statement on Querystatus to also query for interfaces.   Greate idea!   Cheers Erick Look at the anouncement!
GeneralOrdered CollectionmemberTom Bean5 Aug '03 - 18:39 
Erick,   I have been using your Addin extensively and it's great. I need some of the collections in sorted order and wondered if you have any suggestions about the best method to use.   I have used a binary search algorithm to determine the positon to insert an object into arrays...

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

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 5 Jul 2003
Article Copyright 2003 by Erick Sgarbi
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid