What’s the preferred way to save and restore a M

When I started creating my project, it made sense to separate the Goxam data from our Entity Framework database. Thus, I designed a system that would have a parallel list of our Entity Framework Objects (Arcs and Nodes), and a Goxam list of Links and Nodes. When I save the Diagram, I create an XML representation of our GraphLinksModel. When I restore the Diagram, I go through all of our EntityFramework Objects, create a new

Now that the project has matured, I would like to use proper databinding, and bind our Entity Object’s data directly to the Goxam data. Maintaining the 2 lists of a nightmare, and an easy vector to introducing bugs (bye-bye last Friday!)

So far, it’s going pretty well, in that I figured out how to extend our Entity model to provide things like a Category (for Node Template selection). I can also restore the positions of the Nodes via setting the Location of each Goxam Node’s Location field.

The problems I’m having now are:

1. The Link arrows don’t end at the same place as before the Diagram was saved, and I’m not sure how to set it (see attached pic). If I drag the Node in the Diagram, the links will return to their desired locations. What am I forgetting to set? Do I need to add a Node and a Link to the PartManager via the .AddLinkForData and .AddNodeForData calls for each Node and Link I add to the LinksSource and NodesSource?

2. I can’t get the Node location properties until well after the Diagram is loaded into the Prism view because the Diagram.PartManager.Nodes is not filled until then. Is there a way to get the PartManager to detect changes in the Diagram.Model and create the PartManager properties before the control is finished loading?

3. For Simple models I can load, but for larger (200 – 300 nodes), when I restore using my Bound method, it hands when I add it to my Prism View. I’m not sure how to debug this, but I assume that since I’m likely restoring the model incorrectly, the hang is associate with that. (Update: It doesn’t hang, it just takes a REALLY, REALLY long time to create all the default Node locations, I suppose!)

Is there a better approach to Saving and then Re-loading the Diagram? Can I just Serialize the PartManager, save it, and then Restore it? I don’t want to add any location properties to my Entity Model, as we’re trying to make the Diagram aspect optional if possible.

Thanks!

-Dan

Attachments:

Default positioning (no setting of PartManager properties, because the PartManager isn't created until Control is finished and presented to the user:

Diagram after calling UpdatePositions() via an arbitrary Node_DoubleClick event from the UI

After dragging each of the Nodes, the Arrows re-flow to their proper positions on the Nodes.

Does your database contain the locations (and sizes?) and other properties needed to produce the desired nodes and links? Your DataTemplates should not have changed, except for some property names (maybe).
As a first step, don’t touch the PartManager at all. In fact, see if you can avoid having any code-behind at all except for converters and the code to perform your Entity Framework database query. Really, for read-only diagrams, you should not need much code at all – everything’s in XAML.

Later you can support updating the database. If you are using a Save/Load style persistence, that should be pretty straight-forward. If you want a continuously updated database, implement a Model.Changed event handler that looks for a “CommittedTransaction” change and then performs a database transaction that updates all of the changes in the GoXam transaction that you care to reproduce in your database. The UpdateDemo sample demonstrates this.

You may also need code for customized commands or tools.

Your first screenshot shows that the nodes do not have locations (i.e. Node.Location was bound to NaN,NaN). The default Diagram.Layout assigned locations to those nodes, in a manner that fills up a rectangular area.

The second screenshot implies that the links were automatically routed when the nodes had zero size. Is the size information in your database records? It ought to be represented in your node data class and data bound. Chances are that you have a go:NodeShape in your DataTemplate. It’s Width and Height properties should be bound to those data properties. There are lots of examples of this in the samples demo. However I cannot account for why the links did not automatically reroute when the node sizes changed to have their desired final size. This is not a problem when it is data bound correctly.

Also, make sure you are not getting any data binding warnings or errors. Look in the Output Window for messages.

The database does not contain location information for the Node objects, because they are shared across multiple Run objects, and their location can differ from Run to Run. Therefore, the location information is stored in an XML string that is associated with the specific Run object. It might be possible to put location information in with the Node, but it would really, really be frowned on.

Can I bind the Location information to another object/structure that uses the same Key identifier?
For Category, I created an on-demand generator in the Node partial that uses the Object’s Type information, and that works for that, because the Category doesn’t change from Run to Run. I could do something similar in the Run object, where I have a new Object that knits together the Node (mine) object with the new properties I need for Goxam - although that could pose a problem itself, because the Point is in System.Windows.xxx and I don’t have access to that in our Base classes (and don’t see how to easily add it). I could do this in my Schematic class, though, if it is necessary & is a viable path forward.

[QUOTE=walter]Later you can support updating the database. If you are using a Save/Load style persistence, that should be pretty straight-forward. If you want a continuously updated database, implement a Model.Changed event handler that looks for a “CommittedTransaction” change and then performs a database transaction that updates all of the changes in the GoXam transaction that you care to reproduce in your database. The UpdateDemo sample demonstrates this.

You may also need code for customized commands or tools.
[/quote]
I think I have these covered fairly well - I’ve been adding custom commands for adding nodes, etc, already. Not 100%, but working.

[QUOTE=walter]Your first screenshot shows that the nodes do not have locations (i.e. Node.Location was bound to NaN,NaN). The default Diagram.Layout assigned locations to those nodes, in a manner that fills up a rectangular area.

The second screenshot implies that the links were automatically routed when the nodes had zero size. Is the size information in your database records? It ought to be represented in your node data class and data bound. Chances are that you have a go:NodeShape in your DataTemplate. It’s Width and Height properties should be bound to those data properties. There are lots of examples of this in the samples demo. However I cannot account for why the links did not automatically reroute when the node sizes changed to have their desired final size. This is not a problem when it is data bound correctly.
[/quote]

I’ll look into extending my Node and Arc classes in my Schematic class and add a dynamic Location generator based on the Run object and see how that works.

As for Size, here is one of my NodeTemplates - the Width and Height are hard-coded for now, and Sizing is fixed:

<go:NodePanel MouseLeftButtonDown=“Node_MouseLeftButtonDown” go:Node.Location="{Binding Path=Data.Location, Mode=TwoWay}"
Sizing=“Fixed”
Width=“60” Height=“80”
go:Part.SelectionAdorned=“True”
go:Part.SelectionAdornmentTemplate="{StaticResource ONodeSelectionTemplate}"
go:Node.LinkableFrom=“True” go:Node.LinkableTo=“True”
go:Node.LinkableDuplicates=“True” go:Node.LinkableSelfNode=“True”>
<go:NodeShape go:NodePanel.Figure=“Triangle” Cursor=“Hand”
Fill="{StaticResource ReservoirFill}" StrokeThickness=“2”
Stroke="{Binding Path=Part.IsSelected,
Converter={StaticResource theSelectedBrushConverter}}" />

