Connectors between compartment shape entries with DSL Tools – part 1

Tags: , , , , ,
6 Comments »

[Update (2008-05-21): This code is now hosted at CodePlex as part of JaDAL. And a follow-up article was published.]

This is the first part of an article series to this topic on a library I wrote. At the end of this article you find links to the upcoming articles. The download can be found at part 2 along with a brief "user guide".

Let’s begin…

With DSL Tools you get compartment shapes for your diagrams that look very much like the class-shapes in the graphical class designer of Visual Studio. These compartment shapes are very useful in many model designs and can visualize much information in a compact way. The user can collapse the whole shape (Shape 4) or single compartment lists (Shape 1, Shape 6) to save even more space on his diagram.

compartmentExample

Like in the class designer you can only create connections from one shape to another not from one compartment entry to a shape or another compartment entry. The ability to create such connectors can simplify many DSL designs and on the VSX forum a couple of people asked for this feature. But there is a simple answer to this topic:

"This would be a useful feature, but unfortunately it doesn’t fit into our plans for DSL tools V1.  It’s something we’ll consider for the next version, though."
[Grayson Myers MSFT]

What should I do? Change my DSL and make it complicated and less useful because of a limitation of the framework? Wait for an upcoming Version without a release date and without knowing that it will work for me? This all sounds like a very bad idea to me. So let’s change or extend the framework!

I wrote a library to bring this feature to the DSL Tools. With this library you can

  • Create connections between compartment entries of two different compartment shapes (or even within the same shape, if you like). You can specify the allowed type of the source or target shape or select the same type for source and target. You can also constraint the allowed compartment entries by type if your source (or target) compartment shapes define more then one compartment group.
  • The user experience isn’t changed: The user creates this connections with drag and drop as usual in the generated DSL model designer.
  • You can add code to write custom accept rules for this connection.
  • You can decide how the information of the source and target entry is stored in the generated Domain Relationship.
  • If you want, the whole compartment shape can be the source or target of a connection as well. This will be visualized through a connector from or to the head of the shape.
  • When the user deletes the compartment entry, the connection will be automatically deleted, too. (optional)
  • One of source or target can be a regular shape. E.g. you can create a connector where the source is a compartment entry of a certain compartment shape and the target is a regular geometry or image shape. Since the update (see top of article) both source and target can be a regular shape, too or a base class mapped to a regular and compartment shape.
  • All other properties, extension points, configuration, visualizations and so on will be used from DSL Tools.

Since I can not change the code of the DSL Tools framework and libraries and I want not change the generated code in your DSL project (this will be overwritten each time you generate it) I started to write a library. To use this library in your DSL project you have to design some elements in your DSL as described by me later on. It is curial to set certain properties or it will not work as expected. Then you have to add come code to your DSL project to extend the partial classes generated by the DSL Tools code generator. To minimize the amount of  hand written code I created a code generator as well. With this generator you need only to specify the connection you want to use as a compartment entry connection. Then only one class with three methods is left to be filled with code.

Because of the limitations of the DSL Tools framework and the given extension points not everything is working as someone will expect. But at this moment I can only think of one feature you may miss with my solution:

  • Obviously the connector have to start on the left or right side of the shape just besides the corresponding compartment entry. Every time you change (add or remove entries; collapse or expand the shape) or move the compartment shape this connection points will be recalculated.
    The routing of the connector may change when this happens, but it will always use the routing algorithm you already know within the DSL editors. To ensure a correct visual presentation of the connector the user MUST NOT change the routing! For every connector under the control of my library the user CANNOT change the routing. – In my opinion this is a small price for the features you can add to the DSL framework.

Upcoming articles

  • This article showed the basics of what my library can do.
  • In the next article I will give you a brief user guide on how to provide this functionality to your DSL developments. The download of the library, the source code and examples will be there as well.
  • The third part gives you a deeper look inside of the library and shows you the way it is working
  • In the last part I will explain a way of finding and removing some default commands from the DSL editor.

Adding properties to Domain Classes at runtime with DSL Tools

Tags: , , , ,
3 Comments »

Each element you can see in a DSL Diagram within the DSL editor is based on a Domain Class defined in your DSL Definition. If the user selects on of these shapes in the Editor he can change the values of the defined Domain Properties in the well known Properties Window of Visual Studio, but only Properties declared at design time will be shown in the Properties Window.

For some reason I want to change the number, name and type of the properties at runtime. E.g. some code will add some properties to one shape while the user works with the DSL model. Different shapes based on the same Domain Class should be able to display different properties as well. Such a behavior is not support by the DSL Tools in the current version, so I build a small library as a workaround to provide this functionality. As I know some of you have a similar request, I will explain my approach here, and of cause publish the sourcecode of my library below.

What you get…

But first of all, let me show you what you can expect. So you can decide to stop reading my article right at this point 😉

I designed a new Class DynamicPropertyCollection which is a container of all new added properties. You will add a Domain Property of this type to your Domain Class at design time and then you can add more properties to this collection at runtime. I will care for showing these properties in the Properties Window.

prop14

You can see here the Properties Window while one Example Element is selected. The Name is the only regular designed Domain Property. The field Props is also specified at design time as a Domain Property of the type DynamicPropertyCollection, but the user can not insert any text to this row. But as you can see with the minus symbol in front of the Props row there are some subproperties. These are the dynamic added properties and you can change these list at runtime as promised. The subproperties can contain any data type that can be used with the properties grid, and even more DynamicPropertyCollections. The even more row contains such an element:

prop24

