다음을 통해 공유


프로그램 코드에서 모델 탐색 및 업데이트

모델 요소를 만들고 삭제하고, 해당 속성을 설정하고, 요소 간에 링크를 만들고 삭제하는 코드를 작성할 수 있습니다. 트랜잭션 내에서 모든 변경이 이루어져야 합니다. 다이어그램에서 요소를 볼 경우 다이어그램은 트랜잭션이 끝날 때 자동으로 "고정"됩니다.

예제 DSL 정의

다음은 이 항목의 예제에 대한 DslDefinition.dsl의 주요 부분입니다.

DSL 정의 다이어그램 - 패밀리 트리 모델

이 모델은 다음 DSL의 인스턴스입니다.

튜더 패밀리 트리 모델

참조 및 네임스페이스

이 항목에서 코드를 실행하려면 다음을 참조해야 합니다.

Microsoft.VisualStudio.Modeling.Sdk.11.0.dll

코드는 다음 네임스페이스를 사용합니다.

using Microsoft.VisualStudio.Modeling;

또한 DSL이 정의된 것과 다른 프로젝트에서 코드를 작성하는 경우 Dsl 프로젝트에서 빌드된 어셈블리를 가져와야 합니다.

속성

DSL 정의에서 정의하는 도메인 속성은 프로그램 코드에서 액세스할 수 있는 속성이 됩니다.

Person henry = ...;

if (henry.BirthDate < 1500) ...

if (henry.Name.EndsWith("VIII")) ...

속성을 설정하려면 트랜잭션 내에서 설정해야 합니다.

henry.Name = "Henry VIII";

DSL 정의에서 속성의 종류계산된 경우 설정할 수 없습니다. 자세한 내용은 계산 및 사용자 지정 스토리지 속성을 참조하세요.

관계

DSL 정의에서 정의하는 도메인 관계는 관계의 각 끝에 있는 클래스에 하나씩 속성 쌍이 됩니다. 속성의 이름은 DslDefinition 다이어그램에 관계의 각 측면에 있는 역할의 레이블로 표시됩니다. 역할의 곱셈에 따라 속성의 형식은 관계의 다른 쪽 끝에 있는 클래스 또는 해당 클래스의 컬렉션입니다.

foreach (Person child in henry.Children) { ... }

FamilyTreeModel ftree = henry.FamilyTreeModel;

관계의 반대쪽 끝에 있는 속성은 항상 상호 속성입니다. 링크를 만들거나 삭제하면 두 요소의 역할 속성이 업데이트됩니다. 다음 식(확장 사용)은 예제의 System.LinqParentsHaveChildren 관계에 대해 항상 true입니다.

(Person p) => p.Children.All(child => child.Parents.Contains(p))

&& p.Parents.All(parent => parent.Children.Contains(p));

ElementLinks. 관계는 도메인 관계 유형의 인스턴스인 링크라는 모델 요소로도 표시됩니다. 링크에는 항상 하나의 원본 요소와 하나의 대상 요소가 있습니다. 원본 요소와 대상 요소는 같을 수 있습니다.

링크 및 해당 속성에 액세스할 수 있습니다.

ParentsHaveChildren link = ParentsHaveChildren.GetLink(henry, edward);

// This is now true:

link == null || link.Parent == henry && link.Child == edward

기본적으로 관계의 인스턴스는 둘 이상의 모델 요소 쌍을 연결할 수 없습니다. DSL 정의에서 관계에 대한 Allow Duplicates 플래그가 true인 경우에는 두 개 이상의 링크가 있을 수 있으므로 GetLinks를 사용해야 합니다.

foreach (ParentsHaveChildren link in ParentsHaveChildren.GetLinks(henry, edward)) { ... }

링크에 액세스하는 다른 방법도 있습니다. 다음은 그 예입니다.

foreach (ParentsHaveChildren link in ParentsHaveChildren.GetLinksToChildren(henry)) { ... }