<ContextMenuService.ContextMenu>



</ContextMenuService.ContextMenu>
</go:NodePanel>

These errors are all obvious to me - 1 template has an intentional bug, and my Arcs don’t have any of the bound information in them at this point. Solvable.

If it doesn’t make sense to store the location with node data, that’s OK.
You could do as you suggested.
But you could also try using a Converter to look up the appropriate location point for a node data, depending on which Diagram is being used.

You could use a Converter to deal with not storing System.Windows.Points, but to generate that upon demand from a string or whatever you can store for the location in the “Run”.

Walter,

Thanks for the help. Ended up with some headbanging on how to get the convert to work right, and how to pass what I wanted, but in the end it was pretty easy. I’m now bound directly to my Node and Arc entity collections. What’s really nice about this approach is that I won’t have to maintain 2 mirrored lists of properties, and I can separate out the Node & Link edits (movement, etc) from the underlying Data store changes. Neat. I just need to make sure that the EntityCollections fire when parts are Added, Deleted, and Modified, and have my SchematicVM listen to those (I’m HOPING, and assuming, EF is already firing those events, though I ready that EntityAssociations changing doesn’t trigger an event, and had to code around that previously)

Thanks again, especially for the weekend responses!
-Dan

I ended up using a MultiConverter, and for reference here’s the implementation:

Here’s the XAML in my NodeTemplates

Here’s the LocationConverter. It’s a bit verbose, and I create the XDocument, etc, every time, but I don’t want to manage changes-to-changes-to-changes of pre-processed objects at the moment.

Likewise, for my links, I want to toggle their visibility based on a value also stored about the run (“HiddenLink”), so I decided to toggle the Link’s LinkPanel visibility instead of the Link’s StrokeThickness that I used previously, and whittled down from 3 ArcTemplates to a Single Arc Template.

The Control’s Code-behind finds the hidden links and toggles them.

You’re welcome. Unlike our competitors we specifically designed GoXam to work reasonably well in the XAML/data-binding/modelling environment of WPF and Silverlight.

Walter,

