C# WPF: Create TreeView from text file - wpf

I create my TreeViewItems with the class Node . In the example nodes are specified in source code. But how do I do it if the nodes are to be imported from a text file with content like this:
text file content
Any ideas?
I have tried the following.
public MainWindowVM()
{
private ObservableCollection<Node> mRootNodes;
public IEnumerable<Node> RootNodes { get { return mRootNodes; } }
List<string[]> TreeNodes = new List<string[]>();
string[] lines = null;
try
{
lines = System.IO.File.ReadAllLines(MainWindow.TextFilePath , System.Text.Encoding.Default);
}
catch (IOException ex)
{
MessageBox.Show(ex.Message);
Environment.Exit(0);
}
if (lines == null || lines.Length == 0)
{
MessageBox.Show("Text file has no content!");
Environment.Exit(0);
}
foreach (var line in lines)
{
TreeNodes.Add(line.Split('|'));
}
Node newNode = null;
Node childNode = null;
Node root = new Node() { Name = TreeNodes[0][0] };
if (TreeNodes[0].Length > 1)
{
newNode = new Node() { Name = TreeNodes[0][1] };
root.Children.Add(newNode);
}
for (int s = 2; s < TreeNodes[0].Length; s++)
{
childNode = new Node() { Name = TreeNodes[0][s] };
newNode.Children.Add(childNode);
newNode = childNode;
}
}
but I get only the first two nodes. I do not know how to build the whole TreeView with a loop.
TreeView

input example
Root|A
Root|B|C
Root|B|D
Root|E
the problem with your code is that you only process TreeNodes[0] element. to process a collection of elements you need a loop
public MainWindowVM()
{
private ObservableCollection<Node> mRootNodes;
public IEnumerable<Node> RootNodes { get { return mRootNodes; } }
string[] lines = null;
try
{
lines = System.IO.File.ReadAllLines(MainWindow.TextFilePath , System.Text.Encoding.Default);
}
catch (IOException ex)
{
MessageBox.Show(ex.Message);
Environment.Exit(0);
}
if (lines == null || lines.Length == 0)
{
MessageBox.Show("Text file has no content!");
Environment.Exit(0);
}
Dictionary<string, Node> nodeCache = new Dictionary<string, Node>();
// processing each line
foreach (var line in lines)
{
Node parentNode = null;
string key = null;
// in each line there are one or more node names, separated by | char
foreach (string childNodeName in line.Split('|'))
{
Node childNode;
// names are not unique, we need a composite key (full node path)
key += "|" + childNodeName;
// each node has unique key
// if key doesn't exists in cache, we need to create new child node
if (false == nodeCache.TryGetValue(key, out childNode))
{
childNode = new Node { Name = childNodeName };
nodeCache.Add(key, childNode);
if (parentNode != null)
// each node (exept root) has a parent
// we need to add a child node to parent ChildRen collection
parentNode.Children.Add(childNode);
else
// root nodes are stored in a separate collection
mRootNodes.Add(childNode);
}
// saving current node for next iteration
parentNode = childNode;
}
}
}

Related

RSSService item with the same key multiple times