숨겨진 역할. DSL 정의에서 Is 속성 생성이 특정 역할에 대해 false 이면 해당 역할에 해당하는 속성이 생성되지 않습니다. 그러나 여전히 링크에 액세스하고 관계의 메서드를 사용하여 링크를 트래버스할 수 있습니다.

foreach (Person p in ParentsHaveChildren.GetChildren(henry)) { ... }

가장 자주 사용되는 예제는 PresentationViewsSubject 모델 요소를 다이어그램에 표시하는 셰이프에 연결하는 관계입니다.

PresentationViewsSubject.GetPresentation(henry)[0] as PersonShape

요소 디렉터리

요소 디렉터리를 사용하여 저장소의 모든 요소에 액세스할 수 있습니다.

store.ElementDirectory.AllElements

다음과 같은 요소를 찾는 메서드도 있습니다.

store.ElementDirectory.FindElements(Person.DomainClassId);

store.ElementDirectory.GetElement(elementId);

클래스 정보 액세스

DSL 정의의 클래스, 관계 및 기타 측면에 대한 정보를 가져올 수 있습니다. 다음은 그 예입니다.

DomainClassInfo personClass = henry.GetDomainClass();

DomainPropertyInfo birthProperty =

personClass.FindDomainProperty("BirthDate")

DomainRelationshipInfo relationship =

link.GetDomainRelationship();

DomainRoleInfo sourceRole = relationship.DomainRole[0];

모델 요소의 상위 클래스는 다음과 같습니다.

  • ModelElement - 모든 요소와 관계는 ModelElements입니다.

  • ElementLink - 모든 관계는 ElementLinks입니다.

트랜잭션 내에서 변경 수행

프로그램 코드가 스토어의 모든 항목을 변경할 때마다 트랜잭션 내에서 변경해야 합니다. 이는 모든 모델 요소, 관계, 셰이프, 다이어그램 및 해당 속성에 적용됩니다. 자세한 내용은 Transaction를 참조하세요.

트랜잭션을 관리하는 가장 효율적인 방법은 try...catch 문 내에 using 문을 포함하여 사용하는 것입니다.

Store store; ...
try
{
  using (Transaction transaction =
    store.TransactionManager.BeginTransaction("update model"))
    // Outermost transaction must always have a name.
  {
    // Make several changes in Store:
    Person p = new Person(store);
    p.FamilyTreeModel = familyTree;
    p.Name = "Edward VI";
    // end of changes to Store

    transaction.Commit(); // Don't forget this!
  } // transaction disposed here
}
catch (Exception ex)
{
  // If an exception occurs, the Store will be
  // rolled back to its previous state.
}

한 트랜잭션 내에서 원하는 수의 변경을 수행할 수 있습니다. 활성 트랜잭션 내에서 새 트랜잭션을 열 수 있습니다.

변경 내용을 영구적으로 적용하려면 트랜잭션이 삭제되기 전에 해당 트랜잭션을 삭제해야 합니다 Commit . 트랜잭션 내에서 catch되지 않은 예외가 발생하면 스토어가 변경되기 전에 해당 상태로 다시 설정됩니다.

모델 요소 만들기

이 예제에서는 기존 모델에 요소를 추가합니다.

FamilyTreeModel familyTree = ...; // The root of the model.
using (Transaction t =
    familyTree.Store.TransactionManager
    .BeginTransaction("update model"))
{
  // Create a new model element
  // in the same partition as the model root:
  Person edward = new Person(familyTree.Partition);
  // Set its embedding relationship:
  edward.FamilyTreeModel = familyTree;
          // same as: familyTree.People.Add(edward);
  // Set its properties:
  edward.Name = "Edward VII";
  t.Commit(); // Don't forget this!
}

