我编写第一个VS2005的Addin--Getter/Setter

最近公司用Castle框架开发系统,其中利用到了ORM-Nhibernate,和Hibernate类似,在hbm.xml生成对应的POJO类需要Getter/Setter来存取属性值。在eclipse中提供了getter & setter生成器,但是在VS2005中是没有的。网上搜索一番,决定自己编写Getter & Setter生成器插件(Addin)来替代重复的,无聊的Getter&Setter手工Coding.提高生产效率。步骤大致如下:

1)在VS2005中新建Addin项目,在其他项目类型->扩展性->Visual Studio外接程序,取名GetSetAddin;

2)按照VS2005提供的Wizard,一步一步Next,此处略。OK后,VS会自动将大部分的框架代码生成好,我们做的就是填空式的将代码写在Connect.cs文件的方法Exec中。此方法就是用户选择我们自定义的菜单或工具栏按钮时触发的方法。

3)OnConnection方法用来注册菜单的。大部分代码和注释VS已帮我们生成,默认情况下,会在工具栏(Tool)中注册菜单。在这里我把菜单注册到编辑(Edit)菜单下。代码如下:

        /// <summary>实现 IDTExtensibility2 接口的 OnConnection 方法。接收正在加载外接程序的通知。</summary>

        /// <param term='application'>宿主应用程序的根对象。</param>

        /// <param term='connectMode'>描述外接程序的加载方式。</param>

        /// <param term='addInInst'>表示此外接程序的对象。</param>

        /// <seealso class='IDTExtensibility2' />

        public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)

        {

            _applicationObject = (DTE2)application;

            _addInInstance = (AddIn)addInInst;

            if (connectMode == ext_ConnectMode.ext_cm_UISetup)

            {

                object[] contextGUIDS = new object[] { };

                Commands2 commands = (Commands2)_applicationObject.Commands;

                string toolsMenuName;



                try

                {

                    //若要将此命令移动到另一个菜单,则将“工具”一词更改为此菜单的英文版。

                    //  此代码将获取区域性,将其追加到菜单名中,然后将此命令添加到该菜单中。

                    //  您会在此文件中看到全部顶级菜单的列表

                    //  CommandBar.resx.

                    ResourceManager resourceManager = new ResourceManager("GetSetAddin.CommandBar", Assembly.GetExecutingAssembly());

                    CultureInfo cultureInfo = new System.Globalization.CultureInfo(_applicationObject.LocaleID);

                    string resourceName = String.Concat(cultureInfo.TwoLetterISOLanguageName, "Edit");

                    toolsMenuName = resourceManager.GetString(resourceName);

                }

                catch

                {

                    //我们试图查找“工具”一词的本地化版本,但未能找到。

                    //  默认值为 en-US 单词,该值可能适用于当前区域性。

                    toolsMenuName = "Edit";

                }



                //将此命令置于“工具”菜单上。

                //查找 MenuBar 命令栏,该命令栏是容纳所有主菜单项的顶级命令栏:

                Microsoft.VisualStudio.CommandBars.CommandBar menuBarCommandBar = ((Microsoft.VisualStudio.CommandBars.CommandBars)_applicationObject.CommandBars)["MenuBar"];



                //在 MenuBar 命令栏上查找“工具”命令栏:

                CommandBarControl toolsControl = menuBarCommandBar.Controls[toolsMenuName];

                CommandBarPopup toolsPopup = (CommandBarPopup)toolsControl;



                //如果希望添加多个由您的外接程序处理的命令,可以重复此 try/catch 块,

                //  只需确保更新 QueryStatus/Exec 方法,使其包含新的命令名。

                try

                {

                    //将一个命令添加到 Commands 集合:

                    Command command = commands.AddNamedCommand2(_addInInstance, "GetSetAddin", "生成Getter&&Setter(&G)", "生成Get/Set属性", true, 59, ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton);



                    //将对应于该命令的控件添加到“工具”菜单:

                    if ((command != null) && (toolsPopup != null))

                    {

                        command.AddControl(toolsPopup.CommandBar, 1);

                    }

                }

                catch (System.ArgumentException)

                {

                    //如果出现此异常,原因很可能是由于具有该名称的命令

                    //  已存在。如果确实如此,则无需重新创建此命令,并且

                    //  可以放心忽略此异常。

                }

            }

        }
这里有个问题需要注意,在VS2005中文版的开发环境下,默认生成在Tool菜单下注册自定义菜单,但是运行测试是看不到效果的,debug后才知道,取Tool菜单的Key是zhTools,而不是zh-CHSTools,所以在CommandBar.resx文件中加上zhTools(工具),在这里我加上的是zhEdit(编辑)。运行,OK,我们可以看到在编辑菜单下有个菜单【生成Getter&&Setter(&G)】,只是什么事情都没做。
4)在生成代码之前,需要获取到当前激活文档对象(FileCodeModel),像这样:
        private FileCodeModel ObtainCurrentFileCodeModel()

        {

            //获取当前正活动的文档

            Document activeDocument = _applicationObject.ActiveDocument;

            if (activeDocument != null)

            {

                ProjectItem projectItem = activeDocument.ProjectItem;

                if (projectItem != null)

                {

                    return projectItem.FileCodeModel;

                }

            }



            return null;

        }
