Shared State Example in GoXam

Hi,
We are using technique show in Shared State example to have group of nodes where each node can be in more than one parent.
I didn’t see the Shared State example in GoXam samples, is it possible to achieve the same result in GoXam and how? What should I do inside the Model and what should I do in the view?

Thank you,
Ido.

Originally we were intending to have a model class that supported node data being members of multiple groups. However, we never finished implementing that.

The way that the SharedStates sample works in GoDiagram is that it doesn’t make use of GoSubGraphs (aka GoXam Groups) at all. It just updates the “super” state nodes as nodes are moved. We can do the same sort of thing in GoXam.

However, I did not bother to implement support in this GoXam sample for copying or deleting node data in the model and maintaining the “super” relationships. Nor is there any checking for invalid memberships such as cycles. If you want that kind of functionality, you’ll need to implement it yourself by extending this sample implementation.

[code]


<UserControl.Resources>
<go:BooleanBrushConverter x:Key=“theBooleanBrushConverter”
TrueBrush=“Magenta” FalseBrush=“Black” />

<go:DataTemplateDictionary x:Key="DTD">
  <DataTemplate x:Key="">
    <go:NodePanel Sizing="Auto" go:Part.SelectionAdorned="True"
                  go:Node.Location="{Binding Path=Data.Location, Mode=TwoWay}">
      <Rectangle Fill="White" Stroke="Black" StrokeThickness="1" />
      <TextBlock Text="{Binding Path=Data.Text}" Margin="5" />
    </go:NodePanel>
  </DataTemplate>
  <DataTemplate x:Key="Super">
    <go:SpotPanel go:Part.SelectionAdorned="True">
      <Rectangle x:Name="Shape"
                 Fill="#AAAAAAAA" RadiusX="10" RadiusY="10"
                 MinWidth="30" MinHeight="30" />
      <TextBlock go:SpotPanel.Spot="0 0 3 0" go:SpotPanel.Alignment="0 0"
                 FontWeight="Bold"
                 Text="{Binding Path=Data.Text}" />
    </go:SpotPanel>
  </DataTemplate>
</go:DataTemplateDictionary>

</UserControl.Resources>

<go:Diagram x:Name=“myDiagram” Padding=“10”
HorizontalContentAlignment=“Stretch” VerticalContentAlignment=“Stretch”
NodeTemplateDictionary="{StaticResource DTD}"
AllowCopy=“False” AllowDelete=“False”>
go:Diagram.Layout
<local:CustomLayout />
</go:Diagram.Layout>
go:Diagram.DraggingTool
<local:CustomDraggingTool />
</go:Diagram.DraggingTool>
</go:Diagram>

[/code]

[code]/* Copyright © Northwoods Software Corporation, 2008-2011. All Rights Reserved. */

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Northwoods.GoXam;
using Northwoods.GoXam.Layout;
using Northwoods.GoXam.Model;
using Northwoods.GoXam.Tool;