이 예제에서는 요소를 만드는 방법에 대한 다음 필수 사항을 보여 줍니다.

  • 스토어의 특정 파티션에 새 요소를 만듭니다. 모델 요소 및 관계의 경우 셰이프가 아니라 일반적으로 기본 파티션입니다.

  • 포함 관계의 대상을 지정합니다. 이 예제의 DslDefinition에서 각 인물은 FamilyTreeHasPeople 관계를 포함하는 대상이어야 합니다. 이를 위해 Person 개체의 FamilyTreeModel 역할 속성을 설정하거나 FamilyTreeModel 개체의 People 역할 속성에 Person을 추가할 수 있습니다.

  • 새 요소의 속성, 특히 DslDefinition에서 true인 속성을 IsName 설정합니다. 이 플래그는 소유자 내에서 요소를 고유하게 식별하는 역할을 하는 속성을 표시합니다. 이 경우 Name 속성에는 해당 플래그가 있습니다.

  • 이 DSL의 정의는 스토어에 미리 로드되어 있어야 합니다. 확장을 작성할 때, 특히 메뉴 명령과 같은 경우 보통 이미 참으로 설정되어 있을 것입니다. 다른 경우에는 모델을 Store에 명시적으로 로드하거나 ModelBus 를 사용하여 로드할 수 있습니다. 자세한 내용은 방법: 프로그램 코드의 파일에서 모델 열기를 참조하세요.

    이러한 방식으로 요소를 만들면 셰이프가 자동으로 만들어집니다(DSL에 다이어그램이 있는 경우). 자동으로 할당된 위치에 기본 도형, 색 및 기타 기능이 표시됩니다. 연결된 셰이프가 표시되는 위치와 방법을 제어하려면 요소 및 해당 셰이프 만들기를 참조하세요.

예제 DSL 정의에는 두 개의 관계가 정의되어 있습니다. 각 관계는 관계의 각 끝에 있는 클래스의 역할 속성을 정의합니다.

관계의 인스턴스를 만들 수 있는 세 가지 방법이 있습니다. 이러한 세 가지 방법 각각은 동일한 효과를 줍니다.

  • 원본 역할 플레이어의 속성을 설정합니다. 다음은 그 예입니다.

    • familyTree.People.Add(edward);

    • edward.Parents.Add(henry);

  • 대상 역할 플레이어의 속성을 설정합니다. 다음은 그 예입니다.

    • edward.familyTreeModel = familyTree;

      이 역할의 다중성을 고려하여 값을 할당합니다.

    • henry.Children.Add(edward);

      이 역할의 다중성은 0..*이므로 우리는 컬렉션에 추가합니다.

  • 관계의 인스턴스를 명시적으로 생성합니다. 다음은 그 예입니다.

    • FamilyTreeHasPeople edwardLink = new FamilyTreeHasPeople(familyTreeModel, edward);

    • ParentsHaveChildren edwardHenryLink = new ParentsHaveChildren(henry, edward);

    마지막 메서드는 관계 자체에 속성을 설정 하려는 경우에 유용 합니다.

    이러한 방식으로 요소를 만들면 다이어그램의 연결선이 자동으로 만들어지지만 기본 도형, 색 및 기타 기능이 있습니다. 연결된 커넥터를 만드는 방법을 제어하려면 요소 및 해당 셰이프 만들기를 참조하세요.

요소 삭제

다음을 호출 Delete()하여 요소를 삭제합니다.

henry.Delete();

이 작업은 또한 다음을 삭제합니다.

  • 요소 간 관계 링크입니다. 예를 들어 edward.Parents에는 더 이상 henry를 포함하지 않습니다.

  • 플래그가 true인 경우의 역할에 해당하는 PropagatesDelete 요소입니다. 예를 들어 요소를 표시하는 셰이프가 삭제됩니다.

기본적으로 모든 포함 관계의 대상 역할은 PropagatesDelete로 기본 설정됩니다. henry를 삭제해도 familyTree는 삭제되지 않지만, familyTree.Delete()는 모든 Persons을 삭제합니다.

기본적으로 PropagatesDelete는 참조 관계의 역할에 대해 true가 아닙니다.