获取后便可以遍历其中的元素,例如类,接口,属性,字段(变量),方法,命名空间,注释等等。下面的代码片断是递归获取所有类(在这里我们只需要获取类):
        private System.Collections.Generic.IList<CodeClass2> GetCodeClasses(CodeElements codeElements)

        {

            System.Collections.Generic.IList<CodeClass2> result = new System.Collections.Generic.List<CodeClass2>();



            foreach (CodeElement2 ce2 in codeElements)

            {

                switch (ce2.Kind)

                {

                    case vsCMElement.vsCMElementNamespace:

                        CodeNamespace cn = (CodeNamespace)ce2;

                        System.Collections.Generic.IList<CodeClass2> list = GetCodeClasses(cn.Members);

                        foreach (CodeClass2 clazz in list)

                        {

                            result.Add(clazz);

                        }

                        break;

                    case vsCMElement.vsCMElementClass:

                        CodeClass2 cc = (CodeClass2)ce2;

                        result.Add(cc);

                        break;

                    case vsCMElement.vsCMElementInterface:

                        break;

                    case vsCMElement.vsCMElementFunction:

                        break;

                    case vsCMElement.vsCMElementProperty:

                        break;

                    case vsCMElement.vsCMElementEvent:

                        break;

                    case vsCMElement.vsCMElementDelegate:

                        break;

                    case vsCMElement.vsCMElementStruct:

                        break;

                    case vsCMElement.vsCMElementEnum:

                        break;

                    case vsCMElement.vsCMElementAttribute:

                        break;

                    case vsCMElement.vsCMElementUsingStmt:

                        break;

                    case vsCMElement.vsCMElementVariable:

                        break;



                }

            }

            return result;

        }

好了,得到了当前文档的所有类,那么接下来就是生成Getter&Setter方法,在此Addin项目中我用的一个Form,在其中用一个Tree来遍历了当前文档的所有类,并将类中的所有变量加入到子节点,由用户来选择生成哪些Getter&Setter,其界面参考的是eclipse的Getter&Setter生成器。如图所示:
生成按钮的Getter&Setter生成代码片断如下:
        private void GenerateGetterAndSetter(TreeNode node, vsCMAccess vsCMA)

        {

            try

            {

                /* add getter & setter method */

                CodeVariable2 cv = (CodeVariable2)node.Tag;

                CodeClass2 cc = (CodeClass2)node.Parent.Tag;

                string name = cv.Name.TrimStart('_');

                name = name.Substring(0, 1).ToUpper() + name.Substring(1);

                string getter = node.FirstNode.Checked ? name : string.Empty;

                string setter = node.LastNode.Checked ? name : string.Empty;

                CodeProperty cp = cc.AddProperty(getter, setter, cv.Type, -cbPosition.SelectedIndex, vsCMA, null);

                EditPoint ep = null;

                if (!string.IsNullOrEmpty(getter))

                {

                    ep = cp.Getter.StartPoint.CreateEditPoint();

                    ep.ReplaceText(cp.Getter.EndPoint.CreateEditPoint(), "get {return " + cv.Name + ";}", (int)vsEPReplaceTextOptions.vsEPReplaceTextAutoformat);

                }

                if (!string.IsNullOrEmpty(setter))

                {

                    ep = cp.Setter.StartPoint.CreateEditPoint();

                    ep.ReplaceText(cp.Setter.EndPoint.CreateEditPoint(), "set {" + cv.Name + " = value;}", (int)vsEPReplaceTextOptions.vsEPReplaceTextAutoformat);

                }



                if (chkVirtual.Checked)

                {

                    ep = cp.StartPoint.CreateEditPoint();

                    string s = ep.GetText(50).Trim('/t', ' ');

                    int i = ep.LineCharOffset + s.IndexOf(' ');

                    ep.MoveToLineAndOffset(ep.Line, i);

                    ep.Insert(" virtual");

                }

            }

            catch

            {

            }

        }
5)好了,大部分功能已实现,补充一点,在Connec.cs的QueryStatus方法中来判断当前文档是否是cs文件(在这里,我只针对C#语言),如果是才将菜单激活,否则不可见。
        /// <summary>实现 IDTCommandTarget 接口的 QueryStatus 方法。此方法在更新该命令的可用性时调用</summary>

        /// <param term='commandName'>要确定其状态的命令的名称。</param>

        /// <param term='neededText'>该命令所需的文本。</param>

        /// <param term='status'>该命令在用户界面中的状态。</param>

        /// <param term='commandText'>neededText 参数所要求的文本。</param>

        /// <seealso class='Exec' />

        public void QueryStatus(string commandName, vsCommandStatusTextWanted neededText, ref vsCommandStatus status, ref object commandText)

        {

            if (neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)

            {

                if (commandName == "GetSetAddin.Connect.GetSetAddin")

                {

                    if (!IsCSFileOfCurrentDocument()) return;

                    status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;

                    return;

                }

            }

        }



        private bool IsCSFileOfCurrentDocument()

        {

            //获取当前正活动的文档

            Document activeDocument = _applicationObject.ActiveDocument;

            if (activeDocument != null)

                return activeDocument.FullName.EndsWith(".cs", StringComparison.CurrentCultureIgnoreCase);

            return false;

        }
结束语:运行它,呵呵,是不是很爽,再也不用一行一行的敲了。以上只是代码片断,可能有的代码注释的不是很详细,需要翻阅MSDN。这个Addin也是我写的第一个项目,有些地方可能不是很完美,在以后的版本中我会完善。本项目的代码比较多,没贴上来。如果你需要可以email我。或许有时间了我会把此项目上传到csdn资源中,请各位留意。
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值