방법: 도메인별 언어 디자이너 확장

DSL 정의를 편집하는 데 사용할 디자이너에 확장을 만들 수 있습니다.특정 형식의 값이나 관계 변경 시 개시되는 메뉴 명령 추가, 드래그 처리기, 더블 클릭 제스처, 규칙에 대한 처리기 추가를 포함시키는 확장 형식입니다.확장은 VSIX(Visual Studio Integration Extension)로 패키지하여 다른 사용자에게 배포할 수 있습니다.

샘플 코드 및 이 기능에 대한 자세한 내용은 Visual Studio VMSDK(Visualization and Modeling SDK) 웹 사이트를 참조하십시오.

솔루션 설정

확장 코드가 포함된 프로젝트 및 프로젝트를 내보내는 VSIX 프로젝트 설정합니다.솔루션에 동일한 VSIX로 통합되는 다른 프로젝트가 포함되어 있습니다.

DSL 디자이너 확장 솔루션을 만들려면

  1. 클래스 라이브러리 프로젝트 템플릿을 사용하여 새 프로젝트를 만듭니다.새 프로젝트 대화 상자에서 **Visual C#**을 클릭한 다음 가운데 창에서 클래스 라이브러리를 클릭합니다.

    이 프로젝트에는 확장 코드가 포함됩니다.

  2. VSIX 프로젝트 템플릿을 사용하여 새 프로젝트를 만듭니다.새 프로젝트 대화 상자에서 **Visual C#**을 확장하고 확장성을 클릭한 다음, 가운데 창에서 VSIX 프로젝트를 선택합니다.

    솔루션에 추가를 선택합니다.

    VSIX 매니페스트 편집기에서 Source.extension.vsixmanifest가 열립니다.

  3. 콘텐츠 필드 위에 있는 콘텐츠 추가를 클릭합니다.

  4. 콘텐츠 추가 대화 상자에서 콘텐츠 형식 선택MEF 구성 요소로 설정하고 프로젝트를 클래스 라이브러리 프로젝트로 설정합니다.

  5. 버전 선택을 클릭하고 Visual Studio Ultimate이 선택되어 있는지 확인합니다.

  6. VSIX 프로젝트가 솔루션의 시작 프로젝트인지 확인합니다.

  7. 클래스 라이브러리 프로젝트에서 다음 어셈블리에 참조를 추가합니다.

    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와 다른 컴퓨터에 확장을 배포하려면 다음 단계를 따르십시오.

  1. bin\*\*.vsix의 VSIX 프로젝트에서 VSIX 설치 파일을 찾습니다.

  2. 이 파일을 대상 컴퓨터에 복사 하 고 Windows 탐색기 (또는 파일 탐색기)를 두 번 클릭 합니다.

    Visual Studio 확장 관리자가 열려 확장이 설치되었는지 확인합니다.

확장을 제거하려면 다음 단계를 수행합니다.

  1. Visual Studio의 도구 메뉴에서 확장 관리자를 클릭합니다.

  2. 확장을 선택하여 삭제합니다.

바로 가기 메뉴 추가 명령

단축 메뉴 명령어가 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);
            }
        }

    }
}