前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >(翻译)LearnVSXNow! #12- “VsxLibrary” 和“HowToPackage”

(翻译)LearnVSXNow! #12- “VsxLibrary” 和“HowToPackage”

作者头像
明年我18
发布2019-09-18 11:35:32
4180
发布2019-09-18 11:35:32
举报
文章被收录于专栏:明年我18

前面的11篇文章涉及到了在VSX开发中最重要的知识,利用这些知识,我们已经可以开始开发VSPackage了。但是,还有很多重要的主题我们并没有涉及到(例如Package Load Key、部署和安装、属性页,自定义编辑器、项目树,文档窗口等等)。

通过前面这些文章的读者反馈来看,现在是进行下一个主题的时候了。但在这之前,让我先对比一下软件开发和潜水运动…

不仅仅我的昵称叫作DeepDiver,我本身也是一个海底潜水的爱好者。在匈牙利,我们只有能见度非常低的湖泊,对于潜水新手来说,在这些湖里面潜水可不是什么有趣的事情,但如果跟着潜水教练的话,即使是新手也会在能见度非常低的湖泊里找到乐趣。如果在能见度非常高的大海里潜水的话,即便没有教练跟着,你也会觉得自己能够潜的非常棒,但是一旦回到了湖里,你就会失去刚刚建立起来的自信。摆脱这一困境的方法是多学和多练,直到你变成一个潜水教练或者潜水高手。

我现在已经是一个潜水教练了,我们经常和其他人一起练习,并学习了很多理论基础(物理学和生理学)和实践技能(常规任务、自救、应急操作等等)。我们有一个”伙伴系统”:当潜水的时候,伙伴之间要相互帮助。现在,不管是在匈牙利的冰冷的湖里,还是在红海里,我都能潜的很自如。如果你非要问我更喜欢哪一个,我会告诉你这两个我都喜欢:喜欢红海的壮观,喜欢湖泊的冷静和挑战。

我为什么要和你们说这个呢?因为我感觉我自己在.NET编程方面是一个专家,但在VSX开发中却只是一个新手,但我希望自己两个都能精通,所以我还要做很多学习和练习。我猜如果有个“伙伴系统”的话,事情会变得简单很多,所以,如果你想的话,成为我的伙伴吧!

在这篇文章里我准备继续我们的系列。这一次我们创建一个新的package,这个package用于放置“How To”示例,但我并不是简单的添加示例,我还会把一些公用的代码抽取出来,变成可重用的托管代码,从而简化VSX的开发。

创建VsxLibrary和HowToPackage项目

在第10篇中,我创建了一个叫VsxToolset的类库项目,那个时候我想着这个东西可以作为将来开发VSX的真正工具集(甚至框架)的很好的基础。开发工具集有下面几个原则:

工具集里的类型必须减少噪音。我希望能够以更简单的方式访问VS IDE底层的COM互操作类型和方法。我会减少代码行数,加强类型安全,并能够利用托管代码的强大威力。例如第10篇中关于ActivityLog的处理。

COM类型转换成.NET的类型。VS IDE的对象模型是成熟的,但它是用COM技术实现的,由于COM技术和.NET有很大不同,所以对.NET开发人员来说会很不习惯。我想把VS IDE底层的service和类型转换成.NET的实现方式,这样.NET的很多特性和C#(甚至3.0)都可以用了。例如第10篇文章里关于OutputWindowOutputWindowPane的处理。

在可能的地方采用声明式的方法。有很多地方都可以用声明式的开发风格。.在这些地方,可以用NET提供的属性(Attribute)、反射和元数据等技术把命令式的的代码转换成声明式的代码。例如第10篇文章中OutputPaneDefinition类上面就声明了很多属性(Attribute)。

持续不断的文档。我喜欢能够使软件开发变得简单和有效率的框架,但是很多框架都没有很好的文档说明,需要花费很长的时间才能搞清楚框架怎么用。我可不想我这个工具集也这样,所以我打算在这个工具集的开发过程中,遵循下面的原则:

  1. 写好代码注释
  2. 为每个特性编写示例代码
  3. 写相关的文章来描述清楚特性的用法

