Adding a huge amount of Nodes asynchronously

Hi everybody!

I really need you help with SfDiagram. I'm creating an app for showing product variants. I'd lie to visualize the variant tree in SfDiagram but the number of nodes can easily reach 1.000.000 and more.

I'm now trying to first create the nodes in the background and then visualize them like this (this is just a example):

<syncfusion:SfDiagram x:Name="diagram"
  Nodes="{Binding Nodes}"
  Connectors="{Binding Connectors}"
  SelectedItems="{Binding SelectedNode}"
  LayoutManager="{Binding Layout}"
  Constraints="Default,Virtualize,Outline"/>

public async void UpdateDiagramAsync()
{
await Task.Run(() => GenerateNodes());

Nodes.Clear();
foreach(CustomNode node in Nodes_Temp)
{
Nodes.Add(node);
}

Connectors.Clear();
foreach (ConnectorViewModel connector in Connectors_Temp)
{
Connectors.Add(connector);
}

diagram.LayoutManager.Layout.UpdateLayout();
}

private Task GenerateNodes()
{
Exception error = null;

Nodes_Temp = new NodeCollection();
Connectors_Temp = new ConnectorCollection();

double StandardHeight = Properties.Settings.Default.dStandardSize_Rectangle_Start_Height;
double StandardWidth = Properties.Settings.Default.dStandardSize_Rectangle_Start_Width;

NodeCollection Temp_Nodes = new NodeCollection();
CustomNode node = AddNode(80, 53.75, StandardWidth, StandardHeight, 1, 0, "Content", "Rectangle");
Nodes_Temp.Add(node);
for(int i = 0; i <= 100000000; i++)
{
CustomNode node = AddNode(80, 53.75, StandardWidth, StandardHeight, i, i - 1, "Content", "Rectangle");
Nodes_Temp.Add(node);
//Now create connectors for all nodes
Connectors_Temp.Add(CreateConnector(node.ID.ToString(), (i+1).TosTring()));

createdNodes_new.Add(node_new);
}
}

 private CustomNode AddNode(double offsetX, double offsetY, double width, double height, string ID, string Parent_ID, string content, string shape)
{
CustomNode node = new CustomNode();
node.OffsetX = offsetX;
node.OffsetY = offsetY;
node.UnitHeight = height;
node.UnitWidth = width;

node.CustomID = ID;
node.CustomID_Parent = Parent_ID;
node.CustomContent = content;
node.CustomShape = shape;
node.CustomStyle = "ShapeStyle";
node.CustomContentTemplate = "TemplateNode_Node";

return node;
}

private ConnectorViewModel CreateConnector(string source, string target)
{
ConnectorViewModel connector = new ConnectorViewModel()
{
SourceNodeID = source,
TargetNodeID = target,
TargetDecoratorStyle = Application.Current.Resources["TargetDecoratorStyle"] as Style,
ConnectorGeometryStyle = Application.Current.Resources["ConnectorGeometryStyle"] as Style
};

return connector;
}

What would be the best way to create such an amount of Nodes?

Thank you very much for your help?

Best regards


Victor

16 Replies

RA Ranjitha Amirthalingam Syncfusion Team June 4, 2020 03:10 PM UTC

Hi Victor, 
 
Please use Datasource to achieve “to first create the nodes in the background and then visualize them". If you use datasource, then nodes has been created automatically to populate a layout. Also, your requirement is to generate layout with huge number of nodes and connectors, there will be performance impact based on your system ram memory. 
 
For details about DataSource, please visit our online help documentation link. 
 
 
 
Regards, 
Ranjitha A. 



VD Victor Dienstbier June 25, 2020 06:24 AM UTC

Hi!


Thank you for your support :-)