I have a RSSService with an item like the one show below
<item>
<title>Accessori per la cura della persona</title>
<link>http://www.myurl.it/accessori-per-la-cura-della-persona/</link>
<comments>http://www.myurl.it/accessori-per-la-cura-della-persona/#comments</comments>
<pubDate>Tue, 24 Oct 2017 09:29:44 +0000</pubDate>
<dc:creator><![CDATA[Farmacia Rizzo Davide]]></dc:creator>
<category><![CDATA[News]]></category>
<category><![CDATA[Offerte]]></category>
<category><![CDATA[Callifugo]]></category>
<category><![CDATA[Raspa piedi]]></category>
<category><![CDATA[Spazzola ceramica]]></category>
<category><![CDATA[Spazzola piatta]]></category>
<category><![CDATA[Spazzola tonda]]></category>
<category><![CDATA[Spazzole capelli]]></category>
<guid isPermaLink="false">http://www.myurl.it/?p=3982</guid>
<description>.....
To read all the content I use this:
List<Map> records;
...
records = rss.getResults();
...
for (Map m : records) {
Button b = new Button((String)m.get("title"));
if(((String)m.get("category")).equals(CATEGORY_OFFERTE)){
b.setIcon(iconStar);
} else {
b.setIcon(iconNews);
}
b.addActionListener((l)->{
Boolean can = Display.getInstance().canExecute((String)m.get("link"));
if(can != null && can) {
Display.getInstance().execute((String)m.get("link"));
} else {
ToastBar.Status status = ToastBar.getInstance().createStatus();
status.setMessage("Non riesco a connettermi");
status.setExpires(3000);
status.show();
}
});
recordsContainer.addComponent(b);
}
When I read the key "category" I always get the last entry (in this item "Spazzole capelli").
There is a way to read a key like an array? Something like that:
String[] mc = (String[]) m.get("category");
Thank's in advance for any help.
Davide.
This looks like a missing feature in RSSService that expects to find only one category entry and so it parses it into a hash map which effectively allows only one such entry.
I would suggest implementing your own RSS reading and go to the XML directly to get the full power of the protocol. You can use the existing class as a reference on how to do the RSS/ATOM parsing.
This is my idea
protected void textElement(String text) {
if(lastTag != null && current != null) {
// make "ATOM" seem like RSS
if("summary".equals(lastTag)) {
current.put("details", text);
} else {
if("content".equals(lastTag)) {
current.put("description", text);
} else {
if(current.get(lastTag) != null){
try {
List list = (List) current.get(lastTag);
list.add(text);
current.put(lastTag, list);
} catch (ClassCastException e){ // java.lang.String cannot be cast to java.util.List
List list = new ArrayList();
list.add((String)current.get(lastTag));
list.add(text);
current.put(lastTag, list);
}
} else {
current.put(lastTag, text);
}
}
}
}
}
Used in my Form like this:
for (Map m : records) {
Button b = new Button((String)m.get("title"));
try{
String category = (String)m.get("category");
if(category.equals(CATEGORY_OFFERTE)){
b.setIcon(iconStar);
} else {
b.setIcon(iconNews);
}
} catch (ClassCastException e){ // java.lang.String cannot be cast to java.util.List
b.setIcon(iconNews);
List list = (List) m.get("category");
for(int i=0; i < list.size(); i++){
if(((String)list.get(i)).equals(CATEGORY_OFFERTE)){
b.setIcon(iconStar);
}
}
}
b.addActionListener((l)->{
Boolean can = Display.getInstance().canExecute((String)m.get("link"));
if(can != null && can) {
Display.getInstance().execute((String)m.get("link"));
} else {
ToastBar.Status status = ToastBar.getInstance().createStatus();
status.setMessage("Non riesco a connettermi");
status.setExpires(3000);
status.show();
}
});
recordsContainer.addComponent(b);
}

How to identify and remove child folders in a list of folders?

