OnNodeAdded / OnModelChanged

Hello,

I use diagram to visualize my internal structure. Thus I need to add item and remove item in my structure when we cut /add nodes in the diagram.

I think that I have two ways to do that :
using OnNodeAdded or OnModelChanged.

Can you explain me what is the better way ?

I was able to catch adding of node in the both functions but, I realize that the functions are called twice.
It doesn’t matter for delete because I can empty my item and then don’t recall my remove function, but I don’t find tricks for adding node.

(I’m in wpf development).

Thanks
Aurore

I think your “internal structures” are the data that is conceptually part of your diagram model.

Most of PartManager just adds or removes Nodes or Links based on changes to the diagram’s model. None of the methods such as PartManager.OnNodeAdded are supposed to make any changes to the model.

[Actually, there are some PartManager methods that do modify the model, such as PartManager.CopyParts and PartManager.DeleteParts. Those are useful methods that could not go in any model class because they know about Nodes and Links. But it seemed more logical to put those methods there than in any tool or in the Diagram class itself.]

The various tools and the diagram’s CommandHandler all modify the model by calling methods of the model. CommandHandler.Paste for example calls IDiagramModel.AddCollectionCopy to copy the data in the clipboard into the diagram. The DraggingTool, when the user does a control-drop, calls PartManager.CopyParts, which in turn calls IDiagramModel.AddCollectionCopy. CommandHandler.Delete calls PartManager.DeleteParts, which in turn calls IDiagramModel.RemoveNode or RemoveLink.

So the simple answer to your question is OnModelChanged.

You have a choice. Do you want to maintain your internal data structures continually, or only at the end of a committed model transaction?

If you want to update continuously, take a look at the protected methods of the model class that you are using. You’ll want to override some of them to do what you want. For example, you might override InsertNode and DeleteNode and InsertLink and DeleteLink.

An alternative to overriding those model methods is to implement a Model.Changed event handler to look for cases where node data or link data is added or removed or modified.

If you want to update only when a transaction commits, you might find this discussion useful: http://www.nwoods.com/forum/forum_posts.asp?TID=3211.

Yes !

Thanks for the explanation.

Here’s my code :

public class MyGraphLinkModel : GraphLinksModel<MyNodeData, String, String, MyLinkData>
    {
        protected override void InsertNode(MyNodeData nodedata)
        {
            base.InsertNode(nodedata);
            if (nodedata == null) return;
          
                switch (nodedata.Category)
                {
                    case "Part": break;
                    case "Variable":
                        if (nodedata.Item == null)
                        {
                            SphVarText oText = new SphVarText();
                            oText.Name = Session.Survey.GetUniqueItemName("variable");
                            Session.Survey.Add(oText);
                            nodedata.Key = oText.ID.ToString();
                            nodedata.Item = oText;
                   }
                   break;
                
            }
        }
    }

Hello,

All works until I decided to override DeleteNode… Confused

   protected override void DeleteNode(MyNodeData nodedata)
   {
       base.DeleteNode(nodedata);
       if (nodedata == null || (nodedata != null && nodedata.Item == null)) return;
         Session.Survey.Remove(nodedata.Item.Item);
     }

Then I realize that my node is added but also removed ! I see the node in the diagram but in my structure it’s not !

Well, I made some traces (see below).

Maybe I missed something in this post.

Tell me if I’m wrong, I need to detect in modelchanged where the drop transaction is finished, and then add my new node in my structure.

But if I do so, I modify the model (a node’s data) in OnModelChanged but you said that

I’m sorry, i’m lost…

Thanks

18/03/2010 10:30:53    Add Variable : survey nb item before : 44
18/03/2010 10:30:53    Add Variable : survey nb item after : 45

18/03/2010 10:30:54    OnModelChanged : !  AddedNode: 54
18/03/2010 10:30:54    OnModelChanged : !  AddedNode: 54
18/03/2010 10:30:54    OnModelChanged : !  RemovingNode: 54
18/03/2010 10:30:54    OnModelChanged : !  RemovingNode: 54
18/03/2010 10:30:54    OnModelChanged : !  RemovedNode: 54
18/03/2010 10:30:54    OnModelChanged : !  RemovedNode: 54
18/03/2010 10:30:54    OnModelChanged : !  StartedTransaction: Drop
18/03/2010 10:30:54    OnModelChanged : !  StartedTransaction: Drop
18/03/2010 10:30:54    OnModelChanged : !  AddedNode: 54
18/03/2010 10:30:54    OnModelChanged : !  AddedNode: 54
18/03/2010 10:30:54    OnModelChanged : !  CommittedTransaction: ExternalCopy
18/03/2010 10:30:54    OnModelChanged : !  CommittedTransaction: ExternalCopy

Could you explain me why addednode and removednode are called twice ?