개체를 삭제할 때 삭제 규칙이 특정 전파를 생략하도록 할 수 있습니다. 이는 한 요소를 다른 요소로 대체하는 경우에 유용합니다. 삭제를 전파해서는 안 되는 하나 이상의 역할의 GUID를 제공합니다. GUID는 관계 클래스에서 가져올 수 있습니다.

henry.Delete(ParentsHaveChildren.SourceDomainRoleId);

(이 특정 예제는 ParentsHaveChildren 관계의 역할에 대해 PropagatesDeletefalse 이므로 아무런 영향을 미치지 않습니다.)

일부 경우에는, 요소 자체 또는 전파로 인해 삭제될 요소에 잠금이 있어서 삭제가 방지됩니다. 요소를 삭제할 수 있는지 여부를 확인하는 데 사용할 element.CanDelete() 수 있습니다.

역할 속성에서 요소를 제거하여 관계 링크를 삭제할 수 있습니다.

henry.Children.Remove(edward); // or:

edward.Parents.Remove(henry); // or:

링크를 명시적으로 삭제할 수도 있습니다.

edwardHenryLink.Delete();

이러한 세 가지 메서드는 모두 동일한 효과를 갖습니다. 그 중 하나만 사용해야 합니다.

역할에 0..1 또는 1..1 곱셈이 있는 경우 다음 값 또는 다른 값으로 null설정할 수 있습니다.

edward.FamilyTreeModel = null; 또는:

edward.FamilyTreeModel = anotherFamilyTree;

관계의 링크 순서 다시 지정

특정 모델 요소를 대상으로 하는 특정 관계의 링크에는 특정 시퀀스가 있습니다. 추가된 순서대로 표시됩니다. 예를 들어 이 문은 항상 동일한 순서로 자식을 생성합니다.

foreach (Person child in henry.Children) ...

링크의 순서를 변경할 수 있습니다.

ParentsHaveChildren link = GetLink(henry,edward);

ParentsHaveChildren nextLink = GetLink(henry, elizabeth);

DomainRoleInfo role =

link.GetDomainRelationship().DomainRoles[0];

link.MoveBefore(role, nextLink);

Locks

잠금으로 인해 변경 내용을 방지할 수 있습니다. 잠금은 개별 요소, 파티션 및 저장소에서 설정할 수 있습니다. 이러한 레벨 중에서 원하는 변경을 방지하는 잠금이 있는 경우, 시도할 때 예외가 발생할 수 있습니다. 요소를 사용하여 잠금이 설정되었는지 여부를 검색할 수 있습니다. GetLocks()는 네임스페이스에 Microsoft.VisualStudio.Modeling.Immutability정의된 확장 메서드입니다.

자세한 내용은 Read-Only 세그먼트를 만들기 위한 잠금 정책 정의를 참조하세요.

복사하여 붙여넣기

요소 또는 요소 그룹을 IDataObject으로 복사할 수 있습니다.

Person person = personShape.ModelElement as Person;
Person adopter = adopterShape.ModelElement as Person;
IDataObject data = new DataObject();
personShape.Diagram.ElementOperations
      .Copy(data, person.Children.ToList<ModelElement>());

요소는 serialize된 요소 그룹으로 저장됩니다.

IDataObject의 요소를 모델에 병합할 수 있습니다.

using (Transaction t = targetDiagram.Store.
        TransactionManager.BeginTransaction("paste"))
{
  adopterShape.Diagram.ElementOperations.Merge(adopter, data);
}

Merge ()PresentationElement 또는 ModelElement를 수락할 수 있습니다. 지정한 PresentationElement경우 대상 다이어그램의 위치를 세 번째 매개 변수로 지정할 수도 있습니다.

다이어그램 탐색 및 업데이트

DSL에서 Person 또는 Song과 같은 개념을 나타내는 도메인 모델 요소는 다이어그램에 표시되는 내용을 나타내는 셰이프 요소와 별개입니다. 도메인 모델 요소는 개념의 중요한 속성과 관계를 저장합니다. 셰이프 요소는 다이어그램에 개체 뷰의 크기, 위치 및 색과 해당 구성 요소 부분의 레이아웃을 저장합니다.

