Bagikan melalui


Menavigasi dan Memperbarui Model dalam Kode Program

Anda dapat menulis kode untuk membuat dan menghapus elemen model, mengatur propertinya, dan membuat dan menghapus tautan antar elemen. Semua perubahan harus dilakukan dalam transaksi. Jika elemen ditampilkan pada diagram, diagram akan "diperbaiki" secara otomatis di akhir transaksi.

Contoh definisi DSL

Ini adalah bagian utama dari DslDefinition.dsl untuk contoh dalam topik ini:

Diagram Definisi DSL - model pohon keluarga

Model ini adalah instance dari DSL ini:

Model Pohon Keluarga Tudor

Referensi dan Namespace

Untuk menjalankan kode dalam topik ini, Anda harus mereferensikan:

Microsoft.VisualStudio.Modeling.Sdk.11.0.dll

Kode Anda akan menggunakan namespace ini:

using Microsoft.VisualStudio.Modeling;

Selain itu, jika Anda menulis kode dalam proyek yang berbeda dari yang didefinisikan DSL Anda, Anda harus mengimpor rakitan yang dibangun oleh proyek Dsl.

Karakteristik

Properti domain yang Anda tentukan dalam definisi DSL menjadi properti yang dapat Anda akses dalam kode program:

Person henry = ...;

if (henry.BirthDate < 1500) ...

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

Jika Anda ingin mengatur properti, Anda harus melakukannya di dalam transaksi:

henry.Name = "Henry VIII";

Jika dalam definisi DSL, Jenis sebuah properti adalah Dihitung, Anda tidak dapat mengubahnya. Untuk informasi selengkapnya, lihat Properti Penyimpanan Terhitung dan Kustom.

Hubungan

Hubungan domain yang Anda tentukan dalam definisi DSL menjadi pasangan properti, satu pada kelas di masing-masing ujung hubungan. Nama properti muncul dalam diagram DslDefinition sebagai label pada peran di setiap sisi hubungan. Tergantung pada jumlah peran, tipe properti adalah kelas di ujung lain dari hubungan, atau koleksi kelas tersebut.

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

FamilyTreeModel ftree = henry.FamilyTreeModel;

Sifat-sifat di kedua ujung yang berlawanan dari suatu hubungan selalu timbal balik. Saat tautan dibuat atau dihapus, properti peran pada kedua elemen diperbarui. Ekspresi berikut (yang menggunakan ekstensi System.Linq) selalu berlaku untuk hubungan ParentsHaveChildren dalam contoh:

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

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

ElementLinks. Hubungan juga diwakili oleh elemen model yang disebut tautan, yang merupakan instans jenis hubungan domain. Tautan selalu memiliki satu elemen sumber dan satu elemen target. Elemen sumber dan elemen target bisa sama.

Anda dapat mengakses tautan dan propertinya:

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

// This is now true:

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

Secara default, tidak lebih dari satu instans hubungan diizinkan untuk menautkan sepasang elemen model apa pun. Tetapi jika dalam definisi DSL, Allow Duplicates flag benar untuk hubungan tersebut, maka mungkin ada lebih dari satu tautan yang tersedia, dan kamu harus menggunakan GetLinks:

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

Ada juga metode lain untuk mengakses tautan. Contohnya:

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

Peran tersembunyi. Jika dalam definisi DSL, Apakah Properti Dihasilkan adalah false untuk peran tertentu, maka tidak ada properti yang dihasilkan yang sesuai dengan peran tersebut. Namun, Anda masih dapat mengakses tautan dan melintasi tautan menggunakan metode hubungan:

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

Contoh yang paling sering digunakan adalah PresentationViewsSubject hubungan, yang menautkan elemen model ke bentuk yang menampilkannya pada diagram:

PresentationViewsSubject.GetPresentation(henry)[0] as PersonShape

Direktori Elemen

Anda dapat mengakses semua elemen di penyimpanan menggunakan direktori elemen:

store.ElementDirectory.AllElements

Ada juga metode untuk menemukan elemen, seperti berikut ini:

store.ElementDirectory.FindElements(Person.DomainClassId);

store.ElementDirectory.GetElement(elementId);

Mengakses Informasi Kelas

Anda bisa mendapatkan informasi tentang kelas, hubungan, dan aspek lain dari definisi DSL. Contohnya:

DomainClassInfo personClass = henry.GetDomainClass();

DomainPropertyInfo birthProperty =

personClass.FindDomainProperty("BirthDate")

DomainRelationshipInfo relationship =

link.GetDomainRelationship();

DomainRoleInfo sourceRole = relationship.DomainRole[0];

Kelas leluhur elemen model adalah sebagai berikut:

  • ModelElement - semua elemen dan hubungan adalah ModelElements

  • ElementLink - semua relasi adalah ElementLinks