namespace SharedStates {
public partial class SharedStates : UserControl {
public SharedStates() {
InitializeComponent();

  var model = new GraphLinksModel<State, String, String, Transition>();
  model.Modifiable = true;
  model.NodesSource = new ObservableCollection<State>() {
      // create all of the SuperStates
      new State() { Key="A", Text="Operating", Category="Super" },
      new State() { Key="B", Text="Drying", Category="Super" },
      new State() { Key="C", Text="Non drying", Category="Super" },

      // create all of the States
      new State() { Key="1", Text="Starting.End", Location=new Point(100, 100) },
      new State() { Key="2", Text="Running", Location=new Point(250, 100) },
      new State() { Key="3", Text="Starting.Init", Location=new Point(100, 200) },
      new State() { Key="4", Text="Washing", Location=new Point(250, 200) },
      new State() { Key="5", Text="Stopped", Location=new Point(100, 300) },
      new State() { Key="6", Text="Stopping", Location=new Point(250, 300) },
  };
  // create all of the Transitions
  model.LinksSource = new ObservableCollection<Transition>() {
      new Transition() { From="1", To="2" },
      new Transition() { From="3", To="1" },
      new Transition() { From="4", To="2" },
      new Transition() { From="B", To="4" },
      new Transition() { From="5", To="3" },
      new Transition() { From="6", To="5" },
      new Transition() { From="A", To="6" },
  };

  // declare which nodes are in which "Super" states
  // (note that this does not check for invalid relationships!)
  model.FindNodeByKey("B").Supers.Add("A");
  model.FindNodeByKey("3").Supers.Add("A");
  model.FindNodeByKey("4").Supers.Add("A");

  model.FindNodeByKey("1").Supers.Add("B");
  model.FindNodeByKey("2").Supers.Add("B");

  model.FindNodeByKey("3").Supers.Add("C");
  model.FindNodeByKey("4").Supers.Add("C");
  model.FindNodeByKey("5").Supers.Add("C");
  model.FindNodeByKey("6").Supers.Add("C");

  // now make sure all "super" data have up-to-date Members collections,
  // (note that the Members collection is not updated automatically!)
  foreach (State data in model.NodesSource) {
    foreach (String s in data.Supers) {
      State sdata = model.FindNodeByKey(s);
      sdata.Members.Add(data);
    }
  }

  myDiagram.Model = model;
}

}

public class CustomLayout : DiagramLayout {
public CustomLayout() {
this.Conditions = LayoutChange.Standard | LayoutChange.NodeLocationChanged;
}

public override void Invalidate(LayoutChange reason, Part part) {
  base.Invalidate(reason, part);
  if (reason == LayoutChange.NodeLocationChanged) {
    // find any Super States that have this part as a member
    State data = part.Data as State;
    if (data != null) {
      WalkSuperTree(data.Key);
    }
  }
}

private void WalkSuperTree(String id) {
  var data = this.Diagram.Model.FindNodeByKey(id) as State;
  if (data != null) {
    foreach (var sid in data.Supers) {
      this.Invalids.Add(sid);
      WalkSuperTree(sid);
    }
  }
}

HashSet<String> Invalids = new HashSet<String>();

public override void DoLayout(IEnumerable<Node> nodes, IEnumerable<Link> links) {
  // collect "Super" data to be laid out
  var supers = new HashSet<State>();
  // look for any new "Super" nodes
  foreach (var super in nodes.Where(n => n.Category == "Super")) {
    if (Double.IsNaN(super.Location.X)) {
      var sdata = super.Data as State;
      if (sdata != null) supers.Add(sdata);
    }
  }
  var model = this.Diagram.Model;
  foreach (var sid in this.Invalids) {
    var sdata = model.FindNodeByKey(sid) as State;
    if (sdata != null) supers.Add(sdata);
  }

  // Repeatedly go through SUPERS collection to layout
  // those "Super" nodes that don't have any nested invalid "Super" nodes.
  while (supers.Count > 0) {
    var delay = new HashSet<State>();
    // set size and location for each invalidated Super state
    foreach (var sdata in supers) {
      if (SuperIsReady(sdata, supers)) {
        LayoutSuper(sdata);
      } else {
        delay.Add(sdata);
      }
    }
    supers = delay;
  }

  this.Invalids.Clear();
}

private bool SuperIsReady(State sdata, HashSet<State> supers) {
  foreach (var mdata in sdata.Members) {
    if (supers.Contains(mdata)) return false;
  }
  return true;
}

private void LayoutSuper(State sdata) {
  if (sdata == null) return;
  var model = this.Diagram.Model;
  var mgr = this.Diagram.PartManager;
  Node super = mgr.FindNodeForData(sdata, model);
  if (super != null) {
    Rect bounds = this.Diagram.Panel.ComputeBounds(sdata.Members.Select(d => mgr.FindNodeForData(d, model) as Part));
    if (!bounds.IsEmpty) {
      super.Location = new Point(bounds.X - 5, bounds.Y - 20);
      var shape = super.FindNamedDescendant("Shape");
      if (shape != null) {
        shape.Width = bounds.Width + 10;
        shape.Height = bounds.Height + 25;
      }
    }
  }
}

}

public class CustomDraggingTool : DraggingTool {
public override Dictionary<Part, DraggingTool.Info> ComputeEffectiveCollection(IEnumerable parts) {
var hash = new HashSet(parts);
foreach (Part p in parts) {
WalkSubTree(p as Node, hash);
}
return base.ComputeEffectiveCollection(hash);
}

private void WalkSubTree(Node super, HashSet<Part> hash) {
  if (super == null) return;
  hash.Add(super);
  if (super.Category != "Super") return;
  State data = super.Data as State;
  if (data == null) return;
  // recurse through this super state's Members
  var mgr = this.Diagram.PartManager;
  var model = this.Diagram.Model;
  foreach (State mdata in data.Members) {
    WalkSubTree(mgr.FindNodeForData(mdata, model), hash);
  }
}

}

public class State : GraphLinksModelNodeData {
// for declaring membership
public HashSet Supers {
get { return _Supers; }
}
private HashSet _Supers = new HashSet();

// internal, for walking sub-trees
public HashSet<State> Members {
  get { return _Members; }
}
private HashSet<State> _Members = new HashSet<State>();

}

public class Transition : GraphLinksModelLinkData<String, String> {
}
}[/code]

