views:

409

answers:

2

OK, so things have progressed significantly with my DSL since I asked this question a few days ago.

As soon as I've refactored my code, I'll post my own answer to that one, but for now, I'm having another problem.

I'm dynamically generating sub-diagrams from a DSL-created model, saving those diagrams as images and then generating a Word document with those images embedded. So far, so good.

But where my shapes have compartments (for examples, Operations on a Service Contract - can you guess what it is, yet?), the compartment header is displayed but none of the items.

If I examine my shape object, it has a single nested child - an ElementListCompartment which in turn, has a number of items that I'm expecting to be displayed. The ElementListCompartment.IsExpanded property is set to true (and the compartment header has a little 'collapse' icon on it) but where, oh where, are my items?

The shape was added to the diagram using

parentShape.FixupChildShapes(modelElement);

So, can anyone guide me on my merry way?

A: 

Maybe my answer is a little bit too late, but did you confirm using DSL Explorer that your compartments have items?

Luis Filipe
+1  A: 

I've recently faced a related problem, and managed to make it work, so here's the story.

The task I was implementing was to load and display a domain model and an associated diagram generated by ActiveWriter's DSL package.

Here's how I've implemented the required functionality (all the methods below belong to the Form1 class I've created to play around):

private Store LoadStore()
{
    var store = new Store();
    store.LoadDomainModels(typeof(CoreDesignSurfaceDomainModel), typeof(ActiveWriterDomainModel));
    return store;
}

private void LoadDiagram(Store store)
{
    using (var tx = store.TransactionManager.BeginTransaction("tx", true))
    {
        var validator = new ValidationController();
        var deserializer = ActiveWriterSerializationHelper.Instance;
        deserializer.LoadModelAndDiagram(store,
            @"..\..\ActiveWriter1.actiw", @"..\..\ActiveWriter1.actiw.diagram", null, validator);
        tx.Commit();
    }
}

private DiagramView CreateDiagramView()
{
    var store = LoadStore();
    LoadDiagram(store);

    using (var tx = store.TransactionManager.BeginTransaction("tx2", true))
    {
        var dir = store.DefaultPartition.ElementDirectory;
        var diag = dir.FindElements<ActiveRecordMapping>().SingleOrDefault();
        var view = new DiagramView(){Diagram = diag};
        diag.Associate(view);
        tx.Commit();

        view.Dock = DockStyle.Fill;
        return view;
    }
}

protected override void OnLoad(EventArgs e)
{
    var view = CreateDiagramView();
    this.Controls.Add(view);
}

This stuff worked mostly finely: it correctly loaded the diagram from files created with Visual Studio, drew the diagram within my custom windows form, supported scrolling the canvas and even allowed me to drag shapes here. However, one thing was bugging me - the compartments were empty and had default name, i.e. "Compartment".

Google didn't help at all, so I had to dig in by myself. It wasn't very easy but with the help of Reflector and after spending a couple of hours I've managed to make this scenario work as expected!

The problem was as follows. To my surprise DSL libraries do not correctly draw certain diagram elements immediately after they are added to the diagram. Sometimes, only stubs of certain shapes are drawn (as it's displayed in the first picture). Thus, sometimes we need to manually ask the library to redraw diagram shapes.

This functionality can be implemented with so called "rules" that in fact are event handlers that get triggered by certain diagram events. Basically what we have to do is attach certain handler to an element-added event of the diagram and ensure shape initialization.

Luckily we don't even have to write any code since DSL designer autogenerates both fixup rules and an utility method that attaches those rules to the diagram (see the EnableDiagramRules below). All we have to do is to call this method right after the store has been created (prior to loading model and diagram).

private Store LoadStore()
{
    var store = new Store();
    store.LoadDomainModels(typeof(CoreDesignSurfaceDomainModel), typeof(ActiveWriterDomainModel));
    ActiveWriterDomainModel.EnableDiagramRules(store);
    return store;
}

/// <summary>
/// Enables rules in this domain model related to diagram fixup for the given store.
/// If diagram data will be loaded into the store, this method should be called first to ensure
/// that the diagram behaves properly.
/// </summary>
public static void EnableDiagramRules(DslModeling::Store store)
{
    if(store == null) throw new global::System.ArgumentNullException("store");

    DslModeling::RuleManager ruleManager = store.RuleManager;
    ruleManager.EnableRule(typeof(global::Altinoren.ActiveWriter.FixUpDiagram));
    ruleManager.EnableRule(typeof(global::Altinoren.ActiveWriter.ConnectorRolePlayerChanged));
    ruleManager.EnableRule(typeof(global::Altinoren.ActiveWriter.CompartmentItemAddRule));
    ruleManager.EnableRule(typeof(global::Altinoren.ActiveWriter.CompartmentItemDeleteRule));
    ruleManager.EnableRule(typeof(global::Altinoren.ActiveWriter.CompartmentItemRolePlayerChangeRule));
    ruleManager.EnableRule(typeof(global::Altinoren.ActiveWriter.CompartmentItemRolePlayerPositionChangeRule));
    ruleManager.EnableRule(typeof(global::Altinoren.ActiveWriter.CompartmentItemChangeRule));
}

The code above works as follows:

  1. Upon new element being added to the diagram (e.g. during deserialization of diagram) the rule "FixUpDiagram" gets triggered.

  2. The rule then calls Diagram.FixUpDiagram(parentElement, childElement), where childElement stands for an element being added and parentElement stands for its logical parent (determined using tricky conditional logic, so I didn't try to reproduce it by myself).

  3. Down the stack trace FixUpDiagram method calls EnsureCompartments methods of all class shapes in the diagram.

  4. The EnsureCompartments method redraws class' compartments turning the stub "[-] Compartment" graphic into full-blown "Properties" shape as displayed in the picture linked above.

P.S. Steve, I've noticed that you did call the fixup but it still didn't work. Well, I'm not a pro in DSL SDK (just started using it a couple of days ago), so cannot explain why you might have troubles.

Maybe, you've called the fixup with wrong arguments. Or maybe Diagram.FixupDiagram(parent, newChild) does something differently from what parent.FixupChildShapes(newChild) does. However here's my variant that just works. Hope this also helps.

Eugene
What a cracking contribution, Eugene! I'll be sure to take another look.
Steve Morgan