Let's say I have a list of folders in an array:
c:\aaa\bbb
d:\aaa\bbb
c:\aaa\bbb\ccc
c:\aaa\bbb\ccc\ddd
My program will carry out some operations recursively for each objects in those folders. But as you can see, some folders in this array have parent-child relationship. So I should remove the nested folders in this array before my process. For the above example, c:\aaa\bbb\ccc\ddd and c:\aaa\bbb\ccc will be removed as they are nested in c:\aaa\bbb. What's the best way to do this?
You could first sort the array in ascending order and visit each of the folders in that order. Then keep track of a "parent" folder, which starts out with an invalid name:
sort folders
parent = '>'
result = []
for each folder in folders:
if folder does not start with parent followed by a slash:
# keep folder, and remember it as potential parent
append folder to result
parent = folder
Here is a JavaScript code example:
var folders = [
'c:\\aaa\\bbb',
'd:\\aaa\\bbb',
'c:\\aaa\\bbb\\ccc',
'c:\\aaa\\bbb\\ccc\\ddd'
];
folders.sort();
let parent = '>';
let result = [];
for (var i = 0; i < folders.length; i++) {
if (folders[i].indexOf(parent + '\\') != 0) {
result.push(folders[i]); // keep it
parent = folders[i]; // and remember this as potential parent
}
}
// Display result
console.log(result.join('\n'));
I don't know if this is the best way to solve your problem, but it is some light at the process to find it.
First of all, the code.
/**
* Created by Zack at 14/January/2017
*/
public class ChildPathRemover {
private final Node mRoot = new Node();
/**
*
* #param path
* #return True if the directory was added, False if is a child directory
*/
public boolean add(String path) {
Node currNode = mRoot;
String[] parts = path.split(Pattern.quote(File.separator));
for (int i = 0; i < parts.length; i++) {
// contains
Node nextNode = currNode.subNodes.get(parts[i]);
if (nextNode != null) {
// Already has a parent
if (nextNode.isLeaf) {
return false;
} // Process the nextNode
else {
currNode = nextNode;
}
} // Reached the end, so we a good to add new path
else {
for (int k = i; k < parts.length; k++) {
Node newNode = new Node();
currNode.subNodes.put(parts[k], newNode);
currNode = newNode;
}
currNode.isLeaf = true;
break;
}
}
// TODO: if the parent were not added first, you will need to rebuild the paths, based on the
// Nodes and then call the Remove Listener
return true;
}
private static class Node {
boolean isLeaf = false;
HashMap<String, Node> subNodes = new HashMap<>();
}
public interface RemoveListener {
void pathRemoved(String path);
}
/**
* Call this method to remove the child paths
*
* #param paths
*/
public static void removeChildPath(List<String> paths) {
// Sort by the length of the path
Collections.sort(paths, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
return Integer.compare(o1.length(), o2.length());
}
});
ChildPathRemover cpr = new ChildPathRemover();
Iterator<String> itr = paths.iterator();
while (itr.hasNext()) {
if (!cpr.add(itr.next())) {
itr.remove();
}
}
}
}
Example of how to use.
public static void main(String[] args) {
ArrayList<String> paths = new ArrayList<>();
paths.add("d:\\aaa\\bbb");
paths.add("c:\\aaa\\bbb\\ccc");
paths.add("c:\\aaa\\bbb\\ccc\\ddd");
paths.add("c:\\aaa\\bbb");
removeChildPath(paths);
for (String path : paths) {
System.out.println("path = " + path);
}
}
This example will produce
path = d:\aaa\bbb
path = c:\aaa\bbb
Explanation
The idea here is to simulate a tree.
So first we add all the parent folders; this is done by sorting the array by the length of the paths.
Then, every time we add a path with success, it will be marked as leaf. When we try to add another path and it is "bigger", the code will know that it is a child, so we delete it.
EDIT:
Sorry... I did not see that this question was not marked as a Java question...

Populate Winforms treeview with Sharepoint site

I'm very new to sharepoint and I have to a task with it.
I have my site adress but just about it.
I need to populate a treeview with all the hierarchy from SPSite and down for my sharepoint site using C#;
I guess I need to use recursive functions to do so...
can anyone help?
thank you.
Solved!
public void FillTreeSPWeb(SPWeb MySPWeb, TreeNode Node, int num)
{
if (num <= 0)
{
Node.Text = MySPWeb.Title;
GetSPDocumentLibrary(MySPWeb, Node);
}
num--;
foreach (SPWeb item in MySPWeb.Webs)
{
TreeNode Child = new TreeNode();
FillTreeSPWeb(item, Child,num);
Node.Nodes.Add(Child);
}
}
public void GetSPDocumentLibrary(SPWeb MySPWeb,TreeNode Node)
{
foreach (SPList item1 in MySPWeb.Lists)
{
if (item1.BaseType == SPBaseType.DocumentLibrary)
{
SPDocumentLibrary oDocumentLibrary = (SPDocumentLibrary)item1;
try
{
TreeNode Child = new TreeNode(oDocumentLibrary.Title);
GetFiles(oDocumentLibrary.RootFolder, Child);
foreach (SPListItem item in oDocumentLibrary.Folders)
{
TreeNode GrandChild = new TreeNode();
FillFilesAndFolders(item.Folder, GrandChild);
Child.Nodes.Add(GrandChild);
}
Node.Nodes.Add(Child);
}
catch { }
}
}
}
public void FillFilesAndFolders(SPFolder MySPFolder, TreeNode Node)
{
Node.Text = MySPFolder.Name;
GetFiles(MySPFolder, Node);
foreach (SPFolder item1 in MySPFolder.SubFolders)
{
FillFilesAndFolders(item1, Node);
}
}
public void GetFiles(SPFolder MySPFolder,TreeNode Node)
{
foreach (SPFile item in MySPFolder.Files)
{
Node.Nodes.Add(item.Name);
}
}