Melakukan Perubahan di dalam Transaksi

Setiap kali kode program Anda mengubah apa pun di Toko, kode tersebut harus melakukannya di dalam transaksi. Ini berlaku untuk semua elemen model, hubungan, bentuk, diagram, dan propertinya. Untuk informasi selengkapnya, lihat Transaction .

Metode paling nyaman untuk mengelola transaksi adalah dengan using pernyataan yang diapit dalam pernyataan try...catch.

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

Anda dapat membuat sejumlah perubahan di dalam satu transaksi. Anda dapat membuka transaksi baru di dalam transaksi aktif.

Untuk membuat perubahan permanen, Anda harus Commit melakukan transaksi sebelum dibuang. Jika terjadi pengecualian yang tidak tertangkap di dalam transaksi, Store akan diatur ulang ke keadaan sebelum perubahan dilakukan.

Membuat Elemen Model

Contoh ini menambahkan elemen ke model yang sudah ada:

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!
}

Contoh ini mengilustrasikan poin-poin penting ini tentang membuat elemen:

  • Buat elemen baru dalam partisi tertentu dari Store. Untuk elemen dan hubungan model, tetapi bukan bentuk, biasanya ini adalah partisi default.

  • Jadikan sebagai target dalam hubungan penyematan. Dalam DslDefinition dari contoh ini, setiap Orang harus menjadi target dari hubungan penyematan FamilyTreeHasPeople. Untuk mencapai hal ini, kita dapat mengatur properti peran FamilyTreeModel dari objek Person, atau menambahkan objek Person ke properti peran People dari objek FamilyTreeModel.

  • Atur properti elemen baru, terutama properti yang IsName benar di DslDefinition. Bendera ini menandai properti yang berfungsi untuk mengidentifikasi elemen secara unik dalam pemiliknya. Dalam hal ini, properti Nama memiliki bendera tersebut.

  • Definisi DSL dari DSL ini harus telah dimuat ke dalam Store. Jika Anda menulis ekstensi seperti perintah menu, ini biasanya sudah benar. Dalam kasus lain, Anda dapat secara eksplisit memuat model ke penyimpanan, atau menggunakan ModelBus untuk memuatnya. Untuk informasi selengkapnya, lihat Cara: Membuka Model dari File dalam Kode Program.

    Saat Anda membuat elemen dengan cara ini, bentuk secara otomatis dibuat (jika DSL memiliki diagram). Ini muncul di lokasi yang ditetapkan secara otomatis, dengan bentuk, warna, dan fitur default lainnya. Jika Anda ingin mengontrol di mana dan bagaimana bentuk terkait muncul, lihat Membuat Elemen dan Bentuknya.

Ada dua hubungan yang ditentukan dalam contoh definisi DSL. Setiap hubungan mendefinisikan properti peran pada kelas di setiap akhir hubungan.

Ada tiga cara di mana Anda dapat membuat instans hubungan. Masing-masing dari ketiga metode ini memiliki efek yang sama:

  • Konfigurasi properti dari peran sumber. Contohnya:

    • familyTree.People.Add(edward);

    • edward.Parents.Add(henry);

  • Atur properti dari peran sasaran. Contohnya:

    • edward.familyTreeModel = familyTree;

      Kelipatan peran ini adalah 1..1, jadi kami menetapkan nilainya.

    • henry.Children.Add(edward);

      Kemajemukan peran ini adalah 0..*, jadi kami menambahkannya ke kumpulan.

  • Buat instance hubungan secara eksplisit. Contohnya:

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

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

    Metode terakhir berguna jika Anda ingin mengatur properti pada hubungan itu sendiri.

    Saat Anda membuat elemen dengan cara ini, konektor pada diagram dibuat secara otomatis, tetapi memiliki bentuk, warna, dan fitur default lainnya. Untuk mengontrol bagaimana konektor terkait dibuat, lihat Membuat Elemen dan Bentuknya.

Menghapus Elemen

Hapus elemen dengan memanggil Delete():

henry.Delete();

Operasi ini juga akan menghapus:

  • Hubungan tautan dari dan ke elemen tersebut. Misalnya, edward.Parents tidak akan lagi berisi henry.

  • Elemen untuk peran di mana flag PropagatesDelete diaktifkan. Misalnya, bentuk yang menampilkan elemen akan dihapus.

Secara default, setiap hubungan penyematan memiliki PropagatesDelete true pada peran target. Menghapus henry tidak menghapus familyTree, tetapi familyTree.Delete() akan menghapus semua Persons.

Secara default, PropagatesDelete tidak berlaku untuk peran hubungan referensi.

