Rozšíření vašeho DSL pomocí MEF
Jazyk DSL (Domain-Specific Language) můžete rozšířit pomocí rozhraní MEF (Managed Extensibility Framework). Vy nebo jiní vývojáři budete moct psát rozšíření pro DSL beze změny definice DSL a kódu programu. Mezi taková rozšíření patří příkazy nabídek, obslužné rutiny přetahování myší a ověřování. Uživatelé budou moct nainstalovat vaše DSL a pak pro něj volitelně nainstalovat rozšíření.
Kromě toho, když povolíte MEF ve své DSL, může být pro vás jednodušší psát některé funkce DSL, i když jsou všechny vytvořeny společně s DSL.
Další informace o MEF naleznete v tématu Rozhraní MEF (Managed Extensibility Framework) (MEF).
Povolení rozšíření DSL mef
Vytvořte novou složku s názvem MefExtension v projektu DslPackage . Přidejte do něj následující soubory:
Název souboru:
CommandExtensionVSCT.tt
Důležité
Nastavte identifikátor GUID v tomto souboru tak, aby byl stejný jako GUID CommandSetId definovaný v DslPackage\GeneratedCode\Constants.tt
<#@ 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" #>
Název souboru:
CommandExtensionRegistrar.tt
<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #> <#@ include file="DslPackage\CommandExtensionRegistrar.tt" #>
Název souboru:
ValidationExtensionEnablement.tt
<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #> <#@ include file="DslPackage\ValidationExtensionEnablement.tt" #>
Název souboru:
ValidationExtensionRegistrar.tt
Pokud přidáte tento soubor, musíte povolit ověřování v DSL pomocí alespoň jednoho přepínače v EditorValidation v Průzkumníku DSL.
<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #> <#@ include file="DslPackage\ValidationExtensionRegistrar.tt" #>
Název souboru:
PackageExtensionEnablement.tt
<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #> <#@ include file="DslPackage\PackageExtensionEnablement.tt" #>
Vytvořte novou složku s názvem MefExtension v projektu Dsl . Přidejte do něj následující soubory:
Název souboru:
DesignerExtensionMetaDataAttribute.tt
<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #> <#@ include file="Dsl\DesignerExtensionMetadataAttribute.tt" #>
Název souboru:
GestureExtensionEnablement.tt
<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #> <#@ include file="Dsl\GestureExtensionEnablement.tt" #>
Název souboru:
GestureExtensionController.tt
<#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #> <#@ include file="Dsl\GestureExtensionController.tt" #>
Do existujícího souboru s názvem DslPackage\Commands.vsct přidejte následující řádek:
<Include href="MefExtension\CommandExtensionVSCT.vsct"/>
Vložte řádek za existující
<Include>
direktivu.Otevřete DslDefinition.dsl.
V Průzkumníku DSL vyberte Editor\Validation.
V okno Vlastnosti se ujistěte, že alespoň jedna z vlastností s názvem Uses je
true
.Na panelu nástrojů Průzkumník řešení klepněte na příkaz Transformovat všechny šablony.
Podřízené soubory se zobrazují pod jednotlivými přidanmi soubory.
Sestavte a spusťte řešení, abyste ověřili, že stále funguje.
Váš DSL je teď povolený MEF. Jako rozšíření MEF můžete psát příkazy nabídek, obslužné rutiny gest a omezení ověřování. Tato rozšíření můžete v řešení DSL napsat společně s jiným vlastním kódem. Kromě toho můžete vy nebo jiní vývojáři psát samostatná rozšíření sady Visual Studio, která rozšiřují vaše DSL.
Vytvoření rozšíření pro DSL s podporou MEF
Pokud máte přístup k DSL s podporou MEF vytvořený sami nebo někým jiným, můžete pro něj napsat rozšíření. Rozšíření lze použít k přidání příkazů nabídky, gest obslužných rutin nebo omezení ověřování. K vytvoření těchto rozšíření použijete řešení rozšíření sady Visual Studio (VSIX). Řešení má dvě části: projekt knihovny tříd, který sestaví sestavení kódu, a projekt VSIX, který sestavení zabalí.
Vytvoření rozšíření DSL VSIX
Vytvořte nový projekt knihovny tříd.
V novém projektu přidejte odkaz na sestavení DSL.
Toto sestavení má obvykle název, který končí na ". Dsl.dll".
Pokud máte přístup k projektu DSL, najdete soubor sestavení v adresáři Dsl\bin\*
Pokud máte přístup k souboru DSL VSIX, můžete sestavení najít změnou přípony názvu souboru VSIX na ".zip". Dekomprese souboru .zip.
Přidejte odkazy na následující sestavení .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
Vytvořte nový projekt projektu VSIX.
V Průzkumník řešení klikněte pravým tlačítkem myši na projekt VSIX a zvolte Nastavit jako spouštěcí projekt.
V novém projektu open source.extension.vsixmanifest.
Klikněte na Přidat obsah. V dialogovém okně nastavte typ obsahu na součást MEF a zdrojový projekt na projekt knihovny tříd.
Přidejte odkaz VSIX na DSL.
Ve source.extension.vsixmanifest klepněte na tlačítko Přidat odkaz
V dialogovém okně klepněte na tlačítko Přidat datovou část a pak vyhledejte soubor VSIX DSL. Soubor VSIX je integrovaný v řešení DSL v DslPackage\bin\*.
To umožňuje uživatelům nainstalovat DSL a vaše rozšíření současně. Pokud už uživatel nainstaloval DSL, nainstaluje se jenom vaše rozšíření.
Zkontrolujte a aktualizujte další pole source.extension.vsixmanifest. Klikněte na Vybrat edice a ověřte, že jsou nastavené správné edice sady Visual Studio.
Přidejte kód do projektu knihovny tříd. Jako vodítko použijte příklady v další části.
Můžete přidat libovolný počet tříd příkazů, gest a ověřování.
Pokud chcete rozšíření otestovat, stiskněte klávesu F5. V experimentální instanci sady Visual Studio vytvořte nebo otevřete ukázkový soubor DSL.
Psaní rozšíření MEF pro seznamy DSLS
Rozšíření můžete napsat v projektu kódu sestavení samostatného řešení rozšíření DSL. MeF můžete také použít v projektu DslPackage jako pohodlný způsob, jak psát příkazy, gesta a ověřovací kód jako součást DSL.
Příkazy nabídky
Chcete-li napsat příkaz nabídky, definujte třídu, která implementuje ICommandExtension a předponu třídy s atributem definovaným v DSL s názvem YourDslCommandExtension
. Můžete napsat více než jednu třídu příkazů nabídky.
QueryStatus()
je volána vždy, když uživatel klikne pravým tlačítkem myši na diagram. Měl by zkontrolovat aktuální výběr a nastavit command.Enabled
, aby označil, kdy je příkaz použitelný.
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"; }
}
}
}
Obslužné rutiny gest
Obslužná rutina gest může zpracovávat objekty přetažené do diagramu odkudkoli, uvnitř nebo mimo Visual Studio. Následující příklad umožňuje uživateli přetáhnout soubory z Průzkumníka Windows do diagramu. Vytvoří prvky, které obsahují názvy souborů.
Můžete psát obslužné rutiny pro zpracování přetažení z jiných modelů DSL a modelů UML. Další informace naleznete v tématu Postupy: Přidání obslužné rutiny přetažení myší.
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();
}
}
}
}
Omezení ověřování
Metody ověřování jsou označeny atributemValidationExtension
, který je generován DSL, a také .ValidationMethodAttribute Metoda se může objevit v jakékoli třídě, která není označena atributem.
Další informace naleznete v tématu Ověřování v jazyce specifickém pro doménu.
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);
} } } }