There are two additional properties hidden below the even more row and as you can see, all user interface features like the calendar on DateTime values are supported out of the box (all this comes with the great properties grid control).

Maybe you can claim I did not really keep all my promises because I did not add additional properties to the Example Element rather to using these sub properties. That’s right, but I think it’s very close and there are many advantages with this: You do not need to add code to the Domain Class, the Shape or anywhere within the generated classes and you can only use my DynamicPropertyCollection class as a black box (although I try to whiten it here a little bit).

How does all this magic work?

The trick comes with the ICustomTypeDescriptor Interface. Normally the properties grid will use reflection to access all public properties of a class and shows these properties in the grid. But if you implement this interface the grid will ask you for a list of all properties in a PropertyDescriptorCollection. This question I can answer with another list of properties each time and so I can show dynamic properties for each element.

In my code you find a DynamicPropertyCollection class that implements this interface and uses for most methods a default implementation, but some special code for public PropertyDescriptorCollection GetProperties(Attribute[] attributes). Internally there is a private readonly List<DynamicProperty> properties that contains all these properties.

The DynamicPropertyDescriptor objects returned by GetProperties() contain every information the property grid needs to show the list. Further more it will delegate get and set operations for the properties to this DynamicPropertyDescriptor which stores the values in the underlying DynamicProperty objects.

As a user you will hardly see the DynamicPropety or DynamicPropertyDescriptor class and need not to worry about these details. You can only work with the public interface of DynamicPropertyCollection:

public void AddProperty(string name, Type type)
public void AddProperty(string name, Type type, object value)
public void RemoveProperty(string name)
public bool ExistsProperty(string name)


public
Type GetPropertyType(string name) public object GetPropteryValue(string name) public void SetPropertyValue(string name, object value)

With this few classes and only few lines of code you get the properties shown as in the screenshots above!

…but how to serialize?

But this is only half the way to go. We need to serialize all the properties with the Domain Model and deserialize them when loading a model. Since the DynamicPropertyCollection is part of the Domain Class as a Domain Property this seems to be the right and natural place to store all the values. The DSL Tools will use the TypeConverter of each type to convert the object to a string, so we have to provide a TypeConverter for the DynamicPropertyCollection:

public class DynamicPropertyCollectionTypeConverter : ExpandableObjectConverter

and attach this TypeConverter to the DynamicPropertyCollection:

[TypeConverter("BenjaminSchroeter.DynamicDslProperties.DynamicPropertyCollectionTypeConverter")]

public class DynamicPropertyCollection : ICustomTypeDescriptor

(There is a known issue and a workaround I described here with the TypeConverterAttribute and the DSL Tools…)

The DynamicPropertyCollectionTypeConverter inherits from ExpandableObjectConverter to provide the expandable functionality in the properties grid, but all the other code is used for serialization only.

I have to provide Code to convert from and to string. In my string representation I have to store all properties with there name, the type and the value, of cause. To store the different values I use a TypeConverter and convert the objectvalues to string as well. So you can only use types that can be converted to and from string, but that are all types most people use in the Properties Window since the user can only type and see strings there.

The representation in the XML file of your DSL will look like this:

<exampleElement name="ExampleElement1">
<props>
name_1="some text" type_1="System.String" value_1="hello"
name_2="some number" type_2="System.Int32" value_2="42"
name_3="yes or no" type_3="System.Boolean" value_3="True"
</props>

As you see, it is still human readable.

What should I do?

If you only want to use the library look at the following steps:

  1. Compile the DynamicDslProperties code as a library and add a reference to your DSL project. Maybe you want change the key file for signing.
  2. In the DSL Explorer you have to add a External Type with Namespace = "BenjaminSchroeter.DynamicDslProperties" and Name = "DynamicPropertyCollection".
  3. Now you can add a Domain Property of type DynamicPropertyCollection to any Domain Class.
  4. Change the Default Value of the property to " " (one space only). This is crucial to initialize the class on creation of new Domain Elements! Otherwise the new Domain Element will have a null value for this property and you must handle it at various places.
  5. In the DSL Explorer look at the Xml Serialization Behavior of the property of this Domain Class and change the field Representation from Attribute to Element. This step is optional but provides you a much nicer XML representation.
  6. Add somewhere code to add properties to the DynamicPropertyCollection. Remember to wrap this and all changes to properties in a transaction.

For testing and debugging

For a fast jumpstart and to test the features I provide a small dialog to add and delete properties.

edit6

You can simply add some code to a shape to open this dialog on double click.

partial class ExampleShape
{
  public override void OnDoubleClick(DiagramPointEventArgs e)
  {
    ExampleElement elem = e.HitDiagramItem.Shape.ModelElement as ExampleElement;
    if ( elem != null)
    {
       EditDynamicPropertiesDialog f;
f = new EditDynamicPropertiesDialog(elem.Props, elem.Store); f.ShowDialog(); } base.OnDoubleClick(e); } }

Of cause this window and the DoubleClick event are only good while debugging and testing. For real world projects you will use the public interface described above to add and remove properties.

The code

In the zip file you will find the whole sourcecode of the described library to use in your projects. Just leave my copyright and the link to this article in the header of each file and do not expect any warranty. If you extend the library, find or fix some bugs I would appreciate a comment here.

There is also a whole DSL project in the zip file to try my code right away.

dynamicdslpropertiessource.zip

Update

This code is now part of the JaDAL – Just another DSL-Tools Addon Library project. Please download the current version from that page. The download over at CodePlex contains the source code described here, an example DSL language and the library as a binary. Future enhancements of the code will be published there, too.

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Log in