To be sure that I’m not wrong I modified your FlowChart sample to add the same traces :

OnNodeAdded Node:Read
OnModelChanged !  AddedNode: Read
OnModelChanged !  Property Location: Read old: 0;305,76 new: 17,24;328,04
OnModelChanged !  Property Location: Read old: 17,24;328,04 new: 38,24;329,04
OnModelChanged !  Property Location: Read old: 38,24;329,04 new: 41,24;329,04
OnModelChanged !  Property Location: Read old: 41,24;329,04 new: 46,24;329,04
OnModelChanged !  Property Location: Read old: 46,24;329,04 new: 47,24;329,04
OnModelChanged !  Property Location: Read old: 47,24;329,04 new: 48,24;329,04
OnModelChanged !  Property Location: Read old: 48,24;329,04 new: 49,24;329,04
OnModelChanged !  Property Location: Read old: 49,24;329,04 new: 50,24;328,04
OnModelChanged !  Property Location: Read old: 50,24;328,04 new: 51,24;328,04
OnModelChanged !  Property Location: Read old: 51,24;328,04 new: 52,24;328,04
OnModelChanged !  Property Location: Read old: 52,24;328,04 new: 53,24;328,04
OnModelChanged !  Property Location: Read old: 53,24;328,04 new: 54,24;328,04
OnModelChanged !  Property Location: Read old: 54,24;328,04 new: 56,24;327,04
OnModelChanged !  Property Location: Read old: 56,24;327,04 new: 57,24;327,04
OnModelChanged !  Property Location: Read old: 57,24;327,04 new: 59,24;327,04
OnModelChanged !  Property Location: Read old: 59,24;327,04 new: 64,24;327,04
OnModelChanged !  Property Location: Read old: 64,24;327,04 new: 67,24;327,04
OnModelChanged !  Property Location: Read old: 67,24;327,04 new: 71,24;327,04
OnModelChanged !  Property Location: Read old: 71,24;327,04 new: 78,24;327,04
OnModelChanged !  Property Location: Read old: 78,24;327,04 new: 80,24;327,04
OnModelChanged !  Property Location: Read old: 80,24;327,04 new: 82,24;327,04
OnModelChanged !  Property Location: Read old: 82,24;327,04 new: 84,24;327,04
OnModelChanged !  Property Location: Read old: 84,24;327,04 new: 86,24;327,04
OnModelChanged !  Property Location: Read old: 86,24;327,04 new: 87,24;327,04
OnNodeRemoved Node:Read
OnModelChanged !  RemovingNode: Read
OnModelChanged !  RemovedNode: Read
OnModelChanged !  StartedTransaction: Drop
OnNodeAdded Node:Read
OnModelChanged !  AddedNode: Read
OnModelChanged !  Property Location: Read old: 0;305,76 new: 87,24;327,04
OnModelChanged !  CommittedTransaction: ExternalCopy old: CompoundEdit: ExternalCopy 2 edits, complete
OnModelChanged !  StartedTransaction: Layout
OnModelChanged !  CommittedTransaction: Layout

There are two issues here that I see.

First, I do not understand why your trace has duplicated lines of output:

18/03/2010 10:30:54    OnModelChanged : !  AddedNode: 54
18/03/2010 10:30:54    OnModelChanged : !  AddedNode: 54
As you demonstrate in the modified FlowChart application, that does not happen in the sample. I am guessing it is because your implementation of InsertNode calls both the base method and your own Survey.Add method, and because the latter method modifies the collection of nodes again.

The second issue is why in the external drag-and-drop there is a sequence of AddedNode, RemovedNode, AddedNode.

You’ll note that only the last AddedNode is within a transaction. The first AddedNode is when the temporary node that represents the dragged object is created so that it can be dragged by the DraggingTool. When the drop occurs, or if the drop is not completed, or if the drop is canceled, that temporary node is removed. Finally, if there is a successful drop, there is a transaction and the “real” node is copied as part of the drop operation.

[QUOTE=walter]There are two issues here that I see.

First, I do not understand why your trace has duplicated lines of output:

18/03/2010 10:30:54    OnModelChanged : !  AddedNode: 54
18/03/2010 10:30:54    OnModelChanged : !  AddedNode: 54
As you demonstrate in the modified FlowChart application, that does not happen in the sample. I am guessing it is because your implementation of InsertNode calls both the base method and your own Survey.Add method, and because the latter method modifies the collection of nodes again.

[/quote]

Yes, i understand that in your example, the node is temporary construct, until the drop is complete. Well, In my Survey.add function I only add a node in my structure, not in yours.
When I try to add my data in the addingNode in drop transaction, I don’t see my data in the diagram. I suspect that it is because the data change is not taking into account by the model.

When you say :