프레젠테이션 요소

기본 셰이프 및 요소 형식의 클래스 다이어그램

DSL 정의에서 지정하는 각 요소는 다음 표준 클래스 중 하나에서 파생된 클래스를 만듭니다.

요소의 종류 기본 클래스
도메인 클래스 ModelElement
도메인 관계 ElementLink
Shape NodeShape
Connector BinaryLinkShape
도표 Diagram

다이어그램의 요소는 일반적으로 모델 요소를 나타냅니다. 일반적으로(항상 그렇지는 않음) NodeShape 도메인 클래스 인스턴스를 나타내고 BinaryLinkShape 도메인 관계 인스턴스를 나타냅니다. PresentationViewsSubject 관계는 노드 또는 링크 셰이프를 그것이 나타내는 모델 요소에 연결하는 역할을 합니다.

모든 노드 또는 링크 셰이프는 하나의 다이어그램에 속합니다. 이진 링크 셰이프는 두 개의 노드 셰이프를 연결합니다.

도형에는 두 집합의 자식 도형이 있을 수 있습니다. 집합의 NestedChildShapes 도형은 부모의 경계 상자로 제한됩니다. 목록의 RelativeChildShapes 셰이프는 레이블 또는 포트와 같이 부모 범위 외부 또는 부분적으로 나타날 수 있습니다. 다이어그램에는 RelativeChildShapesParent이 없습니다.

셰이프와 요소 간 탐색

도메인 모델 요소와 셰이프 요소는 PresentationViewsSubject 관계로 연결됩니다.

// using Microsoft.VisualStudio.Modeling;
// using Microsoft.VisualStudio.Modeling.Diagrams;
// using System.Linq;
Person henry = ...;
PersonShape henryShape =
  PresentationViewsSubject.GetPresentation(henry)
    .FirstOrDefault() as PersonShape;

동일한 관계는 다이어그램의 커넥터에 관계를 연결합니다.

Descendants link = Descendants.GetLink(henry, edward);
DescendantConnector dc =
   PresentationViewsSubject.GetPresentation(link)
     .FirstOrDefault() as DescendantConnector;
// dc.FromShape == henryShape && dc.ToShape == edwardShape

또한 이 관계는 모델의 루트를 다이어그램에 연결합니다.

FamilyTreeDiagram diagram =
   PresentationViewsSubject.GetPresentation(familyTree)
      .FirstOrDefault() as FamilyTreeDiagram;

셰이프가 나타내는 모델 요소를 얻으려면 다음을 사용합니다.

henryShape.ModelElement as Person

diagram.ModelElement as FamilyTreeModel

일반적으로 다이어그램에서 셰이프와 연결선 사이를 탐색하는 것은 바람직하지 않습니다. 다이어그램의 모양을 작업해야 하는 경우에만 셰이프와 연결선 간에 이동하여 모델의 관계를 탐색하는 것이 좋습니다. 이러한 메서드는 각 끝의 셰이프에 연결자를 연결합니다.

personShape.FromRoleLinkShapes, personShape.ToRoleLinkShapes

connector.FromShape, connector.ToShape

많은 셰이프가 복합체입니다. 부모 셰이프와 하나 이상의 자식 레이어로 구성됩니다. 다른 셰이프를 기준으로 배치되는 셰이프는 자식이라고 합니다. 부모 도형이 이동하면 자식 도형도 함께 이동합니다.

상대 자식 은 부모 도형의 경계 상자 밖에 나타날 수 있습니다. 중첩된 자식은 부모의 경계 안에만 존재합니다.

다이어그램에서 상위 셰이프 집합을 가져오려면 다음을 사용합니다.

Diagram.NestedChildShapes

셰이프 및 연결선의 상위 클래스는 다음과 같습니다.

ModelElement

-- PresentationElement

-- ShapeElement

----- NodeShape

------- Diagram

------- YourShape

