如何:扩展域特定语言设计器

可以对您使以编辑 DSL 定义的设计器进行扩展。可以创建包括添加菜单命令,添加拖动和双击姿态的处理程序会触发扩展的类型和规则,该值或关系的特定类型的更改。这些扩展可以打包到一个 Visual Studio 集成扩展 (VSIX) 中,并将它们分发给其他用户。

有关示例代码和更多关于此特性的信息,请参见 Visual Studio 可视化和建模 SDK (VMSDK) 网站

设置解决方案。

设置包括您扩展的代码的项目和输出项目的 VSIX 项目。你的解决方案可以包含合并到同一 VSIX 中的其他项。

若要创建 DSL 设计器扩展解决方案

  1. 使用类库项目模板来创建一个新的项目。在**“新建项目”对话框中,单击“Visual C#”,然后在窗口中间单击“类库”**。

    此项目将包含扩展编码。

  2. 使用 VSIX 项目模板创建新项目。在**“新建项目”对话框的中,依次扩展“Visual C#”,单击“扩展性”,最后在窗口中间选择“VSIX 项目”**。

    选择 “添加到解决方案”

    Source.extension.vsixmanifest 将在 VSIX 清单编辑器中打开。

  3. 在“内容”字段上方,单击**“添加内容”**。

  4. 在**“添加内容”对话框中,设置“选择内容类型”** 到 MEF 组件中,设置**“项目”**到您的类库项目中。

  5. 单击**“选择版本”[Select Editions],并确保选中“Visual Studio Ultimate”** 。

  6. 确保 VSIX 项目是解决方案的 Startup 项目。

  7. 在类库项目中,添加引用到下面的程序集:

    Microsoft.VisualStudio.CoreUtility

    Microsoft.VisualStudio.Modeling.Sdk.11.0

    Microsoft.VisualStudio.Modeling.Sdk.Diagrams.11.0

    Microsoft.VisualStudio.Modeling.Sdk.DslDefinition.11.0

    Microsoft.VisualStudio.Modeling.Sdk.Integration.11.0

    System.ComponentModel.Composition

    System.Drawing

    System.Drawing.Design

    System.Windows.Forms

测试和部署

若要测试本主题中的任何扩展,请生成并运行解决方案。Visual Studio 的实验实例将打开。在这种实例中,打开 DSL 解决方案。编辑 DslDefinition 关系图。扩展名行为可以查看。

若要部署扩展到主 Visual Studio 和到其他计算机,请执行以下步骤:

  1. 在 bin\*\*.vsix 的 VSIX 项目中查找 VSIX 安装文件

  2. 此文件复制到目标计算机,然后在Windows资源管理器(或文件Explorer),双击它。

    Visual Studio “扩展管理器”打开以确认已安装扩展。

若要卸载这些扩展,请按以下步骤进行操作:

  1. 在 Visual Studio 中的**“工具”菜单上,单击“扩展管理器”**。

  2. 选中相应 扩展,然后删除。

添加快捷菜单命令

若要使快捷菜单命令显示在“DSL 设计器图面”或在“DSL 资源管理器”窗口中,编写类似于下面的类。

该类必须实现 ICommandExtension 并具有 DslDefinitionModelCommandExtension 属性。

using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using Microsoft.VisualStudio.Modeling.Diagrams;
using Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnablement;
using Microsoft.VisualStudio.Modeling.DslDefinition;
using Microsoft.VisualStudio.Modeling.DslDefinition.ExtensionEnablement;
using Microsoft.VisualStudio.Modeling.DslDesigner;
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;

namespace Fabrikam.SimpleDslDesignerExtension
{
  /// <summary>
  /// Command extending the DslDesigner.
  /// </summary>
  [DslDefinitionModelCommandExtension] 
  public class MyDslDesignerCommand : ICommandExtension
  {
    /// <summary>
    /// Selection Context for this command
    /// </summary>
    [Import]
    IVsSelectionContext SelectionContext { get; set; }

    /// <summary>
    /// Is the command visible and active?
    /// This is called when the user right-clicks.
    /// </summary>
    public void QueryStatus(IMenuCommand command)
    {
      command.Visible = true;
      // Is there any selected DomainClasses in the Dsl explorer?
      command.Enabled = 
        SelectionContext.AtLeastOneSelected<DomainClass>();

      // Is there any selected ClassShape on the design surface?
      command.Enabled |= 
        (SelectionContext.GetCurrentSelection<ClassShape>()
                .Count() > 0);
    }
    /// <summary>
    /// Executes the command 
    /// </summary>
    /// <param name="command">Command initiating this action</param>
    public void Execute(IMenuCommand command)
    {
      ...
    }
    /// <summary>
    /// Label for the command
    /// </summary>
    public string Text
    {
      get { return "My Command"; }
    }
  }
}

处理鼠标笔势

该编码类似于菜单命令的编码。