我现在把VsxTools这个类库重命名为VsxLibrary了,并且同时创建了一个名为HowToPackage的项目,目的是在这个项目里可以演示以后的文章中涉及到的VSX开发方面的内容。这两个项目已经放到了CodePlex网站的LearnVSXNow项目上面了。

在这篇文章里,我们来做一下VsxLibraryHowToPackage的简单的概述。

Solution文件的结构

下载了源代码之后,你会看到如下的目录结构:

目录

内容

PackageStartupSamples

第2篇到第11文章里的示例代码,solution文件是 PackageStartupSamples.sln。我一般情况下不会在这个下面增加新的示例,当然如果有必要的话(例如新版本的VS SDK出来了,或者原来的例子有bug),我还是会做些更新的。

DiveDeeper.VsxLibrary

这个目录是VsxLibrary(原来叫作VsxTools)的主目录。这个solution文件下只包含这个类库和它的单元测试项目。

DiveDeeper.HowToPackage

HowToPackage用于演示一些例子。这个solution文件里包含了Package项目和单元测试项目,同时也把VsxLibrary项目添加了进来。

创建初始代码

用VSPackage向导创建了HowToPackage项目之后,我添加了一个简单的菜单和工具窗。我不太喜欢向导生成的类和常数的名字,所以我用重构工具改了一些名字。另外,我也删了向导生成的大部分的注释。

创建了VsxLibrary项目之后,我打算根据VS IDE中服务的类型来组织我的目录。例如我把Output Window相关的代码放到了OutputWindow目录下,把MessageBox相关的功能放到了VsUIShell目录下。

VsxLibrary概览

我说过VsxLibrary是从原来的VsxTools的基础上创建的,在后面的文章里我会继续向这个类库里添加新功能,但现在我先给你展示一下这个类库里已有的功能。

Utility类

VsxLibrary会在任何可能的地方使用声明式的代码风格。使用声明式风格的关键是使用attribute。所以我创建了一些attribute的抽象类型:BoolAttributeStringAttributeInt32AttributeUInt32Attribute。它们只有一个Value属性,这个属性的类型是和这几个attribute的名字相对应的,例如BoolAttribute的定义如下:

代码语言:javascript
复制
public abstract class BoolAttribute: Attribute{  private readonly bool _Value;    protected BoolAttribute(bool value)  {    _Value = value;  }    public bool Value  {    get { return _Value; }  }}

所有其他的attribute类继承上面这些相应的基类。如你所知,System.Attribute是不能用泛型的,所以我们不得不为每种attribute定义它的基类。

通过继承这些基类,只有一个属性的attribute(很多attitude都只有一个属性)就可以用很少的代码行来定义了:

代码语言:javascript
复制
[AttributeUsage(AttributeTargets.Class)]public sealed class PaneNameAttribute: StringAttribute{  public PaneNameAttribute(string value) : base(value)  {  }}  [AttributeUsage(AttributeTargets.Class)]public sealed class InitiallyVisibleAttribute: BoolAttribute{  public InitiallyVisibleAttribute(bool value): base(value)  {  }}

随着VsxLibrary的不断开发,其他的attribute基类也会添加进来。

另外一个utility类是VsxConverter静态类。在.NET基础类库和VS shell的互操作(interop)类之间,有很多含义一样但实现方式不同的常数或者枚举。例如在System.Windows.Forms命名空间下,有DialogResult这个枚举,相应的,在VS shell的互操作类里,用1到7来分别表示这个枚举值。再比如,Windows forms里有一个MessageBoxButtons的枚举,在VS Shell里,相应的有OLEMSGBUTTON这个枚举。

我认为.net开发人员比较喜欢.NET基础类库里的类型(枚举、常数等等),所以我创建了VsxConverter静态类,这个类负责在基础类型和VS Shell类型之间做转换。目前它已经有了几个方法了(会越来越多的),例如:

代码语言:javascript
复制
public static OLEMSGBUTTON ConvertToOleMsgButton(MessageBoxButtons buttons){  switch (buttons)  {    case MessageBoxButtons.AbortRetryIgnore:      return OLEMSGBUTTON.OLEMSGBUTTON_ABORTRETRYIGNORE;    case MessageBoxButtons.OKCancel:      return OLEMSGBUTTON.OLEMSGBUTTON_OKCANCEL;    case MessageBoxButtons.RetryCancel:      return OLEMSGBUTTON.OLEMSGBUTTON_RETRYCANCEL;    case MessageBoxButtons.YesNo:      return OLEMSGBUTTON.OLEMSGBUTTON_YESNO;    case MessageBoxButtons.YesNoCancel:      return OLEMSGBUTTON.OLEMSGBUTTON_YESNOCANCEL;    default:      return OLEMSGBUTTON.OLEMSGBUTTON_OK;  }}

我知道随着VsxLibrary的开发,会有越来越多的utility类型,一旦我添加了这些类,我会在相应的文章里介绍它们。

封装SVsUIShell服务

有很多服务提供了很多方法,例如SVsUIShell服务。我们可以在VsxLibrary类库里去封装它们。另外,SVsUIShell的某些方法,例如FindToolWindowCreateToolWindow,已经在MPF里封装了(可以通过Package类来访问它们)。

当我们创建了一个带有菜单的package之后(例如第3篇里讲到的),它同时创建了用于显示一个消息框的代码:

代码语言:javascript
复制
IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));Guid clsid = Guid.Empty;int result;Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(  uiShell.ShowMessageBox(    0,    ref clsid,    "SimpleCommand",    string.Format(CultureInfo.CurrentCulture, "Inside {0}.MenuItemCallback()",       this.ToString()),    string.Empty,    0,    OLEMSGBUTTON.OLEMSGBUTTON_OK,    OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST,    OLEMSGICON.OLEMSGICON_INFO,    0,        // false    out result));

这段代码很简单,但也很烦杂。所以为了让它更简单,我封装了VsUIShell。封装后,如果想显示消息框的话,只需要用下面简单的代码就行了:

代码语言:javascript
复制
VsUIShell.ShowMessageBox(  string.Format(CultureInfo.CurrentCulture,   "Inside {0}.MenuItemCallback()", this),  "SimpleCommand);

我是用非常简单的方式封装ShowMessageBox方法的。在VsUIShell类内部,我添加了一个叫作ShowMessageBoxInternal的私有方法,这个方法接受的是.NET基础类型,而不是VS Shell的类型:

代码语言:javascript
复制
private static DialogResult ShowMessageBoxInternal(string title, string message,   string helpFile, uint helpTopic, MessageBoxButtons buttons,   MessageBoxDefaultButton defButton, MessageBoxIcon icon, bool sysAlert){  Guid clsid = Guid.Empty;  int result;  ErrorHandler.ThrowOnFailure(UIShell.ShowMessageBox(             0,             ref clsid,             title,             message,              helpFile,             helpTopic,             VsxConverter.ConvertToOleMsgButton(buttons),             VsxConverter.ConvertToOleMsgDefButton(defButton),             VsxConverter.ConvertToOleMsgIcon(icon),             sysAlert ? 1 : 0,             out result));  return VsxConverter.Win32ResultToDialogResult(result);}

然后我创建了一些ShowMessageBox 的重载方法,就像MessageBox.Show系列方法那样。

总结

在这篇文章里,为了演示VSX开发,我创建了一个叫作HowToPackage的solution,并且打算在后面的文章里不断的扩展它。我在第9篇和第10篇文章里说过,如果能把VS Shell里的类型转换成.NET风格,并拥有CLR(例如元数据、attribute、泛型等等)和C#(例如扩展方法、LINQ等等)的特性,VSX开发就会变的简单很多。所以,在创建HowToPackage的同时,我也创建了一个很小的框架,叫作VsxLibrary

在下一篇文章里,我们将继续探讨VSX的开发。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2010-04-23 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 创建VsxLibrary和HowToPackage项目
  • Solution文件的结构
  • 创建初始代码
  • VsxLibrary概览
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档