----- LinkShape

------- BinaryLinkShape

--------- YourConnector

셰이프 및 연결선의 속성

대부분의 경우 셰이프를 명시적으로 변경할 필요는 없습니다. 모델 요소를 변경한 경우 "수정" 규칙은 셰이프와 연결선을 업데이트합니다. 자세한 내용은 변경 사항에 대응하고 전파하는 방법을 참조하세요.

그러나 모델 요소와 독립적인 속성에서 셰이프를 명시적으로 변경하는 것이 유용합니다. 예를 들어 다음과 같은 속성을 변경할 수 있습니다.

  • Size - 셰이프의 높이와 너비를 결정합니다.

  • Location - 부모 셰이프 또는 다이어그램을 기준으로 하는 위치

  • StyleSet - 셰이프 또는 연결선을 그리는 데 사용되는 펜 및 브러시 집합

  • Hide - 셰이프를 보이지 않게 합니다.

  • Show - Hide() 뒤에 모양을 표시합니다.

요소 및 해당 셰이프 만들기

요소를 만들어 포함 관계의 트리에 연결하면 셰이프가 자동으로 만들어지고 연결됩니다. 이 작업은 트랜잭션이 끝날 때 실행되는 "수정" 규칙에 의해 수행됩니다. 그러나 셰이프가 자동으로 할당된 위치에 표시되고 해당 셰이프, 색 및 기타 기능에는 기본값이 있습니다. 셰이프를 만드는 방법을 제어하려면 병합 함수를 사용할 수 있습니다. 먼저 ElementGroup에 추가하려는 요소를 추가한 다음 그룹을 다이어그램에 병합해야 합니다.

이 메서드는 다음과 같습니다.

  • 속성을 요소 이름으로 할당한 경우 이름을 설정합니다.

  • DSL 정의에서 지정한 요소 병합 지시문을 관찰합니다.

다음은 사용자가 다이어그램을 두 번 클릭할 때 마우스 위치에 셰이프를 만드는 예제입니다. 이 샘플에 대한 DSL 정의에서 ExampleShape의 속성 FillColor이 노출되었습니다.

using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
partial class MyDiagram
{
  public override void OnDoubleClick(DiagramPointEventArgs e)
  {
    base.OnDoubleClick(e);

    using (Transaction t = this.Store.TransactionManager
        .BeginTransaction("double click"))
    {
      ExampleElement element = new ExampleElement(this.Store);
      ElementGroup group = new ElementGroup(element);

      { // To use a shape of a default size and color, omit this block.
        ExampleShape shape = new ExampleShape(this.Partition);
        shape.ModelElement = element;
        shape.AbsoluteBounds = new RectangleD(0, 0, 1.5, 1.0);
        shape.FillColor = System.Drawing.Color.Azure;
        group.Add(shape);
      }

      this.ElementOperations.MergeElementGroupPrototype(
        this,
        group.CreatePrototype(),
        PointD.ToPointF(e.MousePosition));
      t.Commit();
    }
  }
}

둘 이상의 도형을 제공하는 경우 .를 사용하여 AbsoluteBounds상대 위치를 설정합니다.

이 메서드를 사용하여 커넥터의 색 및 기타 노출된 속성을 설정할 수도 있습니다.

트랜잭션 사용

셰이프, 연결선 및 다이어그램은 ModelElement의 하위 형식이며 스토어에 존재합니다. 따라서 트랜잭션 내에서만 변경해야 합니다. 자세한 내용은 방법: 트랜잭션을 사용하여 모델 업데이트

문서 보기 및 문서 데이터

표준 다이어그램 형식의 클래스 다이어그램

파티션 저장

모델이 로드되면 함께 제공되는 다이어그램이 동시에 로드됩니다. 일반적으로 모델은 Store.DefaultPartition에 로드되고 다이어그램 콘텐츠는 다른 파티션으로 로드됩니다. 일반적으로 각 파티션의 콘텐츠가 로드되고 별도의 파일에 저장됩니다.