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

  1. 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" #>
    
  2. 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" #>
    
  3. 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.

  4. Otevřete DslDefinition.dsl.

  5. V Průzkumníku DSL vyberte Editor\Validation.

  6. V okno Vlastnosti se ujistěte, že alespoň jedna z vlastností s názvem Uses je true.

  7. 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.

  8. 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

  1. Vytvořte nový projekt knihovny tříd.

  2. 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.

  3. 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

  4. Vytvořte nový projekt projektu VSIX.

  5. 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.

  6. V novém projektu open source.extension.vsixmanifest.

  7. 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.

  8. Přidejte odkaz VSIX na DSL.

    1. Ve source.extension.vsixmanifest klepněte na tlačítko Přidat odkaz

    2. 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í.

  9. 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.

  10. 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í.

  11. 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.

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);
} } } }