Create nested objects dynamically - c#

I am using third party library where if I wanted to created nested structure for directories I have to create like this
new ClassA("folder1", new ClassA("folder2", new ClassA("folder3")));
this will create folder structure like this folder1->folder2->folder3.
To make it simple for my users I am creating methods where users pass the path as parameter and my method process the path and should create the above object structure which internally creates folder structure.
right now I am able to parse the path like a tree but could not able to create above object structure.
This is the sample console application code
class Program
{
static void Main(string[] args)
{
List<string> Paths = new List<string>();
Paths.Add("D1");
Paths.Add("E1");
Paths.Add(#"E1\E11");
Paths.Add(#"D1\D11");
Paths.Add(#"D1\D12");
Paths.Add(#"D1\D2");
Paths.Add(#"D1\D2\D21");
Node nodeObj = new Node();
foreach (var path in Paths)
{
nodeObj.AddPath(path);
}
//var nodes = nodeObj.Nodes;
Node current = nodeObj;
int level = 0;
ReadNodes(current, level);
}
private static void ReadNodes(Node current, int level)
{
foreach (string key in current.Nodes.Keys)
{
var tablevel = level;
string tab = string.Empty;
while (tablevel>0)
{
tab = tab + " ";
tablevel--;
}
Console.WriteLine(tab +":" + key);
// The child node.
Node child;
if (current.Nodes.TryGetValue(key, out child) && child.Nodes.Count>0)
{
ReadNodes(child, level+1);
}
else { }
}
}
}
public class Node
{
private readonly IDictionary<string, Node> _nodes =
new Dictionary<string, Node>();
public IDictionary<string, Node> Nodes
{
get { return _nodes; }
}
private string Path { get; set; }
public string Source { get; set; }
public void AddPath(string path)
{
char[] charSeparators = new char[] { '\\' };
// Parse into a sequence of parts.
string[] parts = path.Split(charSeparators,
StringSplitOptions.RemoveEmptyEntries);
// The current node. Start with this.
Node current = this;
// Iterate through the parts.
foreach (string part in parts)
{
// The child node.
Node child;
// Does the part exist in the current node? If
// not, then add.
if (!current._nodes.TryGetValue(part, out child))
{
// Add the child.
child = new Node
{
Path = part
};
// Add to the dictionary.
current._nodes[part] = child;
}
// Set the current to the child.
current = child;
}
}
}

class Test : yourClass
{
string name;
Test childTest;
public Test(string name)
{
this.name = name;
this.childTest = null;
}
public Test(string name, Test test)
{
this.name = name;
this.childTest = test;
}
}
Test a = new Test("a", new Test("b", new Test("c")));
Are you just searching for a structure like this? You need two constructors one that accepts only a string and one that accepts a string and another class of the same type

Related

Update TreeView file explorer code to use Microsoft.UI.Xaml in UWP

I'm trying to update my old source code to UWP referencing Microsoft.UI.Xaml.dll TreeView control to display/replicate files and folders stored in a string[].
string[] stores the complete paths to one or more files.
Example:
C:\Users\User\Documents\Test1.txt
C:\Users\User\Documents\Test2.txt
C:\Users\User\Documents\folder\Test1.txt
C:\Users\User\Documents\folder\Test2.txt
The code that I would like to update is the following:
private void PopulateTreeView(TreeView treeView, string[] paths, char pathSeparator)
{
TreeNode lastNode = null;
string subPathAgg;
long count = 0;
foreach (string path in paths)
{
subPathAgg = string.Empty;
foreach (string subPath in path.Split(pathSeparator))
{
Application.DoEvents();
subPathAgg += subPath + pathSeparator;
TreeNode[] nodes = treeView.Nodes.Find(subPathAgg, true);
if (nodes.Length == 0)
{
if (lastNode == null)
{
lastNode = treeView.Nodes.Add(subPathAgg, subPath);
}
else
{
lastNode = lastNode.Nodes.Add(subPathAgg, subPath);
}
count++;
}
else
{
lastNode = nodes[0];
}
}
lastNode = null; // This is the place code was changed
}
}
Does anyone know how to update this code using Microsoft.UI.Xaml.dll TreeView control?
I thought this should be easy, but I feel like I've missed something.
Your code should be WPF or WinForm. The TreeView in UWP has a lot of changes compared to them. If you want to handle this control from scratch, you can check this document.
Let us now look at the code. If you plan to create a TreeView via a path string, it will be more complicated in UWP.
Create a class to hold the display text and the real path
public class PathDisplay
{
public string DisplayText { get; set; }
public string Path { get; set; }
public PathDisplay(){ }
public PathDisplay(string d, string p)
{
DisplayText = d;
Path = p;
}
public override string ToString()
{
return DisplayText;
}
}
Create a recursive method for detecting the same node as subPathAgg (the Find method is no longer available in TreeView)
private IEnumerable<TreeViewNode> GetSameNodes(IList<TreeViewNode> nodes, string path)
{
foreach (var node in nodes)
{
var content = node.Content as PathDisplay;
if (content?.Path == path)
{
yield return node;
}
else
{
if (node.Children.Count > 0)
{
foreach (var item in GetSameNodes(node.Children,path))
{
yield return item;
}
}
}
}
}
Rewrite the PopulateTreeView method. In UWP, many class names and methods have changed.
private void PopulateTreeView(TreeView treeView, string[] paths, char pathSeparator)
{
TreeViewNode lastNode = null;
string subPathAgg;
long count = 0;
foreach (string path in paths)
{
subPathAgg = string.Empty;
foreach (string subPath in path.Split(pathSeparator))
{
subPathAgg += subPath + pathSeparator;
var displayModel = new PathDisplay(subPath, subPathAgg);
TreeViewNode[] nodes = GetSameNodes(treeView.RootNodes,subPathAgg).ToArray();
if (nodes.Length == 0)
{
if (lastNode == null)
{
lastNode = new TreeViewNode() { Content = displayModel };
treeView.RootNodes.Add(lastNode);
}
else
{
var node = new TreeViewNode() { Content = displayModel };
lastNode.Children.Add(node);
lastNode = node;
}
count++;
}
else
{
lastNode = nodes[0];
}
}
lastNode = null; // This is the place code was changed
}
}
So that we can use it
Xaml
<controls:TreeView x:Name="TestTreeView"
/>
Xaml.cs
public TreeViewPage()
{
this.InitializeComponent();
PopulateTreeView(TestTreeView, new string[]{
#"C:\Users\User\Documents\Test1.txt",
#"C:\Users\User\Documents\Test2.txt",
#"C:\Users\User\Documents\folder\Test1.txt"
}, '\\');
}
it looks like this:
The code can be rewritten this way, but I recommend the collection binding in the document, which is more conducive to later maintenance.
Best regards.

what is the best way to write a list of items in a text file in tree view form

i have a list of objects , and each object have a list of dependant objects , i want to write the list in a text file in a tree view form .
i tried doing foreach on the list but i can't all dependencies and the correct levels of objects
//the list of all objects
List<Object> objects;
//object Class
class Object {
string name;
List<Object> depandantObj;
}
the expected result must be writen in text file under the form :
object1:
object2
object3:
object5
object1
object6:
object2
etc...
Recursive method to append a new line for each object with indent:
public string GetText(Object obj, int indentLevel)
{
string text = "";
string indentation = new string(' ', indentLevel * 8);
text += indentation + obj.name;
if (obj.depandantObj != null && obj.depandantObj.Count > 0)
{
indentLevel++;
foreach (Object o in obj.depandantObj)
{
text += Environment.NewLine + GetText(o, indentLevel);
}
}
else
return text;
return text;
}
Call the method for each object in the list and write the text into the text file at the end:
make sure both of the fields (name and depandantObj) in Object class are public
List<Object> objects;
//add items to list
...
if(objects != null)
{
string text = "";
foreach (Object obj in objects)
{
text += GetText(obj, 0);
}
File.WriteAllText(Server.MapPath("~/sample.txt"), text);
}
First create your Objects with nested list forms. For example:
public class MyObject{
// some properties here
}
public class MySecondObject{
List<MyObject> {get; set;}
}
public class MythirdObject{
List<MySecondObject> {get; set;}
}
And when you want to save the data to a file just seriliaze them to json , it will already create a readable json file for you.
// I assume you can create your data or fetch them
var data = List<MyThirdObject> ();
string json = JsonConvert.SerializeObject(_data);
//write string to file
System.IO.File.WriteAllText(#"D:\path.txt", json);
If you don't want json than you can create a recusrive method that add each object to under last one.
Have a look this question for how you can this at this way.
Consider using json, download newtonsoft dll from Nuget.
a code example:
public class MyObject
{
public string Name { get; set; }
}
public class MySecondObject
{
public List<MyObject> DepObj { get; set; } = new List<MyObject>();
}
usage example:
MyObject obj = new MyObject
{
Name = "example"
};
MySecondObject mySecond = new MySecondObject();
mySecond.DepObj.Add(obj);
var data = new List<MySecondObject>
{
mySecond
};
string json = JsonConvert.SerializeObject(data, Formatting.Indented);
System.IO.File.WriteAllText(#"D:\file.txt", json);
File content:
[
{
"DepObj": [
{
"Name": "example"
}
]
}
]
First, let's elaborate initial Object class; I've renamed it (in order not to conflict with System.Object), make fields being public, add a constructor:
class MyObject {
public string name = "";
public List<MyObject> depandantObj = new List<MyObject>();
public MyObject(string value, params MyObject[] dependent) {
name = value;
if (dependent != null)
foreach (var item in dependent)
depandantObj.Add(item);
}
}
Then we can implement an iterator, IEnumerable<string>:
private static IEnumerable<string> MyObjectToTree(IEnumerable<MyObject> roots, int shift = 6) {
if (null == roots)
yield break;
foreach (var root in roots) {
// We don't want infinte loop if objects create a cycle
HashSet<MyObject> completed = new HashSet<MyObject>();
Stack<Tuple<int, MyObject>> agenda = new Stack<Tuple<int, MyObject>>();
agenda.Push(Tuple.Create(0, root));
while (agenda.Any()) {
Tuple<int, MyObject> item = agenda.Pop();
if (!completed.Add(item.Item2))
continue;
List<MyObject> children = item.Item2?.depandantObj ?? new List<MyObject>();
children.Reverse();
yield return $"{new string(' ', shift * item.Item1)}{item.Item2?.name}{(children.Any() ? ":" : "")}";
foreach (var child in children)
agenda.Push(Tuple.Create(item.Item1 + 1, child));
}
}
}
Demo:
// I've added the MyObject constructor for this readable creation
List<MyObject> objects = new List<MyObject>() {
new MyObject("object1",
new MyObject("object2"),
new MyObject("object3",
new MyObject("object4"),
new MyObject("object5"))),
new MyObject("object6",
new MyObject("object2")),
};
foreach (string line in MyObjectToTree(objects, 6))
Console.WriteLine(line);
// If you want to write into file:
// File.WriteAllLines(#"c:\MyFile.txt", MyObjectToTree(objects, 6));
Outcome:
object1:
object2
object3:
object4
object5
object6:
object2

Turn array into a POCO object

I'm having trouble converting the following string array into a POCO object.
Given the following:
string files = [
"./Folder/file.ext",
"./Folder/file2.ext",
"./Folder/file3.ext",
"./Folder/nestedfolder/file.ext",
"./Folder2/file1.ext",
"./Folder2/file2.ext",
"./file1.ext",
"./file2.ext",
"./file3.ext",
];
I would like to convert it to something like:
public class HierarchicalSource{
public List<HierarchicalSource> Children = new List <HierarchicalSource> ();
public bool folder { get; set; }
public string FullPath;
public HierarchicalSourceSource(string path) {
this.FullPath = path;
}
}
Where HierarchicalSource is the root, and has a list of children
UPDATE:
I ended up changing the list to a dictionary. There must be a more efficient way to do this, but I did as follows:
string fileList = files.Select(x => x.Remove(0, 2)).ToArray();
var root = new HierarchicalSource("root");
foreach(var f in fileList){
var current = root;
string[] splitFile = f.Split('/');
foreach(var s in splitFile){
if(!current.Children.ContainsKey(s)){
current.Children.Add(s, new List<HierarchicalSource>{ new HierarchicalSource(s) });
}
current = current.Children[s].Last();
}
}
POCO:
public class HierarchicalSource{
public string name;
public Dictionary<string, List<HierarchicalSource>> Children = new Dictionary<string, List<HierarchicalSource>>();
public HierarchicalSource(string name){
this.name = name;
}
}
If I understand you correctly, this requires looping through the array, but it'll allow you to parse each item in the array so you can generate the HierarchicalNode object's values.
var node = new HierarchicalSource();
foreach(var str in files)
{
var pathParts = str.Split('/').ToList();
node.Children.Add(new HierarchicalNode()
{
FullPath = str,
Folder = pathParts[1] // you may need to do some debugging to see what the results for pathParts are instead of just [#]
});
}
Since the FullPath member in HierarchicalNode is public you can set that value without having to go through any constructor.
// using the above code for reference
node.FullPath = whateverThePathYouNeedIs;
Also update that property in the class to use getters and setters
public string FullPath { get; set; }

Create a object hierarchy from a list of folder locations

I have a list of locations as strings;
locA/locB
locA/locB/locH
locC/locD/locE
locC/locD/locE/locK
locF/locG
I've been trying to create an object that uses the same structure as the list of locations passed to it;
e.g. Something like..
var myHObject=CreateHeirarchicalObjectFromList(myStringListOfLocations);
I'm having problems looping through the list without almost doing it manually with loads of loops. Is there an easier way, maybe recursion?
I want to end up with an object like this;
.locA
.locB
.locH
.locC
.locD
.locE
.locK
.locF
.locG
That I can use to create a visual hierarchy.
Prob not the best but knocked up in LinqPad, will reformat in a sec..
void Main()
{
var strings = new string[]{"locA/locB","locA/locB/locH",
"locC/locD/locE","locC/locD/locE/locK","locF/locG"};
var folders = Folder.Parse(strings);
folders.Dump();
}
public class Folder
{
public string Name { get; set; }
public List<Folder> Folders { get; internal set; }
public Folder()
{
Folders = new List<Folder>();
}
//Presume that each string will be folder1/folder2/folder3
public static IEnumerable<Folder> Parse(IEnumerable<string> locations)
{
var folders = new List<Folder>();
foreach (string location in locations)
{
string[] parts = location.Split('/');
Folder current = null;
foreach (string part in parts)
{
var useFolders = current != null ?
current.Folders : folders;
current = useFolders.SingleOrDefault(f => f.Name == part) ?? new Folder() { Name = part };
if (!useFolders.Contains(current)) { useFolders.Add(current); }
}
}
return folders;
}
}

Filling up a TreeView control

I have a N-Ary non sorted in any way tree and each node can have 0-N children. Given the data structure below, how can I fill the tree view assuming you have an array of TermNodes and that array is the first level of the TreeView? I have not been able to come up with a recursive way to do this.
class TermNode
{
public string Name;
public string Definition;
public List<TermNode> Children
}
Here is a bit of code to get you started with the recursion. It's not tested (I can't right now), but you should get the idea:
public static void BuildTreeView(TreeNodeCollection Parent, List<TermNode> TermNodeList)
{
foreach (TermNode n in TermNodeList)
{
TreeNode CurrentNode = Parent.Add(n.Name);
// no need to recurse on empty list
if (n.List.Count > 0) BuildTreeView(CurrentNode.Nodes, n.List);
}
}
// initial call
List<TermNode> AllTermNodes = /* all your nodes at root level */;
BuildTreeView(treeView1.Nodes, AllTermNodes);
Just took out Generics for a spin.. Worked nicely. Worth a look at...
public interface INode<T>
{
List<T> Children { get; }
}
class TermNode:INode<TermNode>
{
public string Name;
public string Definition;
public List<TermNode> Children { get; set; }
public TermNode()
{
this.Children = new List<TermNode>();
}
}
public class TreeBuilder<T> where T : INode<T>
{
public Func<T, TreeNode> obCreateNodeFunc;
public void AddNode(TreeView obTreeView, T obNodeToAdd, TreeNode obParentNodeIfAny)
{
TreeNodeCollection obNodes;
if (obParentNodeIfAny == null)
{
obNodes = obTreeView.Nodes;
}
else
{
obNodes = obParentNodeIfAny.Nodes;
}
int iNewNodeIndex = obNodes.Add(obCreateNodeFunc(obNodeToAdd));
TreeNode obNewNode = obNodes[iNewNodeIndex];
foreach (T child in obNodeToAdd.Children)
{
AddNode(obTreeView, child, obNewNode);
}
}
}
// calling code - Some class
static TreeNode GetTreeNodeFor(TermNode t)
{
return new TreeNode(t.Name); // or any logic that returns corr TreeNode for T
}
void Main()...
{
TermNode[] arrNodesList;
// populate list with nodes
TreeBuilder<TermNode> tb = new TreeBuilder<TermNode>();
tb.obCreateNodeFunc = GetTreeNodeFor;
foreach (TermNode obNode in arrNodesList)
{
tb.AddNode(treeView, obNode, null);
}
}
Thanks All I was getting confused because I did not realize that for a given TreeNode tn, tn.Nodes.Add would return the added TreeNode
Once you know that the solution is straight forward like so
private void /*TreeNode*/ RecursiveAdd(OntologyNode on, TreeNode tn)
{
if (on.Children.Count == 0)
{
return;
}
foreach (OntologyNode child in on.Children)
{
TreeNode tCur = tn.Nodes.Add(child.Name);
tCur.Tag = child;//optional for some selected node events
RecursiveAdd(child, tCur);
}
}
and to start of the recursive call
foreach( OntologyNode on in Nodes )
{
if (on.IsTopLevelNode == true)// internal not pertinent to this code snippet
{
TreeNode tn = tvOntoBrowser.Nodes.Add(on.Name);
tn.Tag = on;
if (on.Children.Count > 0)
{
RecursiveAdd(on, tn);
}
}
}

Categories

Resources