Introduction
.NET introduces object oriented programming, which is why it became so popular. One of the object oriented programming concepts is Encapsulation. It brings clean code and it's for the best. But once you have defined your private members, you have to spend some awful time coding corresponding properties. I thought it would be interesting to accelerate the process and to automatically generate those properties. I did some research and I found around here some macros which would do the trick. However, I did think that it wasn't very productive and I thought that a right-click on a property would be easier. So I turned to Visual Studio add-ins...
Background
In order to develop some add-ins, Visual Studio allows us to use a wizard which makes the task easier. But it's not as simple as it should be, especially when you want to add some functionalities like contextual popup menu. I found a couple of articles which treated the subject, but each one concluded with the fact that MSDN wasn't that complete on the subject mostly because it was linked to Office.
Using the code
The source files contain two projects, one for the add-in, and one for the setup. In order to install the add-in correctly, you have to pass through the setup, or you can create a *.reg file to register it. The simplest way is to use the setup. As I work on Visual Studio .NET 2003, I don't know if it works on Visual Studio .NET 2002, but I think it would. Here is the OnConnection
method which creates the buttons:
public void OnConnection(object application,
Extensibility.ext_ConnectMode connectMode, object addInInst,
ref System.Array custom)
{
applicationObject = (_DTE)application;
addInInstance = (AddIn)addInInst;
if(connectMode == Extensibility.ext_ConnectMode.ext_cm_Startup)
{
object []contextGUIDS = new object[] { };
Commands commands = applicationObject.Commands;
_CommandBars commandBars = applicationObject.CommandBars;
try
{
prefix = Helper.GetRegistryValue(Helper.OPTION_PREFIX,
Helper.DEFAULT_PREFIX);
getterSetterComment =
Helper.GetRegistryValue(Helper.OPTION_GETTERSETTER_COMMENT,
Helper.DEFAULT_GETTERSETTER_COMMENT);
getterComment =
Helper.GetRegistryValue(Helper.OPTION_GETTER_COMMENT,
Helper.DEFAULT_GETTER_COMMENT);
setterComment =
Helper.GetRegistryValue(Helper.OPTION_SETTER_COMMENT,
Helper.DEFAULT_SETTER_COMMENT);
popupMenuEnabled =
Boolean.Parse(Helper.GetRegistryValue(Helper.OPTION_POPUPMENU,
Helper.DEFAULT_POPUPMENU));
CommandBar cmbHost;
CommandBar cmbCodeWindow = (CommandBar)commandBars["Code Window"];
string libGetterSetter = "getter / setter";
string libGetter = "getter";
string libSetter = "setter";
if (popupMenuEnabled)
{
Command cmdEnabler = commands.AddNamedCommand(addInInstance,
"PropertyGeneratorEnabler",
"",
"",
true,
0,
ref contextGUIDS,
(int)vsCommandStatus.vsCommandStatusInvisible);
cmdEnabler.AddControl(cmbCodeWindow, 1);
CommandBarPopup cbcPropertyGenerator = (CommandBarPopup)
cmbCodeWindow.Controls.Add(MsoControlType.msoControlPopup,
Missing.Value,
Missing.Value,
1,
true);
cbcPropertyGenerator.Visible = true;
cbcPropertyGenerator.BeginGroup = true;
cbcPropertyGenerator.Caption = "Generate property for...";
cmbPropertyGenerator = cbcPropertyGenerator.CommandBar;
cmbHost = cmbPropertyGenerator;
}
else
{
libGetterSetter = libGetterSetter + "Generate property for ";
libGetter = libGetter + "Generate property for ";
libSetter = libSetter + "Generate property for ";
cmbHost = cmbCodeWindow;
}
Command cmdGetterSetter = commands.AddNamedCommand(addInInstance,
"PropertyGeneratorGetterSetter",
libGetterSetter,
"Generate property for Getter/Setter",
true,
6948,
ref contextGUIDS,
(int)vsCommandStatus.vsCommandStatusSupported +
(int)vsCommandStatus.vsCommandStatusEnabled);
cmdGetterSetter.AddControl(cmbHost, 1);
Command cmdGetter = commands.AddNamedCommand( addInInstance,
"PropertyGeneratorGetter",
libGetter,
"Generate property for Getter",
true,
6947,
ref contextGUIDS,
(int)vsCommandStatus.vsCommandStatusSupported+
(int)vsCommandStatus.vsCommandStatusEnabled);
cmdGetter.AddControl(cmbHost, 2);
Command cmdSetter = commands.AddNamedCommand(addInInstance,
"PropertyGeneratorSetter",
libSetter,
"Generate property for Setter",
true,
6943,
ref contextGUIDS,
(int)vsCommandStatus.vsCommandStatusSupported+
(int)vsCommandStatus.vsCommandStatusEnabled);
cmdSetter.AddControl(cmbHost, 3);
}
catch(System.Exception e)
{
foreach(Command cmd in commands)
{
if ((cmd.Name != null) &&
(cmd.Name.StartsWith("PropertyGenerator")))
cmd.Delete();
}
MessageBox.Show("An error ocurred : " +
e.Message + "\r\n\nPropertyGenerator "+
"has been cleaned. Please restart Visual Studio",
"PropertyGenerator error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
}
PropertyGenerator creates buttons at Visual Studio startup and deletes them on Visual Studio stop. It's easier this way to control the add-in than to create them on setup.
Here is the code that generates the property:
private void GenerateProperty(PropertyTypeValue propertyType)
{
string propType = "";
string propName = "";
string propVariable = "";
string prop = "";
try
{
FormatParameters(prefix, ref propType,
ref propName, ref propVariable);
TextSelection txsSelection =
(TextSelection)applicationObject.ActiveDocument.Selection;
txsSelection.EndOfLine(false);
txsSelection.NewLine(2);
switch(propertyType)
{
case PropertyTypeValue.GetterSetter :
prop = String.Concat( "/// <SUMMARY>\r\n/// ",
getterSetterComment,
" \r\n/// </SUMMARY>\r\npublic ",
propType,
" ",
propName,
" {\r\nget\r\n{\r\nreturn ",
propVariable,
";\r\n}\r\nset\r\n{\r\n",
propVariable,
" = value;\r\n}\r\n}");
break;
case PropertyTypeValue.Getter :
prop = String.Concat( "/// <SUMMARY>\r\n/// ",
getterComment,
" \r\n/// </SUMMARY>\r\npublic ",
propType,
" ",
propName,
" {\r\nget\r\n{\r\nreturn ",
propVariable,
";\r\n}\r\n}");
break;
case PropertyTypeValue.Setter :
prop = String.Concat( "/// <SUMMARY>\r\n/// ",
setterComment,
" \r\n/// </SUMMARY>\r\npublic ",
propType,
" ",
propName,
" {\r\nset\r\n{\r\n",
propVariable,
" = value;\r\n}\r\n}");
break;
}
txsSelection.Insert(prop,
(int)vsInsertFlags.vsInsertFlagsInsertAtEnd);
txsSelection.StartOfLine(
vsStartOfLineOptions.vsStartOfLineOptionsFirstText, true);
txsSelection.SmartFormat();
txsSelection.GotoLine(txsSelection.TopPoint.Line + 1, false);
txsSelection.EndOfLine(false);
}
catch(Exception)
{
throw;
}
}
I did an Options tab to set some parameters:
They are stored in the registry, that's why you have to use the setup to create useful keys. You can parameter the prefix, automatic comments, and you can choose if you want the buttons in a popup menu or not. To add an Options tab, you have to create a UserControl
class that implements the IDTToolsOptionsPage
interface.
Points of Interest
I hope this little add-in will help some guys. I believe there're some interesting concepts in here like Option tabs popup menus:
History
- 01/07/2005 - Version 1.0.0
- 02/09/2005 - Version 1.1.0:
- Main bug correction for property generation.
- Contextual labels correction.
- Prefix size optimization (thanks to Olivier).
- 02/09/2005 - Version 1.2.0:
- Bug correction for members without visibility (caught by vasiletomoiaga).
- 02/11/2005 - Version 1.3.0:
- Bug in the Options tab (caught by Stefan).
- 26/04/2005 - Version 1.4.0:
- Use summary from the member as comment for the property (thanks to Bj�rnar Sundsb�).