Unfortunately I cannot make my app work with your example as soon as I add this:

                 diagram.DataSourceSettings = new DataSourceSettings()
                {
                    DataSource = Allgemein._viewModel_VariantCustomization.Nodes,
                    Id = "ID",
                    ParentId = "Parent",
                    Root = ""
                };

                diagram.LayoutManager = new LayoutManager()
                {
                    Layout = new DirectedTreeLayout()
                    {
                        HorizontalSpacing = 30,
                        VerticalSpacing = 50,
                        Orientation = TreeOrientation.TopToBottom,
                        Type = LayoutType.Hierarchical,
                        AvoidSegmentOverlapping = false,
                    },
                };

The following error occurs:

HResult: -2147467261
Message: Der Wert darf nicht NULL sein. Parametername: source

If have created the diagram like this: <syncfusion:SfDiagram x:Name="diagram" />

Which basicaly means: The Value is not allowed to be NULL. Propertyname: Source

Am I missing something? The source definately has Nodes in it...

BR

Victor


RA Ranjitha Amirthalingam Syncfusion Team June 25, 2020 07:25 AM UTC

Hi Victor, 
 
Sorry, we could not able to replicate the issue with the details specified. We suspect that issue may occur at DataSource. So, please provide us some data’s specified in Allgemein._viewModel_VariantCustomization.Nodes collection. Also , please share us the complete stack trace. This helps us to track the exact cause of the issue. 
 
 
Regards, 
Ranjitha A. 



VD Victor Dienstbier June 25, 2020 12:49 PM UTC

Is it anyhow possible to share you the complete Program without posting it here?

Then I can send you the app - it is not that big :-)


RA Ranjitha Amirthalingam Syncfusion Team June 26, 2020 06:05 AM UTC

Hi Victor, 
 
You can mail your complete application to our Support mail box [email protected]. We will keep your application as confidential. 
 
 
Regards, 
Ranjitha A. 



VD Victor Dienstbier June 30, 2020 12:46 PM UTC

Hi :-)

Did you get my message with the App?

BR

Victor


RA Ranjitha Amirthalingam Syncfusion Team July 1, 2020 06:22 AM UTC

Hi Victor, 
 
We didn’t receive any mail from your end. Can you please resend it or send it through drop box. 
 
Regards, 
Ranjitha A.  



RA Ranjitha Amirthalingam Syncfusion Team July 1, 2020 01:00 PM UTC

Hi Victor, 
 
Thanks for sharing the application. 
 
We could able to reproduce the issue as specified in the below screenshot. 

Screenshot: 
 

And we have solved this issue by uncommenting the below binding Nodes, Connectors collection logic code in your application. Please refer to the below code example. 
 
Code Example: 
<syncfusion:SfDiagram x:Name="diagram"            
                                                          Nodes="{Binding Nodes}" 
                                                          Connectors="{Binding Connectors}" 
                                                          SelectedItems="{Binding SelectedNode}" 
                                                          LayoutManager="{Binding Layout}" 
                                                          Constraints="Default,Virtualize,Outline"/> 
 
                                    <!--<syncfusion:SfDiagram x:Name="diagram" />--> 



Regards, 
Ranjitha A. 



VD Victor Dienstbier July 9, 2020 01:12 PM UTC

Hi!

Thanks for your answer! You are right! This works, but I did comment it out on purpose: You first wrote, that I should change to DataSource and therefore I added this in the Class pagVariantCustomization.xaml.cs:

private void PagVariantCustomization_Loaded(object sender, RoutedEventArgs e)
        {
            //Entfernen vom Dublikate-Button
            QuickCommandCollection commands = (diagram.SelectedItems as SelectorViewModel).Commands as QuickCommandCollection;
            commands.RemoveAt(2);
            commands.RemoveAt(1);

            diagram.KnownTypes = () => new List()
            {
                typeof(ConfigurationItem)
            };

            //DataSoruce Setzen
            if (Allgemein._viewModel_VariantCustomization != null)
            {
                diagram.DataSourceSettings = new DataSourceSettings()
                {
                    DataSource = Allgemein._viewModel_VariantCustomization.Nodes,
                    Id = "ID",
                    ParentId = "Parent",
                    Root = ""
                };

                diagram.LayoutManager = new LayoutManager()
                {
                    Layout = new DirectedTreeLayout()
                    {
                        HorizontalSpacing = 30,
                        VerticalSpacing = 50,
                        Orientation = TreeOrientation.TopToBottom,
                        Type = LayoutType.Hierarchical,
                        AvoidSegmentOverlapping = false,
                    },
                };
            }
        }