I’m making good progress thanks to your suggestions. However, I’m stuck on the process of refreshing the screen after items are added to the LinksSource and the NodesSource.

I’ve added items (verified by looking at the GraphLinksModel data I have), but the screen never reflects this. Is there something general I’m just not getting?

It seems like the PartManager and the Model are out of Sync - I started noticing this when I deleted a Link or a Node, and although it would get removed from the LinksSource or NodesSource, the screen wouldn’t update. I’m assuming it’s because I’m not refreshing something I’m overriding, but I’m stumped.

I’ll post some code if you need more info.

Thanks!

Remember that any change to the Model or its data should be performed within a Transaction. That’s probably what’s causing the inconsistency, particularly if that inconsistency is temporary.

I currently have no Transactions (I’m refactoring like mad and decided not to put any in right now). I noticed that my Diagram says that IsInTransaction == true. Is the PartManager waiting for this (automatically created?) transaction to finish before updating?

Is there a way to close all transactions (temporary hack)?

Thanks!

Well then, someone must have started a transaction and not done a corresponding commit or rollback.

I suppose you could do:

  while (myDiagram.Model.UndoManager.TransactionLevel > 0) myDiagram.CommitTransaction("HACK!");

All of the standard commands and tools behave responsibly, of course.

[QUOTE=walter]

Well then, someone must have started a transaction and not done a corresponding commit or rollback.

I suppose you could do:

  while (myDiagram.Model.UndoManager.TransactionLevel > 0) myDiagram.CommitTransaction("HACK!");

All of the standard commands and tools behave responsibly, of course.[/quote]
Sadly, that didn’t do anything (although I saw a “Create Node” transaction at one point, it was likely an artifact of putting things in and testing them out)

In order to do the binding via adding nodes, I needed to extend the GraphLinksModel like this:

Also, if I look at my Diagram, the Links and Nodes are getting added properly (and the right type). I’m adding the nodes via a CustomClickCreatingTool so that I can get the PrototypeData loaded.

and I’ve added a handler to do some clean-up after ClickCreatingTool fires. One thing I noticed is that I my DiagramEventArge e.Part and e.Element are both null, and previously they were loaded with the newly created part. I’m assuming somehow the binding changed things? Is this normal?

The Nodes and Links are definitely getting created - I used my (still not fully refactored) Save code, and it adds the Nodes and Links (in default positions) when I re-load the model.

I’m convinced that the root of my problem lies in this code:
void OasisDiagram_NodeCreated(object sender, DiagramEventArgs e)

In my working, un-bound version, the DiagramEventArgs e contains a Part, and in the non-working, bound version, the DiagramEventArgs contains nothing.

Is it because the standard NodeCreated (NodeCreation) process doesn’t know how to create one of my bound EntityObjects?

If I change my LinksSource and NodesSource to a List and List, it will add the nodes to the Diagram (visibly). If I click-drag to create a link, it is still wanting me to create an override for InsertLink.

So, either I’m overriding the GraphLinksModel wrong (highly possible), or binding directly to an EntityCollection isn’t a good/working idea.

So, I took my EntityCollection, make it into a List, and attached THAT to my model.NodesSource, and it is (mostly) working.

Then I looked at my override for the InsertLink, and noticed that this works:

While this doesn’t work:

Do I need to RaisePropertyChanged for the NodesSource and the LinksSource when Links/Nodes are added/deleted? What’s the proper format to do that? This doesn’t work:

What platform are you running on?
The GraphLinksModel looks at the LinksSource collection in the property setter. If the collection implements INotifyCollectionChanged, it attaches an event handler to deal with changes to the collection, as you would expect. This is the normal situation when dealing with data that meets the expectations for XAML data binding, just as implementing INotifyPropertyChanged is expected for handling changes to property values on individual data objects.

If the collection does not implement that interface, it is your responsibility to do what that event handler would have done. That means calling GraphLinksModel.DoLinkAdded and/or DoLinkRemoved appropriately.

I’m on Win7 x64, running EF 4.0

Hazzah! That was it. My override wasn’t calling those functions. Added that, and all is well.

EntityCollections are strange beasts to work with - EF does all this in-house management of your objects, but does a pretty bad job notifying the outside world of changes. They don’t support PropertyChanged on NavigationProperties natively - see here (EntityFramework doesn’t support PropertyChanged). And here (EntityFramework doesn’t support CollectionChanged)

That last one’s from '09, and the poster’s comment is still true…

Thanks, looks like I’ll have some good progress today!
-Dan