Thank you very much for the detailed answer.

  1. Why didn’t you finish the model implementation? Did you had specific design issue or it’s just the lack of intrest for this feature?

  2. Is it possible for me to implement such a model without using any internal members?

Thank you,
Ido.

  1. Certainly lack of time was the major reason. And few people ask for it.

  2. This sample doesn’t make use of any internal GoXam API.
    You should be able to compile and run it, both in WPF and in Silverlight 4.

BTW, this CustomLayout assumes that all regular nodes have an assigned Location.
If they don’t, they won’t show up in the diagram.

Oh, and for those people who prefer looking at pictures instead of reading words:

Thank you very much.
Ido.

Hello Walter,
What does it takes for me to create a GraphModel derived class and implement any other needed class to have sub-group that may belong to more than one container?
The solution you offer here works, but I think it will cause other things like Layout and navigation to have to be augmented and treat specifically after that change.

Thank you,
Ido.

Yes, you will need to figure out how to do diagram layout. If your application is one where users build it up manually, that shouldn’t be an issue. If your application has to be able to display diagrams from a database where there is no node location information, I suggest customizing ForceDirectedLayout (instead of DiagramLayout as this sample does). The force-directed layout could operate on all of the regular nodes and links, and the “groups” would just be where their members were. But you’ll need to figure out how to handle the links that connect directly to “groups”.

If you are porting an existing application, you probably can adopt whatever it did.

I’m not sure I understand your issue(s) regarding navigation. Nodes and Links are the same as always, and you’re not using Groups. But it’s true that you cannot use the GraphModel or GraphLinksModel methods for dealing with group membership, so you may want to implement your own.

The sample supports the minimal API: the State.Supers collection. But I did extend the functionality by adding the State.Members collection which is setup once after the whole model has been initialized, for the convenience of those methods that wanted to find the members of a “super” node data.

Again, I don’t know exactly what you need and what you want. This sample was just meant to answer your question about having nodes belong to more than one “group”. That’s a non-trivial bit of functionality that I thought deserved its own sample application, just as it did in GoDiagram. How you want to design your own application architecture and make use of this technology isn’t something that I can answer in a forum post or an e-mail message.

Thank you again, and I agree that this is not the place and I’m not asking about the architecture of my application.
We have a working layout code that deal with the fact that a node may be belong to several parents, exactly as you describe it. We are actually using GraphSharp layout algorithms and apply the result to the nodes inside GoDiagram (hopefully soon to be GoXam) so this part is not a problem.

The issue I see with using the sample above (thanks again for it) is that it a view oriented solution, the model of the graph still does not aware of the multiple parent ability.

I am going over the GoXamIntro document to understand what exists out-of-the-box before trying to write my own stuff. I don’t like to reinvent the wheel.

I’ll post here when I will have specific question.

Thanks,
Ido.

Hi,
It’s me again. I’ve play a bit with the code sample you made and one thing that is an issue for me is that moving the inner state a bit cause the super states to overlap each other in a way that is hard to read and hard to interact with.
I know it’s a result of my request to have shared state between nodes, I would like to know if you have a way to make it easier for the user, maybe change the Z-Order of the super nodes after drag to be as good as possible.

Thank you,
Ido.

I’m afraid I have some more issues :\

I’ve add the following lines to the initialization of the SharedState sample to have 3 level deep of super in super in super.

model.FindNodeByKey("D").Supers.Add("B");

model.FindNodeByKey("2").Supers.Add("D");

This cause the super A (operating) to be "one step behind" in layout. It does not take the new location of B (Drying) into account when calculating the bounds, as far as I can tell.

Do you have way to handle it? I'm trying to find a way myself.

Thank you,

Ido.

You can change the Z-order of the Parts if you wish. The easiest way to do that while not changing the Layer that a Part is in is by calling Layer.Remove and then Layer.Add. But you’ll need to figure out what rules to apply, since I don’t know what you want.

Although first you might want to put all of the regular “state” nodes in one Layer that is in front of a Layer containing all of the “super-state” nodes. That will ensure that users can always see and select regular nodes without any “super” nodes blocking them by being in front of them. You can do this by setting go:Part.LayerName=“Background” in the “Super” DataTemplate. Maybe this will be sufficient for your needs.

I have manage to fix the problem of super inside super does not measure correctly by caching the calculated super bounds and use them during the DoLayout operation.