[DslDefinitionModelGestureExtension]
 class MouseGesturesExtensions : IGestureExtension
 {
  /// <summary>
  /// Double-clicking on a shape representing a Domain model element displays this model element in a dialog box
  /// </summary>
  /// <param name="targetElement">Shape element on which the user has clicked</param>
  /// <param name="diagramPointEventArgs">event args for this double-click</param>
  public void OnDoubleClick(ShapeElement targetElement,
       DiagramPointEventArgs diagramPointEventArgs)
  {
   ModelElement modelElement = PresentationElementHelper.
        GetDslDefinitionModelElement(targetElement);
   if (modelElement != null)
   {
    MessageBox.Show(string.Format(
      "Double clicked on {0}", modelElement.ToString()), 
      "Model element double-clicked");
   }
  }


  /// <summary>
  /// Tells if the DslDesigner can consume the to-be-dropped information
  /// </summary>
  /// <param name="targetMergeElement">Shape on which we try to drop</param>
  /// <param name="diagramDragEventArgs">Drop event</param>
  /// <returns><c>true</c> if we can consume the to be dropped data, and <c>false</c> otherwise</returns>
  public bool CanDragDrop(ShapeElement targetMergeElement,
        DiagramDragEventArgs diagramDragEventArgs)
  {
   if (diagramDragEventArgs.Data.GetDataPresent(DataFormats.FileDrop))
   {
    diagramDragEventArgs.Effect = DragDropEffects.Copy;
    return true;
   }
   return false;
  }


  /// <summary>
  /// Processes the drop by displaying the dropped text
  /// </summary>
  /// <param name="targetMergeElement">Shape on which we dropped</param>
  /// <param name="diagramDragEventArgs">Drop event</param>
  public void OnDragDrop(ShapeElement targetDropElement, DiagramDragEventArgs diagramDragEventArgs)
  {
   if (diagramDragEventArgs.Data.GetDataPresent(DataFormats.FileDrop))
   {
    string[] droppedFiles = 
      diagramDragEventArgs.Data.
               GetData(DataFormats.FileDrop) as string[];
    MessageBox.Show(string.Format("Dropped text {0}",
        string.Join("\r\n", droppedFiles)), "Dropped Text");
   }
  }
 }

响应值更改

此处理程序需要一域模型以便可以正常工作。提供一个简单的域模型。

using System.Diagnostics;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.DslDefinition;
namespace Fabrikam.SimpleDslDesignerExtension
{
  /// <summary>
  /// Rule firing when the type of a domain model property is changed. The change is displayed
  /// in the debugger (Output window of the Visual Studio instance debugging this extension)
  /// </summary>
  [RuleOn(typeof(PropertyHasType))]
  public class DomainPropertyTypeChangedRule : RolePlayerChangeRule
  {
    /// <summary>
    /// Method called when the Type of a Domain Property 
    /// is changed by the user in a DslDefinition
    /// </summary>
    /// <param name="e"></param>
    public override void RolePlayerChanged(RolePlayerChangedEventArgs e)
    {
      // We are only interested in the type
      if (e.DomainRole.Id == PropertyHasType.TypeDomainRoleId)
      {
        PropertyHasType relationship = 
           e.ElementLink as PropertyHasType;
        DomainType newType = e.NewRolePlayer as DomainType;
        DomainType oldType = e.OldRolePlayer as DomainType;
        DomainProperty property = relationship.Property;

         // We write about the Type change in the debugguer
        Debug.WriteLine(string.Format("The type of the Domain property '{0}' of domain class '{1}' changed from '{2}' to '{3}'",
          property.Name,
          property.Class.Name,
          oldType.GetFullName(false),
          newType.GetFullName(false))
} }  }  );

以下代码实现简单模型。创建一个新 GUID 来代替占位符。

using System;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.DslDefinition;
namespace Fabrikam.SimpleDslDesignerExtension
{
    /// <summary>
    /// Simplest possible domain model 
    /// needed only for extension rules. 
    /// </summary>
    [DomainObjectId(SimpleDomainModelExtension.DomainModelId)]
    public class SimpleDomainModelExtension : DomainModel
    {
        // Id of this domain model extension 
        // Please replace this with a new GUID:
        public const string DomainModelId = 
                  "00000000-0000-0000-0000-000000000000";

        /// <summary>
        /// Constructor for the domain model extension
        /// </summary>
        /// <param name="store">Store in which the domain model will be loaded</param>
        public SimpleDomainModelExtension(Store store)
            : base(store, new Guid(SimpleDomainModelExtension.DomainModelId))
        {

        }

        /// <summary>
        /// Rules brought by this domain model extension
        /// </summary>
        /// <returns></returns>
        protected override System.Type[] GetCustomDomainModelTypes()
        {
            return new Type[] {
                     typeof(DomainPropertyTypeChangedRule)
                              };
        }
    }


    /// <summary>
    /// Provider for the DomainModelExtension
    /// </summary>
    [Export(typeof(DomainModelExtensionProvider))]    [ProvidesExtensionToDomainModel(typeof(DslDefinitionModelDomainModel))]
    public class SimpleDomainModelExtensionProvider 
          : DomainModelExtensionProvider
    {
        /// <summary>
        /// Extension model type
        /// </summary>
        public override Type DomainModelType
        {
            get
            {
                return typeof(SimpleDomainModelExtension);
            }
        }

    }
}