Rozszerzanie DSL za pomocą MEF

Język specyficzny dla domeny (DSL) można rozszerzyć przy użyciu programu Managed Extensibility Framework (MEF). Ty lub inni deweloperzy będą mogli pisać rozszerzenia dla języka DSL bez zmiany definicji DSL i kodu programu. Takie rozszerzenia obejmują polecenia menu, programy obsługi przeciągania i upuszczania oraz walidację. Użytkownicy będą mogli zainstalować rozszerzenie DSL, a następnie opcjonalnie zainstalować dla niego rozszerzenia.

Ponadto po włączeniu funkcji MEF w języku DSL można łatwiej napisać niektóre funkcje dsl, nawet jeśli wszystkie są tworzone razem z DSL.

Aby uzyskać więcej informacji na temat mef, zobacz Managed Extensibility Framework (MEF).

Aby umożliwić rozszerzenie praw podmiotu danych przez mef

  1. Utwórz nowy folder o nazwie MefExtension w projekcie DslPackage . Dodaj do niego następujące pliki:

    Nazwa pliku: CommandExtensionVSCT.tt

    Ważne

    Ustaw identyfikator GUID w tym pliku na taki sam jak identyfikator CommandSetId identyfikatora GUID zdefiniowany w pliku 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" #>
    

    Nazwa pliku: CommandExtensionRegistrar.tt

    <#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #>
    <#@ include file="DslPackage\CommandExtensionRegistrar.tt" #>
    

    Nazwa pliku: ValidationExtensionEnablement.tt

    <#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #>
    <#@ include file="DslPackage\ValidationExtensionEnablement.tt" #>
    

    Nazwa pliku: ValidationExtensionRegistrar.tt

    W przypadku dodania tego pliku należy włączyć walidację w języku DSL przy użyciu co najmniej jednego przełącznika w edytorzeValidation w Eksploratorze DSL.

    <#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #>
    <#@ include file="DslPackage\ValidationExtensionRegistrar.tt" #>
    

    Nazwa pliku: PackageExtensionEnablement.tt

    <#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #>
    <#@ include file="DslPackage\PackageExtensionEnablement.tt" #>
    
  2. Utwórz nowy folder o nazwie MefExtension w projekcie Dsl . Dodaj do niego następujące pliki:

    Nazwa pliku: DesignerExtensionMetaDataAttribute.tt

    <#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #>
    <#@ include file="Dsl\DesignerExtensionMetadataAttribute.tt" #>
    

    Nazwa pliku: GestureExtensionEnablement.tt

    <#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #>
    <#@ include file="Dsl\GestureExtensionEnablement.tt" #>
    

    Nazwa pliku: GestureExtensionController.tt

    <#@ Dsl processor="DslDirectiveProcessor" requires="fileName='..\..\Dsl\DslDefinition.dsl'" #>
    <#@ include file="Dsl\GestureExtensionController.tt" #>
    
  3. Dodaj następujący wiersz do istniejącego pliku o nazwie DslPackage\Commands.vsct:

    <Include href="MefExtension\CommandExtensionVSCT.vsct"/>
    

    Wstaw wiersz po istniejącej <Include> dyrektywie.

  4. Otwórz plik DslDefinition.dsl.

  5. W Eksploratorze DSL wybierz pozycję Editor\Validation.

  6. W okno Właściwości upewnij się, że co najmniej jedna z właściwości o nazwie Uses to true.

  7. Na pasku narzędzi Eksplorator rozwiązań kliknij pozycję Przekształć wszystkie szablony.

    Pliki zależne są wyświetlane pod każdym z dodanych plików.

  8. Skompiluj i uruchom rozwiązanie, aby sprawdzić, czy nadal działa.

Twoje rozszerzenie DSL jest teraz włączone za pomocą protokołu MEF. Polecenia menu, procedury obsługi gestów i ograniczenia walidacji można pisać jako rozszerzenia MEF. Te rozszerzenia można napisać w rozwiązaniu DSL razem z innym kodem niestandardowym. Ponadto Ty lub inni deweloperzy mogą pisać oddzielne rozszerzenia programu Visual Studio, które rozszerzają rozszerzenie DSL.

Tworzenie rozszerzenia dla rozszerzenia DSL z obsługą protokołu MEF

Jeśli masz dostęp do rozszerzenia DSL z obsługą protokołu MEF utworzonego samodzielnie lub innej osoby, możesz napisać dla niego rozszerzenia. Rozszerzenia mogą służyć do dodawania poleceń menu, procedur obsługi gestów lub ograniczeń walidacji. Aby utworzyć te rozszerzenia, należy użyć rozwiązania rozszerzenia programu Visual Studio (VSIX). Rozwiązanie ma dwie części: projekt biblioteki klas, który kompiluje zestaw kodu, oraz projekt VSIX, który pakuje zestaw.