Use Kinect to create a digital catalog application

I am creating a WPF application to create digital catalog using kinect v1.8. I am trying to track only a single skeleton using following code:-
private void SensorSkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs skeletonFrameReadyEventArgs)
{
// Even though we un-register all our event handlers when the sensor
// changes, there may still be an event for the old sensor in the queue
// due to the way the KinectSensor delivers events. So check again here.
if (this.KinectSensor != sender)
{
return;
}
SkeletonFrame skeletonFrame = skeletonFrameReadyEventArgs.OpenSkeletonFrame();
if (skeletonFrame == null)
return;
Skeleton[] skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength];
skeletonFrame.CopySkeletonDataTo(skeletons);
Skeleton skeleton = (from skl in skeletons
where skl.TrackingState == SkeletonTrackingState.Tracked
select skl).FirstOrDefault();
if (skeleton == null)
return;
Skeleton[] s = new Skeleton[1];
s[0] = skeleton;
if (SkeletonTrackingState.Tracked == s[0].TrackingState)
{
//s1.SkeletonStream.ChooseSkeletons(s[0].TrackingId);
var accelerometerReading = this.KinectSensor.AccelerometerGetCurrentReading();
this.interactionStream.ProcessSkeleton(s, accelerometerReading, skeletonFrame.Timestamp);
}
}
I am getting an exception when I run the code and skeleton gets detected as follows:-
on the line "this.interactionStream.ProcessSkeleton(s, accelerometerReading, skeletonFrame.Timestamp);"
I need to detect only one skeleton and use it for further processing like to access the digital catalog applications.
Thanks in advance.
I have Faced Same Problem By using below code I have track only one skeleton Which is closer to the sensor.and directly assign the closest skeleton to the main skeleton stream.
private void SensorSkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs skeletonFrameReadyEventArgs)
{
Skeleton[] skeletons = new Skeleton[0];
using (SkeletonFrame skeletonFrame = skeletonFrameReadyEventArgs.OpenSkeletonFrame())
{
if (skeletonFrame != null && this.skeletons != null)
{
//Console.WriteLine(skeletonFrame.SkeletonArrayLength);
skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength];
// Set skeleton datas from skeletonFrame
skeletonFrame.CopySkeletonDataTo(this.skeletons);
TrackClosestSkeleton();
Skeleton[] singleSkeleton = new Skeleton[6];
Skeleton skl=(from mno in this.skeletons where mno.TrackingState==SkeletonTrackingState.Tracked && mno.TrackingId==globalClosestID select mno).FirstOrDefault();
if (skl == null)
return;
//Creating an empty skkeleton
Skeleton emptySkeleton = new Skeleton();
singleSkeleton[0] = skl; //Pass the Tracked skeleton with closestID
singleSkeleton[1] = emptySkeleton; //Passing Empty Skeleton
singleSkeleton[2] = emptySkeleton; //Passing Empty Skeleton
singleSkeleton[3] = emptySkeleton; //Passing Empty Skeleton
singleSkeleton[4] = emptySkeleton; //Passing Empty Skeleton
singleSkeleton[5] = emptySkeleton; //Passing Empty Skeleton
this.snew.SkeletonStream.ChooseSkeletons(globalClosestID);
var accelerometerReading = this.KinectSensor.AccelerometerGetCurrentReading();
this.interactionStream.ProcessSkeleton(singleSkeleton, accelerometerReading, skeletonFrame.Timestamp);
}
}
}
int globalClosestID = 0;
private void TrackClosestSkeleton()
{
if (this.snew != null && this.snew.SkeletonStream != null)
{
if (!this.snew.SkeletonStream.AppChoosesSkeletons)
{
this.snew.SkeletonStream.AppChoosesSkeletons = true; // Ensure AppChoosesSkeletons is set
}
float closestDistance = 10000f; // Start with a far enough distance
int closestID = 0;
foreach (Skeleton skeleton in this.skeletons.Where(s => s.TrackingState != SkeletonTrackingState.NotTracked))
{
if (skeleton.Position.Z < closestDistance)
{
closestID = skeleton.TrackingId;
closestDistance = skeleton.Position.Z;
}
}
if (closestID > 0)
{
this.snew.SkeletonStream.ChooseSkeletons(closestID); // Track this skeleton
globalClosestID = closestID;
}
}
}
check the above code it works for me.It may helpful to you.Here snew is the Kinectsensor.
You should try to track the closest skeleton using this method from MSDN
private void TrackClosestSkeleton()
{
if (this.kinect != null && this.kinect.SkeletonStream != null)
{
if (!this.kinect.SkeletonStream.AppChoosesSkeletons)
{
this.kinect.SkeletonStream.AppChoosesSkeletons = true; // Ensure AppChoosesSkeletons is set
}
float closestDistance = 10000f; // Start with a far enough distance
int closestID = 0;
foreach (Skeleton skeleton in this.skeletonData.Where(s => s.TrackingState != SkeletonTrackingState.NotTracked))
{
if (skeleton.Position.Z < closestDistance)
{
closestID = skeleton.TrackingId;
closestDistance = skeleton.Position.Z;
}
}
if (closestID > 0)
{
this.kinect.SkeletonStream.ChooseSkeletons(closestID); // Track this skeleton
}
}
}
And then in your SkeletonFrameReady
private void SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
{
Skeleton[] skeletons = new Skeleton[0];
using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame())
{
if (skeletonFrame != null && this.skeletonData != null)
{
skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength];
// Set skeleton datas from skeletonFrame
skeletonFrame.CopySkeletonDataTo(this.skeletonData);
TrackClosestSkeleton();
}
}
//Do some stuff here
}

