使用 MEF 扩展 DSL
使用 managed extensibility framework, (MEF)则可以 exntend 这种域特定语言 (DSL)。您或其他开发人员能够 DSL 的编写扩展,而无需更改 DSL 定义和程序代码。此类扩展包括菜单命令、拖放处理程序和验证。用户可以安装 DSL,可以选择然后进行安装的扩展。
此外,那么,当您启用 DSL 时 MEF,可以轻松地编写某些 DSL 的功能,因此,即使它们都使用 DSL 一起编译。
有关 MEF 的更多信息,请参见 Managed Extensibility Framework (MEF)。
使 DSL 由 MEF 扩展
创建名为在 DslPackage 项目内的 MefExtension 的新文件夹。将以下文件添加到:
文件名
文件内容
CommandExtensionVSCT.tt
重要事项设置此文件的 GUID 为在 DslPackage \GeneratedCode\Constants .tt 定义相同的 GUID CommandSetId 相同<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #> <# // CmdSet Guid must be defined before master template is included // This Guid must be kept synchronized with the CommandSetId Guid in Constants.tt Guid guidCmdSet = new Guid ("00000000-0000-0000-0000-000000000000"); string menuidCommandsExtensionBaseId="0x4000"; #> <#@ include file="DslPackage\CommandExtensionVSCT.tt" #>
CommandExtensionRegistrar.tt
<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #> <#@ include file="DslPackage\CommandExtensionRegistrar.tt" #>
ValidationExtensionEnablement.tt
<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #> <#@ include file="DslPackage\ValidationExtensionEnablement.tt" #>
ValidationExtensionRegistrar.tt
如果您将此文件,必须在 DSL 的验证使用至少有一个在 编辑 \Validation 切换在 DSL 资源管理器。
<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #> <#@ include file="DslPackage\ValidationExtensionRegistrar.tt" #>
PackageExtensionEnablement.tt
<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #> <#@ include file="DslPackage\PackageExtensionEnablement.tt" #>
创建名为在 Dsl 项目内的 MefExtension 的新文件夹。将以下文件添加到:
文件名
Content
DesignerExtensionMetaDataAttribute.tt
<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #> <#@ include file="Dsl\DesignerExtensionMetadataAttribute.tt" #>
GestureExtensionEnablement.tt
<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #> <#@ include file="Dsl\GestureExtensionEnablement.tt" #>
GestureExtensionController.tt
<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #> <#@ include file="Dsl\GestureExtensionController.tt" #>
将下行添加到名为 DslPackage\Commands.vsct的现有文件:
<Include href="MefExtension\CommandExtensionVSCT.vsct"/>
插入到现有 <Include> 指令之后的行。
打开 DslDefinition.dsl。
在 DSL 资源管理器中,选择 编辑 \Validation。
在 " 属性 " 窗口中,确保名为 使用… 的至少一个属性是 true。
在解决方案资源管理器工具栏中,单击 转换所有模板。
附属文件在每个文件下出现。添加。
生成并运行解决方案验证它仍可正常工作。
DSL 现在 MEF 启用。您可以编写菜单命令、笔势处理程序并验证约束为 MEF 扩展。您与其他自定义代码。可以编写在 DSL 解决方案的这些扩展。此外,您或其他开发人员可以扩展 DSL 编写单独 Visual Studio 扩展访问。
创建一 MEF 启用 DSL 的扩展
如果可以访问一 MEF 启用 DSL 单独创建的或别人的,则可以编写扩展。扩展可用于添加菜单命令、笔势处理程序或验证约束。若要创作这些扩展,则使用一个 Visual Studio 扩展 (vsix) 解决方案。该解决方案由两部分组成:生成代码程序集的类库项目和 VSIX 项目该包程序集。
创建 DSL VSIX 扩展
创建一个新类库项目。为此,请在 新项目 对话框、选择 Visual Basic 或 Visual C# 然后选择 类库。
在新类库项目中,添加对 DSL 的程序集。
此程序集通常有一个的名称末尾与 “。Dsl.dll”。
如果可以访问 DSL 项目中,可以找到程序集文件在目录下 Dsl\bin\*
如果可以访问 DSL VSIX 文件,您可以通过更改 VSIX 文件的文件扩展名找到程序集为 “.zip”。解压缩的 .zip 文件。
添加对下列 .NET 程序集的引用:
Microsoft.VisualStudio.Modeling.Sdk.11.0.dll
Microsoft.VisualStudio.Modeling.Sdk.Diagrams.11.0.dll
Microsoft.VisualStudio.Modeling.Sdk.Shell.11.0.dll
System.ComponentModel.Composition.dll
System.Windows.Forms.dll
创建一个 VSIX 项目处于同一解决方案。为此,请在 新项目 对话框中,展开 Visual Basic 或 Visual C#,单击 扩展性,然后选择 VSIX 项目。
在解决方案资源管理器中,右击 VSIX 项目然后单击 设置为启动项目。
在新项目中,打开 source.extension.vsixmanifest。
单击**“添加内容”**。在对话框中,设置 内容类型 到 MEF 组件和 源项目 到类库项目。
将 VSIX 对 DSL。
在 source.extension.vsixmanifest,单击 添加引用
在对话框中,单击 添加负载 然后查找 DSL 的 VSIX 文件。VSIX 文件在 DSL 解决方案后,在 DslPackage\bin\*。
这可让用户同时安装 DSL 和该扩展。如果用户已经安装了 DSL,因此,只有将安装扩展。
检查并更新 source.extension.vsixmanifest的其他字段。单击 选择编辑器 并验证正确的 Visual Studio 编辑器设置。
将代码添加到类库项目。使用示例在下一节中作为参考。
可以将任意数量的命令、笔势和验证类。
若要测试扩展,请按 F5。在 Visual Studio的实验实例中,创建或打开 DSL 的一个示例文件。
编写 DSL 的 MEF 扩展
您可以在单独的 DSL 扩展解决方案程序集中的代码项目中编写扩展。作为 DSL 的一部分,则在 DslPackage 项目中使用 MEF,,一种简便方法编写顺序,笔势和验证代码。
菜单命令
编写菜单命令,定义实现 ICommandExtension 的类并将类前缀与 DSL 定义的属性,名为 TheDslCommandExtension。您可以编写多个菜单命令类。
QueryStatus() 调用,只要用户右击关系图。它应检查当前选定内容和设置 command.Enabled 指示命令时适用。
using System.ComponentModel.Composition;
using System.Linq;
using Company.MyDsl; // My DSL
using Company.MyDsl.ExtensionEnablement; // My DSL
using Microsoft.VisualStudio.Modeling; // Transactions
using Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnablement; // IVsSelectionContext
using Microsoft.VisualStudio.Modeling.ExtensionEnablement; // ICommandExtension
namespace MyMefExtension
{
// Defined in Dsl\MefExtension\DesignerExtensionMetaDataAttribute.cs:
[MyDslCommandExtension]
public class MyCommandClass : ICommandExtension
{
/// <summary>
/// Provides access to current document and selection.
/// </summary>
[Import]
IVsSelectionContext SelectionContext { get; set; }
/// <summary>
/// Called when the user selects this command.
/// </summary>
/// <param name="command"></param>
public void Execute(IMenuCommand command)
{
// Transaction is required if you want to update elements.
using (Transaction t = SelectionContext.CurrentStore
.TransactionManager.BeginTransaction("fix names"))
{
foreach (ExampleShape shape in SelectionContext.CurrentSelection)
{
ExampleElement element = shape.ModelElement as ExampleElement;
element.Name = element.Name + " !";
}
t.Commit();
}
}
/// <summary>
/// Called when the user right-clicks the diagram.
/// Determines whether the command should appear.
/// This method should set command.Enabled and command.Visible.
/// </summary>
/// <param name="command"></param>
public void QueryStatus(IMenuCommand command)
{
command.Enabled =
command.Visible = (SelectionContext.CurrentSelection.OfType<ExampleShape>().Count() > 0);
}
/// <summary>
/// Called when the user right-clicks the diagram.
/// Determines the text of the command in the menu.
/// </summary>
public string Text
{
get { return "My menu command"; }
}
}
}
笔势处理程序
笔势处理程序可以处理对象拖动到中的任何位置,内部或外部 Visual Studio的关系图上。下面的示例允许用户从 Windows 资源管理器中拖动文件在关系图上。它创建包含文件名称的元素。
可以处理的处理程序从其他 DSL 模型和 UML 模型的拖动。有关更多信息,请参见 如何:添加拖放处理程序。
using System.ComponentModel.Composition;
using System.Linq;
using Company.MyDsl;
using Company.MyDsl.ExtensionEnablement;
using Microsoft.VisualStudio.Modeling; // Transactions
using Microsoft.VisualStudio.Modeling.Diagrams;
using Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnablement;
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;
namespace MefExtension
{
[MyDslGestureExtension]
class MyGestureExtension : IGestureExtension
{
public void OnDoubleClick(ShapeElement targetElement, DiagramPointEventArgs diagramPointEventArgs)
{
System.Windows.Forms.MessageBox.Show("double click!");
}
/// <summary>
/// Called when the user drags anything over the diagram.
/// Return true if the dragged object can be dropped on the current target.
/// </summary>
/// <param name="targetMergeElement">The shape or diagram that the mouse is currently over</param>
/// <param name="diagramDragEventArgs">Data about the dragged element.</param>
/// <returns></returns>
public bool CanDragDrop(ShapeElement targetMergeElement, DiagramDragEventArgs diagramDragEventArgs)
{
// This handler only allows items to be dropped onto the diagram:
return targetMergeElement is MefDsl2Diagram &&
// And only accepts files dragged from Windows Explorer:
diagramDragEventArgs.Data.GetFormats().Contains("FileNameW");
}
/// <summary>
/// Called when the user drops an item onto the diagram.
/// </summary>
/// <param name="targetDropElement"></param>
/// <param name="diagramDragEventArgs"></param>
public void OnDragDrop(ShapeElement targetDropElement, DiagramDragEventArgs diagramDragEventArgs)
{
MefDsl2Diagram diagram = targetDropElement as MefDsl2Diagram;
if (diagram == null) return;
// This handler only accepts files dragged from Windows Explorer:
string[] draggedFileNames = diagramDragEventArgs.Data.GetData("FileNameW") as string[];
if (draggedFileNames == null || draggedFileNames.Length == 0) return;
using (Transaction t = diagram.Store.TransactionManager.BeginTransaction("file names"))
{
// Create an element to represent each file:
foreach (string fileName in draggedFileNames)
{
ExampleElement element = new ExampleElement(diagram.ModelElement.Partition);
element.Name = fileName;
// This method of adding the new element allows the position
// of the shape to be specified:
ElementGroup group = new ElementGroup(element);
diagram.ElementOperations.MergeElementGroupPrototype(
diagram, group.CreatePrototype(), PointD.ToPointF(diagramDragEventArgs.MousePosition));
}
t.Commit();
}
}
}
}
验证约束
验证方法指示由由 DSL 生成的 ValidationExtension 属性,以及 ValidationMethodAttribute。方法可以出现在未由属性指示的任何类。
有关更多信息,请参见 域特定语言中的验证。
using Company.MyDsl;
using Company.MyDsl.ExtensionEnablement;
using Microsoft.VisualStudio.Modeling.Validation;
namespace MefExtension
{
class MyValidationExtension // Can be any class.
{
// SAMPLE VALIDATION METHOD.
// All validation methods have the following attributes.
// Specific to the extended DSL:
[MyDslValidationExtension]
// Determines when validation is applied:
[ValidationMethod(
ValidationCategories.Save
| ValidationCategories.Open
| ValidationCategories.Menu)]
/// <summary>
/// When validation is executed, this method is invoked
/// for every element in the model that is an instance
/// of the second parameter type.
/// </summary>
/// <param name="context">For reporting errors</param>
/// <param name="elementToValidate"></param>
private void ValidateClassNames
(ValidationContext context,
// Type determines to what elements this will be applied:
ExampleElement elementToValidate)
{
// Write code here to test values and links.
if (elementToValidate.Name.IndexOf(' ') >= 0)
{
// Log any unacceptable values:
context.LogError(
// Description:
"Name must not contain spaces"
// Error code unique to this type of error:
, "MyDsl001"
// Element to highlight when user double-clicks error:
, elementToValidate);
} } } }
请参见
概念
Managed Extensibility Framework (MEF)