方法: ドメイン固有言語デザイナーを拡張する
DSL 定義の編集に使用するデザイナーを拡張できます。 作成できる拡張機能の種類として、メニュー コマンドの追加、ドラッグ操作とダブルクリック操作用のハンドラーの追加、および特定の型の値またはリレーションシップが変更されたときにトリガーされる規則があります。 式を Visual Studio Integration Extension (VSIX) のパッケージに格納し、他のユーザーに配布できます。
この機能のサンプル コードと詳細については、Visual Studio Visualization and Modeling SDK (VMSDK) の Web サイトを参照してください。
ソリューションの設定
拡張機能のコードを含むプロジェクトと、プロジェクトをエクスポートする VSIX プロジェクトを設定します。 ソリューションには、同じ VSIX に組み込まれた他のプロジェクトを含めることができます。
DSL デザイナー拡張ソリューションを作成するには
[クラス ライブラリ] プロジェクト テンプレートを使用して、新しいプロジェクトを作成します。 [新しいプロジェクト] ダイアログ ボックスで、[Visual C#] をクリックし、中央のウィンドウで [クラス ライブラリ] をクリックします。
このプロジェクトは、独自の拡張機能のコードを含みます。
VSIX プロジェクト テンプレートを使用し、新しいプロジェクトを作成します。 [新しいプロジェクト] ダイアログ ボックスで、[Visual C#] を展開し、[機能拡張] をクリックします。次に、中央のウィンドウで [VSIX プロジェクト] をクリックします。
[ソリューションに追加] を選択します。
Source.extension.vsixmanifest は VSIX マニフェスト エディターで開きます。
[コンテンツ] フィールドの上の [コンテンツの追加] をクリックします。
[コンテンツの追加] ダイアログ ボックスで、[コンテンツ タイプの選択] を [MEF コンポーネント] に設定し、[プロジェクト] をクラス ライブラリ プロジェクトに設定します。
[Select Editions] (エディションの選択) をクリックし、[Visual Studio Ultimate] がオンになっていることを確認します。
VSIX プロジェクトがソリューションのスタートアップ プロジェクトであることを確認します。
クラス ライブラリ プロジェクトで、次のアセンブリへの参照を追加します。
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、および他のコンピューターに拡張機能を配置するには、次の手順に従います:
VSIX プロジェクトの bin\*\*.vsix で VSIX インストール ファイルを見つけます。
次に、ターゲット コンピューターに、エクスプローラー (または) ファイル エクスプローラーでこのファイルをダブルクリックし、コピーします。
拡張機能がインストールされていることを確認するために、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);
}
}
}
}