Aby utworzyć rozszerzenie DSL VSIX

  1. Utwórz nowy projekt Biblioteka klas.

  2. W nowym projekcie dodaj odwołanie do zestawu DSL.

    • Ten zestaw zwykle ma nazwę kończącą się ciągiem ". Dsl.dll".

    • Jeśli masz dostęp do projektu DSL, możesz znaleźć plik zestawu w katalogu Dsl\bin\*

    • Jeśli masz dostęp do pliku DSL VSIX, możesz znaleźć zestaw, zmieniając rozszerzenie nazwy pliku VSIX na ".zip". Zdekompresuj plik zip.

  3. Dodaj odwołania do następujących zestawów .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. Utwórz nowy projekt VSIX .

  5. W Eksplorator rozwiązań kliknij prawym przyciskiem myszy projekt VSIX i wybierz polecenie Ustaw jako projekt startowy.

  6. W nowym projekcie open source.extension.vsixmanifest.

  7. Kliknij pozycję Dodaj zawartość. W oknie dialogowym ustaw wartość Typ zawartości na składnik MEF, a projekt źródłowy na projekt biblioteki klas.

  8. Dodaj odwołanie VSIX do rozszerzenia DSL.

    1. W pliku source.extension.vsixmanifest kliknij pozycję Dodaj odwołanie

    2. W oknie dialogowym kliknij pozycję Dodaj ładunek , a następnie znajdź plik VSIX rozszerzenia DSL. Plik VSIX jest wbudowany w rozwiązanie DSL w pliku DslPackage\bin\*.

      Dzięki temu użytkownicy mogą jednocześnie instalować rozszerzenie DSL i rozszerzenie. Jeśli użytkownik zainstalował już rozszerzenie DSL, zostanie zainstalowane tylko rozszerzenie.

  9. Przejrzyj i zaktualizuj inne pola pliku source.extension.vsixmanifest. Kliknij pozycję Wybierz wersje i sprawdź, czy ustawiono poprawne wersje programu Visual Studio.

  10. Dodaj kod do projektu biblioteki klas. Skorzystaj z przykładów w następnej sekcji jako przewodnika.

    Można dodać dowolną liczbę klas poleceń, gestów i walidacji.

  11. Aby przetestować rozszerzenie, naciśnij klawisz F5. W eksperymentalnym wystąpieniu programu Visual Studio utwórz lub otwórz przykładowy plik DSL.

Pisanie rozszerzeń MEF dla rozszerzeń DSL

Rozszerzenia można pisać w projekcie kodu zestawu oddzielnego rozwiązania rozszerzenia DSL. W projekcie DslPackage można również używać mef, jako wygodnego sposobu pisania poleceń, gestów i kodu weryfikacji w ramach rozszerzenia DSL.

Aby napisać polecenie menu, zdefiniuj klasę, która implementuje ICommandExtension i prefiks klasy z atrybutem zdefiniowanym w języku DSL o nazwie YourDslCommandExtension. Można napisać więcej niż jedną klasę poleceń menu.

QueryStatus() jest wywoływany za każdym razem, gdy użytkownik kliknie diagram prawym przyciskiem myszy. Powinien sprawdzić bieżące zaznaczenie i ustawić, command.Enabled aby wskazać, kiedy polecenie ma zastosowanie.

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

Programy obsługi gestów

Procedura obsługi gestów może zajmować się obiektami przeciąganymi na diagram z dowolnego miejsca, wewnątrz lub poza programem Visual Studio. Poniższy przykład umożliwia użytkownikowi przeciąganie plików z Eksploratora Windows do diagramu. Tworzy elementy zawierające nazwy plików.

Programy obsługi można pisać w celu obsługi przeciągania z innych modeli DSL i modeli UML. Aby uzyskać więcej informacji, zobacz How to: Add a Drag-and-Drop Handler (Instrukcje: dodawanie procedury obsługi przeciągania i upuszczania).

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

Ograniczenia walidacji

Metody weryfikacji są oznaczone przez ValidationExtension atrybut generowany przez ROZSZERZENIE DSL, a także przez ValidationMethodAttribute. Metoda może pojawić się w dowolnej klasie, która nie jest oznaczona przez atrybut.

Aby uzyskać więcej informacji, zobacz Walidacja w języku specyficznym dla domeny.

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