Anda dapat menyebabkan aturan penghapusan menghilangkan penyebaran tertentu saat Anda menghapus objek. Ini berguna jika Anda mengganti satu elemen untuk elemen lain. Anda menyediakan GUID dari satu atau beberapa peran yang penghapusannya tidak boleh disebarluaskan. GUID dapat diperoleh dari kelas relasi:

henry.Delete(ParentsHaveChildren.SourceDomainRoleId);

(Contoh khusus ini tidak akan berpengaruh, karena PropagatesDelete adalah false untuk peran dalam hubungan ParentsHaveChildren.)

Dalam beberapa kasus, penghapusan dicegah oleh keberadaan kunci, baik pada elemen atau pada elemen yang akan dihapus oleh penyebaran. Anda dapat menggunakan element.CanDelete() untuk memeriksa apakah elemen dapat dihapus.

Anda dapat menghapus tautan hubungan dengan menghapus elemen dari properti peran:

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

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

Anda juga dapat menghapus tautan secara eksplisit:

edwardHenryLink.Delete();

Ketiga metode ini semuanya memiliki efek yang sama. Anda hanya perlu menggunakan salah satunya.

Jika peran memiliki kelipatan 0..1 atau 1..1, Anda dapat mengaturnya ke null, atau ke nilai lain:

edward.FamilyTreeModel = null; atau:

edward.FamilyTreeModel = anotherFamilyTree;

Mengurutkan ulang Tautan Hubungan

Tautan hubungan tertentu yang bersumber atau ditargetkan pada elemen model tertentu memiliki urutan tertentu. Mereka muncul dalam urutan penambahannya. Misalnya, pernyataan ini akan selalu menghasilkan anak-anak dalam urutan yang sama:

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

Anda dapat mengubah urutan tautan:

ParentsHaveChildren link = GetLink(henry,edward);

ParentsHaveChildren nextLink = GetLink(henry, elizabeth);

DomainRoleInfo role =

link.GetDomainRelationship().DomainRoles[0];

link.MoveBefore(role, nextLink);

Locks

Perubahan Anda mungkin dicegah dengan kunci. Kunci dapat diatur pada elemen individual, pada partisi, dan di toko. Jika salah satu level ini memiliki kunci yang mencegah jenis perubahan yang ingin Anda buat, pengecualian mungkin dilemparkan saat Anda mencobanya. Anda dapat menemukan apakah kunci diatur dengan menggunakan elemen. GetLocks(), yang merupakan metode ekstensi yang ditentukan dalam namespace Microsoft.VisualStudio.Modeling.Immutability.

Untuk informasi selengkapnya, lihat Menentukan Kebijakan Penguncian untuk Membuat Segmen Read-Only.

Salin dan Tempel

Anda dapat menyalin elemen atau grup elemen ke 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>());

Elemen disimpan sebagai Grup Elemen berseri.

Anda dapat menggabungkan elemen dari IDataObject ke dalam model:

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

Merge () dapat menerima baik PresentationElement maupun ModelElement. Jika Anda memberinya PresentationElement, Anda juga dapat menentukan posisi pada diagram target sebagai parameter ketiga.

Menavigasi dan memperbarui diagram

Dalam DSL, elemen model domain, yang mewakili konsep seperti Orang atau Lagu, terpisah dari elemen bentuk, yang mewakili apa yang Anda lihat pada diagram. Elemen model domain menyimpan properti dan hubungan penting dari konsep. Elemen bentuk menyimpan ukuran, posisi, dan warna tampilan objek pada diagram, dan tata letak bagian komponennya.

Komponen Presentasi

Diagram kelas dari bentuk dasar dan jenis elemen

Dalam Definisi DSL Anda, setiap elemen yang Anda tentukan membuat kelas yang berasal dari salah satu kelas standar berikut.

Jenis elemen Kelas dasar
Kelas domain ModelElement
Hubungan domain ElementLink
Shape NodeShape
Connector BinaryLinkShape
Diagram Diagram

Elemen pada diagram biasanya mewakili elemen model. Biasanya (tetapi tidak selalu), NodeShape mewakili sebuah instans kelas domain, dan BinaryLinkShape mewakili sebuah instans hubungan domain. Hubungan ini PresentationViewsSubject menautkan simpul atau bentuk tautan ke elemen model yang diwakilinya.

Setiap simpul atau bentuk tautan milik satu diagram. Bentuk tautan biner menyambungkan dua bentuk simpul.

Bentuk bisa memiliki bentuk anak dalam dua set. Bentuk dalam NestedChildShapes set terbatas pada kotak pembatas induknya. Sebuah bentuk dalam daftar RelativeChildShapes dapat muncul di luar atau sebagian di luar batas elemen induk - misalnya label atau port. Diagram tidak memiliki RelativeChildShapes dan tidak ada Parent.