This is the code:

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> private HashSet Invalids = new HashSet();

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> private Dictionary<object, Rect> superMeasures = new Dictionary<object, Rect>();

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”>

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> public override void DoLayout(IEnumerable nodes, IEnumerable links) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> // collect “Super” data to be laid out

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> var supers = new HashSet();

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> // look for any new “Super” nodes

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> foreach (var super in nodes.Where(n => n.Category == “Super”)) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> if (Double.IsNaN(super.Location.X)) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> var sdata = super.Data as State;

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> if (sdata != null) supers.Add(sdata);

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> var model = this.Diagram.Model;

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> foreach (var sid in this.Invalids) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> var sdata = model.FindNodeByKey(sid) as State;

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> if (sdata != null) supers.Add(sdata);

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”>

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> // Repeatedly go through SUPERS collection to layout

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> // those “Super” nodes that don’t have any nested invalid “Super” nodes.

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> while (supers.Count > 0) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> var delay = new HashSet();

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> // set size and location for each invalidated Super state

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> foreach (var sdata in supers) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> if (SuperIsReady(sdata, supers)) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> LayoutSuper(sdata);

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> else {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> delay.Add(sdata);

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> supers = delay;

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”>

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> Invalids.Clear();

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> superMeasures.Clear();

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”>

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> private bool SuperIsReady(State sdata, HashSet supers) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> foreach (var mdata in sdata.Members) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> if (supers.Contains(mdata)) return false;

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> return true;

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”>

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> private void LayoutSuper(State sdata) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> if (sdata == null) return;

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> var model = this.Diagram.Model;

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> var mgr = this.Diagram.PartManager;

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> Node super = mgr.FindNodeForData(sdata, model);

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> if (super != null) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> Rect bounds = ComputerBounds(sdata.Members.Select(d => mgr.FindNodeForData(d, model) as Part));

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> if (!bounds.IsEmpty) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> super.Location = new Point(bounds.X - 5, bounds.Y - 20);

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> var shape = super.FindNamedDescendant(“Shape”);

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> if (shape != null) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> shape.Width = bounds.Width + 10;

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> shape.Height = bounds.Height + 25;

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”>

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> superMeasures.Add(sdata, new Rect(super.Location, new Size(shape.Width, shape.Height)));

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”>

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> private Rect ComputerBounds(IEnumerable parts) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> double top = double.MaxValue,

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> right = double.MinValue,

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> bottom = double.MinValue,

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> left = double.MaxValue;

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”>

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> foreach (var part in parts) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> Rect elementBounds;

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> if (!superMeasures.TryGetValue(part.Data, out elementBounds)) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> elementBounds = part.GetElementBounds(part.VisualElement);

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> if ((

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> ((elementBounds.Width != 0.0) ||

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> (elementBounds.Height != 0.0)) ||

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> ((elementBounds.X != 0.0) || (elementBounds.Y != 0.0))) &&

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> (!double.IsNaN(elementBounds.X) && !double.IsNaN(elementBounds.Y))

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> ) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> if (elementBounds.Left < left) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> left = elementBounds.Left;

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> if (elementBounds.Top < top) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> top = elementBounds.Top;

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> if (elementBounds.Right > right) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> right = elementBounds.Right;

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> if (elementBounds.Bottom > bottom) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> bottom = elementBounds.Bottom;

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”>

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> if (left == double.MaxValue) {

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> return Rect.Empty;

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> return new Rect(left, top, right - left, bottom - top);

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

<font =“apple-style-span”="" face="‘Courier New’, Courier, mono" size=“2”> }

Thank you,
Ido.

The shared state in GoXam almost works perfectly - one thing that is not working so good is the Overview.
When I add an Overview to my diagram window the containers (SuperState) are not layout as they do on the Observed diagram, they have a minimum size, not the size of the children combined.

I’m investigating it but try to see if you have quick answer.

Thank you,
Ido.

The parts that are shown in an Overview are generated using the same templates from the same model data as in your main Diagram. However, the Overview does not use the same CustomLayout, so the Overview’s copies of the “Super” nodes don’t have their “Shape” objects get the same Width and Height as the corresponding Shapes in your Diagram.

You can add Width and Height properties to the State class with setters that call RaisePropertyChanged. Change the “Super” DataTemplate so that the “Shape”'s Width and Height are data-bound to the corresponding new State properties. Change the LayoutSuper method to set the Width and Height of the State data instead of setting the Shape’s properties directly.

I see, that’s great.
I’m currently using the save DataTemplate for both real and overview diagram but I will change them to be much more simplified.

Many thanks,
Ido.