Modifying Model from code behind properly

Hi,

I try to modify the model programmatically but I have some problems. 2 to be exact. What I trying to do is when a Deaerator (a node) is dropped in the diagram, programmatically I create a Boiler and link this boiler to the Deaerator at the appropriate ports.

First problem:
The link doesn’t start or end from/to a port (see the picture below and the code to link these 2 nodes).

Second problem:
I don’t know why, but 2 Boilers seem to be added to the Model.NodeSource…

What I’m doing wrong?

The code:

private void HandleFlowSheetModelChanged(object sender, Northwoods.GoXam.Model.ModelChangedEventArgs e)
{
<span =“Apple-tab-span” style=“white-space:pre”> //…

        FlowSheetModel.StartTransaction("Create new Boiler");

        //Create the new Boiler
        IBoiler boiler = ViewModelFactory.CreateInstance(typeof(IBoiler), _DialogService) as IBoiler;
        
        //Add the new Boiler to the model
        FlowSheetModel.AddNode(boiler);

        //Add the link between the Deaerator and the Boiler
        FlowSheetModel.AddLink(deaerator,deaerator.OutVent.ToString(),boiler, boiler.InAir.ToString());
        

        FlowSheetModel.CommitTransaction("Create new Boiler");
    }

I am guessing that the problem is that your code is invoked twice: once for the temporary node that is added during the drag from the other diagram, and once for the permanent node that is added upon the drop.

It is common to implement a Diagram.ExternalObjectsDropped event handler instead of a Model.Changed handler. But if you insist on a Model.Changed event handler and do not want to look at the target Diagram, you could see if SkipsUndoManager is true to see if it’s a temporary creation.

Ok thank, it works with the SkipUndoManager approach.

And what about the other problem that I had concerning the link and the ports?

What are the values of GraphLinksModel.LinkFromPath, LinkFromParameterPath, LinkToPath, and LinkToParameterPath?

If your link data class inherits from GraphLinksModelLinkData, those four properties automatically get the appropriate values. But if you implemented your own link data class from scratch, you need to set those four properties (and others) appropriately.

I implemented my own link data class, and those properties are set correctly (see the code below). This only occurs when the link are added programmatically. When link are added in the diagram, everything works well.

public class GoXamModel: GraphLinksModel<INode, Guid, String, IStream>
{

public GoXamModel()
{
base.LinkFromParameterPath = “FromPort”;
base.LinkToParameterPath = “ToPort”;
base.LinkFromPath = “FromNodeId”;
base.LinkToPath = “ToNodeId”;

    }

}

public interface IStream
{
Guid FromNodeId { get ; set; }
Guid ToNodeId { get; set; }
String FromPort { get; set; }
String ToPort { get; set; }
}

The implementation of GraphLinksModel.InsertLink (called by your call to AddLink) creates an instance of the link data by calling Activator.CreateInstance. Maybe that is not what you want. If so, then I think you should create the instance of the link data yourself, probably with your own factory method, make sure it has the desired properties, and then call AddLink(linkdata). Or you can override GraphLinksModel.InsertLink to create the link data in the way that you want and then call InsertLink(data).

I already overwrote the GraphLinksModel.InsertLink method. Other idea??

protected override IStream InsertLink(INode fromdata, string fromparam, INode todata, string toparam)
{

        IMStream newMStream = ModelFactory.ModelFactory.CreateInstance(typeof(IMStream)) as IMStream;
        newMStream.FromPort = fromparam;
        newMStream.FromNodeId = fromdata.Id;
        newMStream.ToPort = toparam;
        newMStream.ToNodeId = todata.Id;

        ((ObservableCollection<IStream>)LinksSource).Add(newMStream);

        return newMStream;

}

Ah, so you were doing the right thing there already. That’s good.

Is it now properly creating the single additional node and linking to it? The only problem is that the link is not connecting directly with the ports but to the node as a whole? If you move either node manually afterwards, does the link’s route fix itself?

Yep exactly!

Nop, it still the same.

The not-fixing-the-route-after-moving-one-node behavior says that the link really thinks it is connecting with the whole node, not with a particular port. That could be for any number of reasons, including:

  • the link data has the wrong port id information
  • the GraphLinksModel is using the wrong port parameter properties (but in this case you have confirmed that this is not the problem)
  • the FrameworkElement ports in the Nodes do not have the correct go:Node.PortId attached property values

Well, I tripple checked and the link data has the right info. I added Ids in tooltip for nodes, ports and link (from, fromport, to, toport) and everything seems OK. I’m really bothered by this problem…

OK, I just tried modifying the Logic Circuit sample to do what I think you are trying to do. I made no changes to the XAML. I only added the following event handler:

myDiagram.ExternalObjectsDropped += (s, e) => { foreach (Part part in myDiagram.SelectedParts) { var node = part as Node; if (node == null) continue; var loc = node.Location; loc.X -= 100; var gate = node.Data as GateData; if (gate.GateType != "TwoInOneOut") continue; var not = new GateData() { Figure=NodeFigure.Inverter, GateType="OneInOneOut", Location=loc }; model.AddNode(not); var wire = new WireData() { From=not.Key, FromPort="", To=gate.Key, ToPort="In1" }; model.AddLink(wire); } };
It works as I think you would expect, adding a “Not” as an input to the first input port.

That’s my fault. My apologies!

Thank for your help again!

Thanks for following up. So I guess the problem boiled down to your OutVent and InAir value type(s) not implementing ToString in the manner you wanted.