And if I do this the error occurs.

I did it in the class, because I don't know how to use the ObservableCollection Nodes defined in the ViewModel as a StaticResource like this e.g.:

                                       DataSource="{Binding Nodes}" />


This is not working :-(

What do I have to do?

Thank you very much!


Victor



RA Ranjitha Amirthalingam Syncfusion Team July 10, 2020 01:32 PM UTC

Hi Victor, 
 
Please find the response as below. 

S.No 
Query 
Response 
1 
Thanks for your answer! You are right! This works, but I did comment it out on purpose: You first wrote, that I should change to DataSource and therefore I added this in the Class pagVariantCustomization.xaml.cs: 
The error occurs due to the Connectors and Nodes collection not initialized in the diagram(view). If you don’t want to bind the viewmodel with view, then you need to initialize the connectors and nodes collection in the view class. 
 
Code Example: 
public pagVariantCustomization() 
       
            InitializeComponent(); 

            diagram.Nodes = new NodeCollection(); 
            diagram.Connectors = new ConnectorCollection(); 
            Loaded += PagVariantCustomization_Loaded; 


I did it in the class, because I don't know how to use the ObservableCollection Nodes defined in the ViewModel as a StaticResource like this e.g.: 
 
<="" div="" style="box-sizing: border-box;"> 
                                       DataSource="{Binding Nodes}" /> 
 
This is not working :-( 
 
What do I have to do? 
 
If you want to define the datasource in xaml as a static resource, you can define it as like in the below page. 
 
 
And in your sample, we could find that “Allgemein._viewModel_VariantCustomization.Nodes” collection is empty. So, it returns empty datasource. 
 
If you want to create a Layout using DataSource, you must define Id and ParentId values for each and every data in the datasource. Based on the information,  Diagram can be populated with the nodes and connectors. 

Please let us know if you need further assistance on this. We are happy to assist you. 
 
Regards, 
Ranjitha A. 



VD Victor Dienstbier July 14, 2020 10:18 AM UTC

Hi Ranjitha,

Thanks for all your help! I couldn't make it work though :-(

I have created a new and small example to show you waht I want to do... But it always throws the same error that the source should not be null...
I'd like to use the DataSource and present the Business Class "ConfigurationItem" from the ObservableCollection in the SfDiagram.

Is this better for you to understand, what the problem is?

Have a great day!

Victor

Attachment: SfDiagramApp_9aed4baa.zip


RA Ranjitha Amirthalingam Syncfusion Team July 14, 2020 02:04 PM UTC

Hi Victor, 
 
Thanks for sharing the simple sample to represent the issue. 
 
As updated earlier, we have fixed this issue by initializing node and connector collection in the Diagram. 
 
Code example: 
 
   <syncfusion:SfDiagram x:Name="diagram" 
                              DataSourceSettings="{Binding DataSourceForDiagram}" 
                              LayoutManager="{Binding LayoutManagerForDiagram}"> 
            <syncfusion:SfDiagram.Nodes> 
                <syncfusion:NodeCollection></syncfusion:NodeCollection> 
            </syncfusion:SfDiagram.Nodes> 
            <syncfusion:SfDiagram.Connectors> 
                <syncfusion:ConnectorCollection></syncfusion:ConnectorCollection> 
            </syncfusion:SfDiagram.Connectors> 
        </syncfusion:SfDiagram> 

Why we need to initialize Node and Connector collection in Diagram while using DataSource? 
 
Internally we have processed the data’s from datasource and append those data’s as content of the Node. Because node is used to visualize the data and connector is used to visualize the relationship. During the layout generation, nodes and connectors can be generated automatically with the information provided through data source and those items will be added to NodeCollection and ConnectorCollection of SfDiagram respectively.It is must to initialize the empty Node and Connector collection in Diagram while creating Layout using DataSource. 
 
 
Regards, 
Ranjitha A. 



VD Victor Dienstbier July 16, 2020 05:49 AM UTC

Hi Ranjitha,

Thank you very much! That explanation was perfect and I now know why this happend! I got it to work like you wrote:

<Page x:Class="WILO_Vario.pagVariants"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:syncfusion="http://schemas.syncfusion.com/wpf"
      xmlns:local="clr-namespace:WILO_Vario"
      mc:Ignorable="d" 
      d:DesignHeight="450" d:DesignWidth="800"
      Title="pagVariants"
      Background="White" 
      DataContext="{StaticResource viewModel_Variants}">

    <Page.Resources>
        <Style TargetType="syncfusion:Node">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <Border  Background="{Binding ColorHEX_Background}" 
                                 BorderBrush="{Binding ColorHEX_Background}" 
                                 Width="{Binding Width}" 
                                 Height="{Binding Height}" 
                                 VerticalAlignment="Center" 
                                 HorizontalAlignment="Center">
                            <TextBlock Margin="5" 
                                       TextWrapping="Wrap" 
                                       FontSize="12" 
                                       Foreground="{Binding ColorHEX_Foreground}"
                                       Text="{Binding Characteristics}" 
                                       FontFamily="Segoe UI"  
                                       VerticalAlignment="Center" 
                                       HorizontalAlignment="Center"/>
                        </Border>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Page.Resources>

    <Grid>
        <syncfusion:SfDiagram x:Name="diagram"
                              DataSourceSettings="{Binding DataSourceForDiagram}"
                              LayoutManager="{Binding LayoutManagerForDiagram}"
                              Constraints="Default,Virtualize">
            <syncfusion:SfDiagram.Theme>
                <syncfusion:OfficeTheme/>
            </syncfusion:SfDiagram.Theme>
            <syncfusion:SfDiagram.Nodes>
                <syncfusion:NodeCollection />
            </syncfusion:SfDiagram.Nodes>
            <syncfusion:SfDiagram.Connectors>
                <syncfusion:ConnectorCollection/>
            </syncfusion:SfDiagram.Connectors>
        </syncfusion:SfDiagram>
    </Grid>
</Page>


And this code behind:

public ViewModel_Variants()
        {
            Nodes = new ObservableCollection<ConfigurationItem>();

            DataSourceForDiagram = new DataSourceSettings()
            {
                DataSource = Nodes,
                Id = "GUID",
                ParentId = "ParentGUID",
                Root = "0"
            };

            LayoutManagerForDiagram = new LayoutManager()
            {
                Layout = new DirectedTreeLayout()
                {
                    Type = LayoutType.Hierarchical,
                    Orientation = TreeOrientation.LeftToRight,
                    HorizontalSpacing = 30,
                    VerticalSpacing = 50,
                },
                RefreshFrequency = RefreshFrequency.Add
            };

            Allgemein._viewModel_Variants = this;

          PopulateItemsAsync();
        }

        private async void PopulateItemsAsync()
        {
            Nodes.Clear();
            
            await Task.Run(() => PopulateItems());
        }

        private void PopulateItems()
        {
            System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
            {
                Nodes.Add(new ConfigurationItem()
                {
                    GUID = "0",
                    ParentGUID = "",
                    Characteristics = "TEst",
                    Height = 80,
                    Width = 120
                });
            }));


            int ID = 1;
            for (int i = 1; i < 100; i++)
            {
                for (int j = 0; j <= 3; j++)
                {
                    System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
                    {
                        Nodes.Add(new ConfigurationItem()
                        {
                            GUID = ID.ToString(),
                            ParentGUID = (i - 1).ToString(),
                            Characteristics = ID.ToString(),
                            Height = 80,
                            Width = 120
                        });
                    }));

                    ID++;
                }
            }
        }

