如何:扩展域特定语言设计器
可以对您使以编辑 DSL 定义的设计器进行扩展。可以创建包括添加菜单命令,添加拖动和双击姿态的处理程序会触发扩展的类型和规则,该值或关系的特定类型的更改。这些扩展可以打包到一个 Visual Studio 集成扩展 (VSIX) 中,并将它们分发给其他用户。
有关示例代码和更多关于此特性的信息,请参见 Visual Studio 可视化和建模 SDK (VMSDK) 网站。
设置解决方案。
设置包括您扩展的代码的项目和输出项目的 VSIX 项目。你的解决方案可以包含合并到同一 VSIX 中的其他项。
若要创建 DSL 设计器扩展解决方案
使用类库项目模板来创建一个新的项目。在**“新建项目”对话框中,单击“Visual C#”,然后在窗口中间单击“类库”**。
此项目将包含扩展编码。
使用 VSIX 项目模板创建新项目。在**“新建项目”对话框的中,依次扩展“Visual C#”,单击“扩展性”,最后在窗口中间选择“VSIX 项目”**。
选择 “添加到解决方案”。
Source.extension.vsixmanifest 将在 VSIX 清单编辑器中打开。
在“内容”字段上方,单击**“添加内容”**。
在**“添加内容”对话框中,设置“选择内容类型”** 到 MEF 组件中,设置**“项目”**到您的类库项目中。
单击**“选择版本”[Select Editions],并确保选中“Visual Studio Ultimate”** 。
确保 VSIX 项目是解决方案的 Startup 项目。
在类库项目中,添加引用到下面的程序集:
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 和到其他计算机,请执行以下步骤:
在 bin\*\*.vsix 的 VSIX 项目中查找 VSIX 安装文件
此文件复制到目标计算机,然后在Windows资源管理器(或文件Explorer),双击它。
Visual Studio “扩展管理器”打开以确认已安装扩展。
若要卸载这些扩展,请按以下步骤进行操作:
在 Visual Studio 中的**“工具”菜单上,单击“扩展管理器”**。
选中相应 扩展,然后删除。
添加快捷菜单命令
若要使快捷菜单命令显示在“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);
}
}
}
}