[QUOTE=walter] Finally, if there is a successful drop, there is a transaction and the “real” node is copied as part of the drop operation.
[/quote]
do you mean that the temporary node is copied to the real node, what’s for data ? is it a deepth copy or just adress copy ?

Thanks for all yours explanations
Aurore

  1. If Survey.Add is adding to your data structures but not to the model’s, I don’t understand why you are seeing duplicated OnModelChanged - AddedNode events. If this is a problem for you, I suggest you break at that point and see who’s calling – why it is change happening?

  2. The temporary Node is thrown away and the corresponding node data is removed from the model. The drag-and-drop data is copied into the model, which causes a new Node to be constructed based on that new data. That might be exactly the same as the dragged data, but might be different (for example due to requiring a unique key).

Hello,

I’ve found why I have duplicated call to OnModelChanged,

it’s about my overview use.

I modify your flow chart sample, to add the overview window opening as in my code.

After this modification, here’s the traces :

OnModelChanged !  StartedTransaction: Drop
OnModelChanged !  StartedTransaction: Drop
OnNodeAdded Node:Conditional
OnModelChanged !  AddedNode: Conditional
OnNodeAdded Node:Conditional
OnModelChanged !  AddedNode: Conditional
OnModelChanged !  Property Location: Conditional old: 0;309,9 new: 82,24;357,04
OnModelChanged !  Property Location: Conditional old: 0;309,9 new: 82,24;357,04
OnModelChanged !  CommittedTransaction: ExternalCopy old: CompoundEdit: ExternalCopy 2 edits, complete
OnModelChanged !  CommittedTransaction: ExternalCopy old: CompoundEdit: ExternalCopy 2 edits, complete
OnModelChanged !  StartedTransaction: Layout
OnModelChanged !  StartedTransaction: Layout
OnModelChanged !  CommittedTransaction: Layout
OnModelChanged !  CommittedTransaction: Layout

Have you an idea ?

Here’s my simplified windows overview xaml :

<Window x:Class="FlowChart.Wdw_Zoom"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:go="http://schemas.nwoods.com/GoXam"
        Title="Wdw_Zoom" Height="300" Width="300" MinWidth="300" MinHeight="300">
    
    <!-- ====================== Overview / Zoom ================== -->
    <DockPanel>

        <StackPanel Orientation="Horizontal"
                    DockPanel.Dock="Bottom"
                    HorizontalAlignment="Center"
                    Margin="0,10,0,0">
            <Slider Minimum="20"
                    Maximum="400"
                    TickPlacement="TopLeft"
                    TickFrequency="1"
                    IsSnapToTickEnabled="True"
                    Name="sliderZoom"
                    Width="200"
                    Value="100">
            </Slider>
            <TextBox Text="{Binding ElementName=sliderZoom, Path=Value, Mode=TwoWay}"
                     Width="30" />
            <Label Content="%" />
        </StackPanel>
        <Border 
                Background="WhiteSmoke"
                HorizontalAlignment="Left"
                VerticalAlignment="Top"
                BorderBrush="Black"
                BorderThickness="1">
            <Border BorderBrush="White"
                    BorderThickness="1">
                <Border BorderBrush="Black"
                        BorderThickness="1">
                    <go:Overview x:Name="myOverview"   HorizontalAlignment="Left" VerticalAlignment="Top"                          Background="WhiteSmoke"
                                  />
                </Border>
            </Border>
        </Border>
       
    </DockPanel>
</Window>

And my modification in your FlowChart Code :

String xml = Demo.MainPage.Instance.LoadText("FlowChart", "xml");
      model.Load<MyNodeData, MyLinkData>(XElement.Parse(xml), "MyNodeData", "MyLinkData");
      model.Modifiable = true;
      model.HasUndoManager = true;
      myDiagram.Model = model;

      oWdwZoom = new Wdw_Zoom();
      oWdwZoom.Topmost = true;
      //both initialisation make problem
      //oWdwZoom.myOverview.Observed = myDiagram;
 this.Loaded += (s, e) => { oWdwZoom.myOverview.Observed = myDiagram; };
      oWdwZoom.DataContext = myDiagram;
      oWdwZoom.Show();

     

To finish the fact rendering : in my code i don’t initialize the overview window in the constructor but in the loaded windows function.

As you’d expect, an Overview control shares its Model with its Observed Diagram.

However, it does not share its PartManager with the observed Diagram’s PartManager. This is because each Overview has to have its own sets of Nodes and Links corresponding to the data in the observed model.

Did you override PartManager.OnModelChanged?

If so, the Overview makes its own copy of your CustomPartManager class. So I suggest you replace it with a standard PartManager in your initialization code:

myOverview.Observed = myDiagram;
myOverview.PartManager = new PartManager();

Hello,

Thanks for your excellent response.

It solve my problem and also my problem of node.location !

Very good.

Aurore