Thank you so much for all your help! I really appreciate it :-)


Have a great day!


Victor


RA Ranjitha Amirthalingam Syncfusion Team July 16, 2020 10:48 AM UTC

Hi Victor,  
 
Most welcome. Please let us know if you need further assistance.  
 
 
Regards, 
Ranjitha A. 



VD Victor Dienstbier July 17, 2020 08:39 PM UTC

Hi Ranjitha,

you indeed can help me with another crazy thing that is happening with the Layout: When I publicate the items it shows a nice tree with all items, but when in Scoll down or up and sometimes when I readd all items the view is not well shaped. I have attached a video :-)

I'm populating the items like this:

private async void PopulateItemsAsync()
        {
            Nodes = new ObservableCollection<ConfigurationItem>();

            UpdateLayout();

            List<string> Parents = new List<string>();
            Parents.Add("00000000-0000-0000-0000-000000000000");

            Nodes.Add(new ConfigurationItem()
            {
                GUID = "00000000-0000-0000-0000-000000000000",
                ParentGUID = "",
                Feature = "Project",
                Characteristic = Project.ProjectName,
                Height = Properties.Settings.Default.dStandardSize_Rectangle_Start_Height,
                Width = Properties.Settings.Default.dStandardSize_Rectangle_Start_Width
            });

            List<string> DoneFeatures = new List<string>();
            await Task.Run(() => PopulateItems(Parents, DoneFeatures));
        }
        private void PopulateItems(List<string> Parents, List<string> DoneFeatures)
        {
            List<string> Parents_New = new List<string>();
            foreach (ConfigurationItem feature in Project.Features)
            {
                if (!DoneFeatures.Contains(feature.GUID))
                {
                    foreach (string parent in Parents)
                    {
                        foreach (ConfigurationItem characteristic in feature.Characteristics)
                        {
                            string guid_new = Guid.NewGuid().ToString();
                            System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
                            {
                                Nodes.Add(new ConfigurationItem()
                                {
                                    GUID = guid_new,
                                    ParentGUID = parent,
                                    Feature = feature.Feature,
                                    Characteristic = characteristic.Characteristic,
                                    Height = Properties.Settings.Default.dStandardSize_Rectangle_Height,
                                    Width = Properties.Settings.Default.dStandardSize_Rectangle_Width
                                });
                            }));
                            Parents_New.Add(guid_new);
                        }
                    }

                    DoneFeatures.Add(feature.GUID);
                    PopulateItems(Parents_New, DoneFeatures);
                }                
            }
        }

public void UpdateLayout()
        {
            DataSourceForDiagram = new DataSourceSettings()
            {
                DataSource = Nodes,
                Id = "GUID",
                ParentId = "ParentGUID",
                Root = "00000000-0000-0000-0000-000000000000"
            };

            LayoutManagerForDiagram = new LayoutManager()
            {
                Layout = new DirectedTreeLayout()
                {
                    Type = LayoutType.Hierarchical,
                    Orientation = TreeOrientation.LeftToRight,
                    HorizontalSpacing = 15
                    VerticalSpacing = 50,
                    SpaceBetweenSubTrees = 150,
                    AvoidSegmentOverlapping = false
                },
                RefreshFrequency = RefreshFrequency.Add
            };
        }


If you like I can invite you to the GitHub Rep for this Project :-)


Thank you very much!

Attachment: 1707_2020_223051_fd0e699f.zip


RA Ranjitha Amirthalingam Syncfusion Team July 20, 2020 12:36 PM UTC

Hi Victor, 
 
We have created a new incident under your Direct trac account to follow up with this query. We suggest you to follow up with the incident for further updates. Please log in using the below link.  
 
 
Regards, 
Ranjitha A. 


Loader.
Up arrow icon