Menavigasi antara bentuk dan elemen

Elemen model domain dan elemen bentuk terkait dengan PresentationViewsSubject hubungan.

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

Hubungan yang sama menghubungkan hubungan dengan konektor pada diagram.

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

Hubungan ini juga menautkan akar model ke diagram:

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

Untuk mendapatkan elemen model yang diwakili oleh bentuk, gunakan:

henryShape.ModelElement as Person

diagram.ModelElement as FamilyTreeModel

Secara umum tidak disarankan untuk menavigasi antara bentuk dan konektor pada diagram. Lebih baik menavigasi hubungan dalam model, bergerak antara bentuk dan konektor hanya ketika perlu untuk bekerja pada tampilan diagram. Metode ini menautkan konektor ke bentuk di setiap ujung:

personShape.FromRoleLinkShapes, personShape.ToRoleLinkShapes

connector.FromShape, connector.ToShape

Banyak bentuk adalah komposit; mereka terdiri dari bentuk induk dan satu atau beberapa lapisan anak. Bentuk yang diposisikan relatif terhadap bentuk lain dikatakan sebagai anak-anaknya. Ketika bentuk induk bergerak, anak-anak bergerak bersamanya.

Anak-anak relatif dapat muncul di luar kotak pembatas bentuk induk. Anak tertanam muncul secara ketat di dalam batas induk.

Untuk mendapatkan set bentuk atas pada diagram, gunakan:

Diagram.NestedChildShapes

Kelas induk dari bentuk dan konektor adalah:

ModelElement

-- PresentationElement

-- ShapeElement

----- NodeShape

------- Diagram

------- YourShape

----- LinkShape

------- BinaryLinkShape

--------- YourConnector

Properti Bentuk dan Konektor

Dalam kebanyakan kasus, tidak perlu membuat perubahan eksplisit pada bentuk. Ketika Anda telah mengubah elemen model, aturan "pengaturan ulang" memperbarui bentuk dan konektor. Untuk informasi selengkapnya, lihat Merespons dan Menyebarkan Perubahan.

Namun, berguna untuk membuat beberapa perubahan eksplisit pada bentuk dalam properti yang independen dari elemen model. Misalnya, Anda dapat mengubah properti ini:

  • Size - menentukan tinggi dan lebar bentuk.

  • Location - posisi relatif terhadap bentuk atau diagram induk

  • StyleSet - set pena dan kuas yang digunakan untuk menggambar bentuk atau konektor

  • Hide - membuat bentuk tidak terlihat

  • Show - membuat bentuk terlihat setelah Hide()

Membuat Elemen dan Bentuknya

Saat Anda membuat elemen dan memasukkannya ke dalam hierarki hubungan, bentuk secara otomatis dibuat dan dikaitkan dengannya. Ini dilakukan oleh aturan "penyempurnaan" yang dijalankan di akhir transaksi. Namun, bentuk akan muncul di lokasi yang ditetapkan secara otomatis, dan bentuknya, warna dan fitur lainnya akan memiliki nilai default. Untuk mengontrol bagaimana bentuk dibuat, Anda dapat menggunakan fungsi penggabungan. Anda harus terlebih dahulu menambahkan elemen yang ingin Anda tambahkan ke dalam ElementGroup, lalu menggabungkan grup ke dalam diagram.

Metode ini:

  • Mengatur nama, jika Anda telah menetapkan properti sebagai nama elemen.

  • Memperhatikan setiap Direktif Penggabungan Elemen yang Anda tentukan dalam Definisi DSL.

Contoh ini membuat bentuk pada posisi mouse, ketika pengguna mengklik dua kali diagram. Dalam Definisi DSL untuk sampel ini, properti FillColor dari ExampleShape telah dipaparkan.

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

Jika Anda menyediakan lebih dari satu bentuk, atur posisi relatifnya menggunakan AbsoluteBounds.

Anda juga dapat mengatur warna dan properti konektor lain yang terekspos menggunakan metode ini.

Gunakan Transaksi

Bentuk, konektor, dan diagram adalah subjenis ModelElement dan berada di Toko. Oleh karena itu, Anda hanya dapat membuat perubahan pada mereka di dalam sebuah transaksi. Untuk informasi selengkapnya, lihat Cara: Menggunakan Transaksi untuk Memperbarui Model.

Tampilan Dokumen dan Data Dokumen

Diagram kelas jenis diagram standar

Partisi Penyimpanan

Saat model dimuat, diagram yang menyertainya dimuat secara bersamaan. Biasanya, model dimuat ke Store.DefaultPartition, dan konten diagram dimuat ke partisi lain. Biasanya, konten setiap partisi dimuat dan disimpan ke file terpisah.