Blazor-Diagrams / Blazor.Diagrams

A fully customizable and extensible all-purpose diagrams library for Blazor
https://blazor-diagrams.zhaytam.com
MIT License
919 stars 176 forks source link

Links not Removed, Diagram doesnt render / update [Help / Bug] #292

Closed AlmightyLks closed 1 year ago

AlmightyLks commented 1 year ago

As you can see in the video below, the Diagram does not update itself, unless I make it by moving stuff around, or moving the canvas. Furthermore, the links are not updated, and they stay where they are What my Render Graph Button does is:

    public void Render()
    {
        if (_originalRootNode is null)
            return;

        Diagram.Links.Clear();
        Diagram.Nodes.Clear();

        _whitelistedNodeIds.Clear();
        WhitelistFilteredNodes(_originalRootNode);

        _treeBuilder.CalculateNodePositions(_originalRootNode);

        if (ResetPosition)
            Diagram.CenterOnNode(_originalRootNode, 1_500);
    }

Getting rid of previous node and link information, and re-calcuating positions (and re-adding the nodes)

A StateHasChanged call wasn't doing the trick, so I thought manually forcing the Diagram to update itself via Diagram.Refresh() at the end of all rendering could do do it - it didn't. Same with going through all Node's within the Diagram, and making them refresh themselves manually

https://user-images.githubusercontent.com/54688434/209883392-e0d6b963-caef-4388-be4f-95a30a47076f.mp4

zHaytam commented 1 year ago

Hello, I see that you're clearing both nodes and links, but still working with a _originalRootNode, is that normal? Also, how is everything re-inserted? Are you adding the same instances again or something else?

AlmightyLks commented 1 year ago

I am re-using the same node objects All I am doing is clearing all nodes and links off the Diagram, and re-adding and re-linking nodes via (simplified)

    private void RenderNode(RepositoryNodeModel current)
    {
        Diagram.Nodes.Add(current);

        if (current.Parent is not null)
        {
            var link = new LinkModel(current.Parent, current)
            {
                TargetMarker = LinkMarker.Arrow
            };
            link.SetSourcePort(current.Parent.GetPort(PortAlignment.Bottom));
            link.SetTargetPort(current.GetPort(PortAlignment.Top));
            Diagram.Links.Add(link);
        }
    }

to the diagram

zHaytam commented 1 year ago

Will links for example ever change? Because I don't see why you would clear everything just to re-insert the same instances, all you need to do is change the node's position (SetPosition) if the links stay the same, they will be updated automatically

AlmightyLks commented 1 year ago

As you can see at the top right, I have a filter option
I want to be able to dynamically decide which nodes (and parent-child links) to display, based on which info was specified
It makes a bit more sense, once I also show the node-whitelist check that lies within the RenderNode function 😄

    private void RenderNode(RepositoryNodeModel current)
    {
        if (!_whitelistedNodeIds.Contains(current.Item.Id))
            return;

        Diagram.Nodes.Add(current);

        if (current.Parent is not null)
        {
            var link = new LinkModel(current.Parent, current)
            {
                TargetMarker = LinkMarker.Arrow
            };
            link.SetSourcePort(current.Parent.GetPort(PortAlignment.Bottom));
            link.SetTargetPort(current.GetPort(PortAlignment.Top));
            Diagram.Links.Add(link);
        }
    }

https://user-images.githubusercontent.com/54688434/209936467-5fb0d0a8-f065-4f8f-8c5e-af274d94da0f.mp4

I deemed, that first setting up a whitelist, and then go through the normal rendering process off the original dataset, with a whitelist check, would be the easiest to go about this As opposed to fiddling with the Diagram-stored nodes and links, especially if I need to re-add nodes, which I would have to find by going through the data structure (parent-children) anyways 😄

zHaytam commented 1 year ago

Can you give me an idea of what CalculateNodePositions does?

AlmightyLks commented 1 year ago

https://github.com/AlmightyLks/ForkHierarchy/blob/main/ForkHierarchy.Core/Helpers/TreeBuilder.cs

Essentially what it does is going up and down through the entire parent-child hierarchy to evenly place the nodes next to each other, based on things like, how many siblings a node has and all that stuff.

It sets the NodeModel-Positions for each and every node directly + at the very end, when setting each and every node's final position, it executes an Action, passing the node, which I set to be my "RenderNode" function. Every time a Node's final position was set, it is added onto the Diagram

https://github.com/AlmightyLks/ForkHierarchy/blob/main/ForkHierarchy.Core/Helpers/TreeBuilder.cs#L60 https://github.com/AlmightyLks/ForkHierarchy/blob/main/ForkHierarchy/ViewModels/HierarchyViewModel.cs#L39

zHaytam commented 1 year ago

Instead of (in your node)

    public double X { get => Position.X; set => Position = new Point(value, Position.Y); }
    public double Y { get => Position.Y; set => Position = new Point(Position.X, value); }

Use SetPosition.

Since you're using the same instances, the ports position isn't updated. SetPosition updates the ports position (with the delta), which should fix your issue. Basically, your links are using the same ports, but the ports have their old positions.

Please try this and tell me if it fixes the issue

AlmightyLks commented 1 year ago

God damn! Yes that did it! Thanks very much

zHaytam commented 1 year ago

Great !! Maybe I should make Position private or init-only to avoid this issue in the future.

AlmightyLks commented 1 year ago

Well, can you think of a reason to have it public-settable?