How to replace words with span tag using jsoup?

Assume I have the following html:
<html>
<head>
</head>
<body>
<div id="wrapper" >
<div class="s2">I am going <a title="some title" href="">by flying</a>
<p>mr tt</p>
</div>
</div>
</body>
</html>
Any words in the text nodes that are equal to or greater than 4 characters for example the word 'going' is replaced with html content (not text) <span>going<span> in the original html without changing anything else.
If I try do something like element.html(replacement), the problem is if lets the current element is <div class="s2"> it will also wipe off <a title="some title"
In this case you must traverse your document as suggested by this answer. Here's a way of doing it using Jsoup APIs:
NodeTraversor and NodeVisitor allow you to traverse the DOM
Node.replaceWith(...) allows for replacing a node in the DOM
Here's the code:
public class JsoupReplacer {
public static void main(String[] args) {
so6527876();
}
public static void so6527876() {
String html =
"<html>" +
"<head>" +
"</head>" +
"<body>" +
" <div id=\"wrapper\" >" +
" <div class=\"s2\">I am going <a title=\"some title\" href=\"\">by flying</a>" +
" <p>mr tt</p>" +
" </div> " +
" </div>" +
"</body> " +
"</html>";
Document doc = Jsoup.parse(html);
final List<TextNode> nodesToChange = new ArrayList<TextNode>();
NodeTraversor nd = new NodeTraversor(new NodeVisitor() {
#Override
public void tail(Node node, int depth) {
if (node instanceof TextNode) {
TextNode textNode = (TextNode) node;
String text = textNode.getWholeText();
String[] words = text.trim().split(" ");
for (String word : words) {
if (word.length() > 4) {
nodesToChange.add(textNode);
break;
}
}
}
}
#Override
public void head(Node node, int depth) {
}
});
nd.traverse(doc.body());
for (TextNode textNode : nodesToChange) {
Node newNode = buildElementForText(textNode);
textNode.replaceWith(newNode);
}
System.out.println("result: ");
System.out.println();
System.out.println(doc);
}
private static Node buildElementForText(TextNode textNode) {
String text = textNode.getWholeText();
String[] words = text.trim().split(" ");
Set<String> longWords = new HashSet<String>();
for (String word : words) {
if (word.length() > 4) {
longWords.add(word);
}
}
String newText = text;
for (String longWord : longWords) {
newText = newText.replaceAll(longWord,
"<span>" + longWord + "</span>");
}
return new DataNode(newText, textNode.baseUri());
}
}
I think you need to traverse the tree. The result of text() on an Element will be all of the Element's text including text within child elements. Hopefully something like the following code will be helpful to you:
import java.io.File;
import java.io.IOException;
import java.util.StringTokenizer;
import org.apache.commons.io.FileUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.nodes.TextNode;
public class ScreenScrape {
public static void main(String[] args) throws IOException {
String content = FileUtils.readFileToString(new File("test.html"));
Document doc = Jsoup.parse(content);
Element body = doc.body();
//System.out.println(body.toString());
StringBuilder sb = new StringBuilder();
traverse(body, sb);
System.out.println(sb.toString());
}
private static void traverse(Node n, StringBuilder sb) {
if (n instanceof Element) {
sb.append('<');
sb.append(n.nodeName());
if (n.attributes().size() > 0) {
sb.append(n.attributes().toString());
}
sb.append('>');
}
if (n instanceof TextNode) {
TextNode tn = (TextNode) n;
if (!tn.isBlank()) {
sb.append(spanifyText(tn.text()));
}
}
for (Node c : n.childNodes()) {
traverse(c, sb);
}
if (n instanceof Element) {
sb.append("</");
sb.append(n.nodeName());
sb.append('>');
}
}
private static String spanifyText(String text){
StringBuilder sb = new StringBuilder();
StringTokenizer st = new StringTokenizer(text);
String token;
while (st.hasMoreTokens()) {
token = st.nextToken();
if(token.length() > 3){
sb.append("<span>");
sb.append(token);
sb.append("</span>");
} else {
sb.append(token);
}
sb.append(' ');
}
return sb.substring(0, sb.length() - 1).toString();
}
}
UPDATE
Using Jonathan's new Jsoup List element.textNode() method and combining it with MarcoS's suggested NodeTraversor/NodeVisitor technique I came up with (although I am modifying the tree whilst traversing it - probably a bad idea):
Document doc = Jsoup.parse(content);
Element body = doc.body();
NodeTraversor nd = new NodeTraversor(new NodeVisitor() {
#Override
public void tail(Node node, int depth) {
if (node instanceof Element) {
boolean foundLongWord;
Element elem = (Element) node;
Element span;
String token;
StringTokenizer st;
ArrayList<Node> changedNodes;
Node currentNode;
for (TextNode tn : elem.textNodes()) {
foundLongWord = Boolean.FALSE;
changedNodes = new ArrayList<Node>();
st = new StringTokenizer(tn.text());
while (st.hasMoreTokens()) {
token = st.nextToken();
if (token.length() > 3) {
foundLongWord = Boolean.TRUE;
span = new Element(Tag.valueOf("span"), elem.baseUri());
span.appendText(token);
changedNodes.add(span);
} else {
changedNodes.add(new TextNode(token + " ", elem.baseUri()));
}
}
if (foundLongWord) {
currentNode = changedNodes.remove(0);
tn.replaceWith(currentNode);
for (Node n : changedNodes) {
currentNode.after(n);
currentNode = n;
}
}
}
}
}
#Override
public void head(Node node, int depth) {
}
});
nd.traverse(body);
System.out.println(body.toString());
I am replacing word hello with hello(span tag)
Document doc = Jsoup.parse(content);
Element test = doc.body();
Elements elemenets = test.getAllElements();
for(int i =0 ;i <elemenets .size();i++){
String elementText = elemenets .get(i).text();
if(elementText.contains("hello"))
elemenets .get(i).html(l.get(i).text().replaceAll("hello","<span style=\"color:blue\">hello</span>"));
}

Resources