Multi Level Tree in Umbraco Custom Section - c#

I'm currently trying to create a custom tree and I'm running into trouble when trying to render a nodes children. After browsing various articles/posts I'm at this point:
public override void Render(ref XmlTree tree)
{
List<Node> articles = NodeUtil.GetAllNodesOfDocumentType(-1, "Promoter");
Node article = articles.Where(p => p.CreatorID == UmbracoEnsuredPage.CurrentUser.Id).FirstOrDefault();
if(promo != null)
{
var dNode = XmlTreeNode.Create(this);
dNode.NodeID = article.Id.ToString();
dNode.Text = article.Name;
dNode.Icon = "doc.gif";
dNode.Action = "javascript:openArticle(" + article.Id + ")";
dNode.Source = article.Children.Count > 0 ? this.GetTreeServiceUrl("" + article.Id) : "";
tree.Add(dNode);
}
}
The code above gets the article belonging to the current user (for the sake of testing, each user only has one article at the moment). I then attempt to print out the children of this article but instead of getting the desired output, I get the follwowing:
Article Name
- Article Name
- Article Name
- Article Name
Each time I expand a node, it just seems to render the same node, and goes on and on.
I've seen other ways of using the treeservice, like:
TreeService treeService = new TreeService(...);
node.Source = treeService.GetServiceUrl();
But I get an error saying there is no GetServiceUrl method that takes 0 arguments. I assume the method above was for earlier versions?

It took me a while to work this out. Here is the solution, hope it would help someone.
const string PARENT_ID = "10"; // The ID of the node that has child nodes
public override void Render(ref XmlTree tree)
{
if (this.NodeKey == PARENT_ID) // Rendering the child nodes of the parent folder
{
// Render a child node
XmlTreeNode node = XmlTreeNode.Create(this);
node.NodeID = "11";
node.Text = "child";
node.Icon = "doc.gif";
node.Action = ...
tree.Add(node);
}
else // Default (Rendering the root)
{
// Render the parent folder
XmlTreeNode node = XmlTreeNode.Create(this);
node.NodeID = PARENT_ID;
node.Source = this.GetTreeServiceUrl(node.NodeID);
node.Text = "parent";
node.Icon = "folder.gif";
tree.Add(node);
}
}

The output suggests that the node tree you're building is nesting each child node - this is because the nodeId is being reset to -1 with each pass.
This post on our.umbraco.org describes the same problem, and suggests that you use NodeKey instead of ID to move between nodes.
**
Not necessarily helpful but I would use the uQuery language extensions that comes with the ucomponents package (and who installs Umbraco without ucomponents?), to simplify the method calls:
For example:
List<Node> articles = uQuery.getNodesByType("Promoter");
foreach(Node article in articles)
{
List<Node> children = article.GetDescendantNodes();
... build tree
}

Related

Solidworks C# can't extract item from feature that has TypeName equals "Reference"

i have opened solidworks assembly (swDocumentTypes_e.swDocASSEMBLY) using C# and i have iterated through all the features in order to get all the Sketchs called 'ISO/XXX' under each part of the assembly, here is the code
public void openFile(string skeletonFilePath)
{
object[] Features = null;
int i = 0;
string FeatType = null;[1]
string FeatTypeName = null;
if (string.IsNullOrWhiteSpace(skeletonFilePath)) { return; }
ModelDoc2 model = _sldWorks.OpenDoc("C:PATH/fileName.SLDASM", (int)swDocumentTypes_e.swDocASSEMBLY);
Feature swFeat = default(Feature);
SelectionMgr swSelMgr = default(SelectionMgr);
swSelMgr = (SelectionMgr)model.SelectionManager;
swFeat = (Feature)model.FirstFeature();
while ((swFeat != null))
{
FeatType = swFeat.Name;
FeatTypeName = swFeat.GetTypeName2();
if ((FeatTypeName == "Reference")
{
Debug.Print(" Name of feature: " + swFeat.Name);
Debug.Print(" Type of feature: " + swFeat.GetTypeName2());
}
swFeat = (Feature)swFeat.GetNextFeature();
}
}
the problem:
each time i try to extract the items under the feature (of one part) i got an exception, i have to tried these ways:
swFeat.GetDefinition() // i've got null exception
swFeat.GetSpecificFeature2() // i've got dynamic value which i don't know the class i need to cast with
var childs = (Object[])swFeatSupport.GetChildren(); // i've got only to constraints under the part
example of project
Your code is only iterating over top level features. You can use IFeature::GetFirstSubFeature() and IFeature::GetNextSubFeature to get sub feature. Make this function recursive so it will iterate over all features, regardless of how many levels deep they are. Another layer you need to consider is the components - you need to iterate over components in an assembly first if you need feature data in the context of individual parts.
Here's an example from the Solidworks API documentation. Its poorly written (IMO) but it will guide you in the right direction.

Add Text Information to Different XML Element each time information is submitted c#

I have an XML file that collects information with Button_Click, so it starts off empty.
XML Sample
<marina>
<dockone>
</dockone>
<docktwo>
</docktwo>
</marina>
When I submit information from a textbox, a new XmlNode is created called slipone, and another XmlNode called reg is nested within that.
XML Sample 2
<marina>
<dockone>
<slipone>
<reg>12345</reg>
<slipone>
</dockone>
<docktwo>
</docktwo>
</marina>
I have attempted to create an if/else statement that will add a new XmlNode called sliptwo, with reg still nested within it, if slipone already has text, like so:
<marina>
<dockone>
<slipone>
<reg>12345</reg>
<slipone>
<sliptwo>
<reg>67890</reg>
<sliptwo>
</dockone>
<docktwo>
</docktwo>
</marina>
However the closest I have gotten is another XMlnode is still created, however it labels itself as slipone, and I am not sure what I am doing wrong:
<marina>
<dockone>
<slipone>
<reg>12345</reg>
<slipone>
<slipone>
<reg>67890</reg>
<slipone>
</dockone>
<docktwo>
</docktwo>
</marina>
This is an example of what I have been playing around with. Ignore the operators as I have resorted to trial and error but still have gotten nowhere. Please help!
C# Example
XmlDocument XmlDocObj1 = new XmlDocument();
XmlDocObj1.Load(Server.MapPath("~/App_Data/SlipData.xml"));
XmlNode rootnode1 = XmlDocObj1.SelectSingleNode("marina/dockone");
XmlNode dockone = rootnode1.AppendChild(XmlDocObj1.CreateNode(XmlNodeType.Element, "slipone", ""));
XmlNode docktwo = rootnode1.AppendChild(XmlDocObj1.CreateNode(XmlNodeType.Element, "sliptwo", ""));
XmlNode dockthree = rootnode1.AppendChild(XmlDocObj1.CreateNode(XmlNodeType.Element, "slipthree", ""));
if (regfinal.Text != dockone.InnerText)
{
dockone.AppendChild(XmlDocObj1.CreateNode(XmlNodeType.Element, "Reg", "")).InnerText = regfinal.Text;
XmlDocObj1.Save(Server.MapPath("/App_Data/SlipData.xml"));
}
else if (regfinal.Text == dockone.InnerText)
{
docktwo.AppendChild(XmlDocObj1.CreateNode(XmlNodeType.Element, "Reg", "")).InnerText = regfinal.Text;
XmlDocObj1.Save(Server.MapPath("/App_Data/SlipData.xml"));
}
Your logic isn't going to do what I think you are saying since the only time (regfinal.Text != dockone.InnerText) will evaluate to false is when you enter nothing in your text control.
I believe you might mean to say if dockone exists then create another node called docktwo. This will require you to change your logic.
Some very simple code to get you a bit farther down the path. Not intended to be perfect or solve all problems...
private void button1_Click(object sender, EventArgs e)
{
XmlDocument XmlDocObj1 = new XmlDocument();
XmlDocObj1.Load(AppDomain.CurrentDomain.BaseDirectory.ToString()+"test.xml");
XmlNode rootnode1 = XmlDocObj1.SelectSingleNode("marina/dockone");
XmlNode dockone = rootnode1.AppendChild(XmlDocObj1.CreateNode(XmlNodeType.Element, "slipone", ""));
XmlNode docktwo = rootnode1.AppendChild(XmlDocObj1.CreateNode(XmlNodeType.Element, "sliptwo", ""));
XmlNode dockthree = rootnode1.AppendChild(XmlDocObj1.CreateNode(XmlNodeType.Element, "slipthree", ""));
//jsh: old logic
//if (textBox1.Text != dockone.InnerText)
//new logic to test whether we have already created the dockone node which should only occur once
//you already have the logic for selecting the dockone node above...now just test if you already have it.
//NOTE: you may actually want a switch statement given that you avhe dockone, docktwo, and dockthree or at least another
// if statement to see if docktwo has been created and thus creaste dockthree.
if (rootnode1 == null )
{
dockone.AppendChild(XmlDocObj1.CreateNode(XmlNodeType.Element, "Reg", "")).InnerText = textBox1.Text;
XmlDocObj1.Save(AppDomain.CurrentDomain.BaseDirectory.ToString() + "test.xml");
}
//else if (textBox1.Text == dockone.InnerText) jsh: old logic
else
{
docktwo.AppendChild(XmlDocObj1.CreateNode(XmlNodeType.Element, "Reg", "")).InnerText = textBox1.Text;
XmlDocObj1.Save(AppDomain.CurrentDomain.BaseDirectory.ToString() + "test.xml");
}
}

How to parse XML in a Windows Phone 7 application

Could someone tell me how to parse a XML-String that i receive from a wcf-rest service?
my webserive XML-String looks like
<WS>
<Info>
<Name>Beta</Name>
<Description>Prototyps</Description>
</Info>
<Pages>
<Page>
<Name>Custom</Name>
<Description>toDo</Description>
</Page>
...many other pages...
</Pages>
</WS>
an my phone sourcecode:
public void DownloadCompleted(Object sender, DownloadStringCompletedEventArgs e)
{
if (!e.Cancelled && e.Error == null)
{
var answer = XElement.Parse(e.Result).Descendants("WS"); // null
...
}
}
if i try to parse it through XDocument.Load(e.Result) then i get the exception: File not Found.
i just want the "unique" information of the Info-Node and a list of all Page-Nodes with their values
Update
Even if i try to load the Root-Element via var item = xdoc.Root.Descendants(); item will be assigned to the whole xml-file.
Update 2 it seems the problem occurs with the namespaces in the root-element. with namespaces xdocument will parse the webservice output not correctly. if i delete the namespaces it works fine. could someone explain me this issue? and is there a handy solution for deleting all namespaces?
update 3 A Handy way for removing namespaces1
With really simple XML if you know the format wont change, you might be interested in using XPath:
var xdoc = XDocument.Parse(e.Result);
var name = xdoc.XPathSelectElement("/WS/Info/Name");
but for the multiple pages, maybe some linq to xml
var xdoc = XDocument.Parse(xml);
var pages = xdoc.Descendants("Pages").Single();
var PagesList = pages.Elements().Select(x => new Page((string)x.Element("Name"), (string)x.Element("Description"))).ToList();
Where Page is a simple class:
public class Page
{
public string Name { get; set; }
public string Descrip { get; set; }
public Page(string name, string descrip)
{
Name = name;
Descrip = descrip;
}
}
Let me know if you need more explanation.
Also to select the Info without XPath:
var info = xdoc.Descendants("Info").Single();
var InfoName = info.Element("Name").Value;
var InfoDescrip = info.Element("Description").Value;
Viktor - XDocument.Load(string) attempts to load an XDocument by the supplied filename, not a string representation of an XML element.
You say var answer = XElement.Parse(e.Result).Descendants("WS"); // null, but which part is null? The parsed XElement or the attempt to grab a descendant? If <WS>...</WS> is your root element, would the .Descendents("WS") call return the root element? Based on the documentation for XElement.DescendantsAndSelf(), I'm guessing not. Have you instead tried calling:
var answer = XElement.Parse(e.Result).Descendants("Info");
A quick test on my end showed that, with WS as the root element, calling XElement.Parse(e.Result).Descendants("WS"); yielded no results, while XElement.Parse(e.Result).Descendants("Info"); yielded the <Info>...</Info> element.

Duplicating content on save for a multilingual umbraco site

[Edit] I have actually been allowed to use the doc names, which makes it much easier but I still think it would be interesting to find out if it is possible.
I have to set a trigger to duplicate content to different branches on the content tree as the site will be in several languages. I have been told that I cannot access the documents by name(as they may change) and I shouldn't use node IDs either(not that I would know how to, after a while it would become difficult to follow the structure).
How can I traverse the tree to insert the new document in the relevant sub branches in the other languages? Is there a way?
You can use the Document.AfterPublish event to catch the specific document object after it's been published. I would use this event handler to check the node type alias is one that you want copied, then you can call Document.MakeNew and pass the node ID of the new location.
This means you don't have to use a specific node ID or document name to trap an event.
Example:
using umbraco.cms.businesslogic.web;
using umbraco.cms.businesslogic;
using umbraco.BusinessLogic;
namespace MyWebsite {
public class MyApp : ApplicationBase {
public MyApp()
: base() {
Document.AfterPublish += new Document.PublishEventHandler(Document_AfterPublish);
}
void Document_AfterPublish(Document sender, PublishEventArgs e) {
if (sender.ContentType.Alias == "DoctypeAliasOfDocumentYouWantToCopy") {
int parentId = 0; // Change to the ID of where you want to create this document as a child.
Document d = Document.MakeNew("Name of new document", DocumentType.GetByAlias(sender.ContentType.Alias), User.GetUser(1), parentId)
foreach (var prop in sender.GenericProperties) {
d.getProperty(prop.PropertyType.Alias).Value = sender.getProperty(prop.PropertyType.Alias).Value;
}
d.Save();
d.Publish(User.GetUser(1));
}
}
}
}

Please help me read a string from my XML file and just print it out on a MessageBox

Here's my XML file:
<?xml version="1.0" encoding="utf-8" ?>
<Hero>
<Legion>
<Andromeda>
<HeroType>Agility</HeroType>
<Damage>39-53</Damage>
<Armor>3.1</Armor>
<MoveSpeed>295</MoveSpeed>
<AttackType>Ranged(400)</AttackType>
<AttackRate>.75</AttackRate>
<Strength>16</Strength>
<Agility>27</Agility>
<Intelligence>15</Intelligence>
<Icon>Images/Hero/Andromeda.gif</Icon>
</Andromeda>
<WitchSlayer>
<HeroType>Agility</HeroType>
<Damage>39-53</Damage>
<Armor>3.1</Armor>
<MoveSpeed>295</MoveSpeed>
<AttackType>Ranged(400)</AttackType>
<AttackRate>.75</AttackRate>
<Strength>16</Strength>
<Agility>27</Agility>
<Intelligence>15</Intelligence>
<Icon>Images/Hero/Andromeda.gif</Icon>
</WitchSlayer>
</Legion>
</Hero>
Here's my method, but it isn't working so I don't know what to do.
public string GetHeroIcon(string Name)
{
//Fix later. Load the XML file from resource and not from the physical location.
HeroInformation = new XPathDocument(#"C:\Users\Sergio\Documents\Visual Studio 2008\Projects\Erth v0.1[WPF]\Tome of Newerth v0.1[WPF]\InformationRepositories\HeroRepository\HeroInformation.xml");
Navigator = HeroInformation.CreateNavigator();
Navigator.MoveToRoot();
Navigator.MoveToChild("Witch","Legion");
string x = "";
do
{
x += Navigator.Value;
} while (Navigator.MoveToNext());
return x;
}
I need help making a method that recieves a string parameter "Name" and then return all of the attributes of the XML element.
In pseudo-code:
public void FindHero(string HeroName)
{
//Find the "HeroName" element in the XML file.
//For each tag inside of the HeroName parent element,
//add it to a single string and blast it out through a MessageBox.
}
I'm LEARNING how to use this, please don't leave snobby remarks like, "we won't do this for you." I'm not asking for something groundbreaking here, just a simple use case for what I need on my program and for my learning nothing else. :D I'm doing the whole app in WPF and I can literally say that I've not done ONE single thing with previous knowledge, I'm doing this just to learn new things in my spare time.
Thanks a bunch SO, you rock!
private static string GetHeroIcon(string name)
{
XDocument doc = XDocument.Load("C:/test.xml");
return doc.Descendants(name).Single().Element("Icon").Value;
}
First off, since you've tagged this question WPF, you should know that WPF has excellent support for binding directly to XML data. You can then for instance map an image in the GUI directly to the Icon element in the XML file. See this link for example: http://www.longhorncorner.com/UploadFile/cook451/DataBindingXAML12102005134820PM/DataBindingXAML.aspx (first hit on google for "wpf databinding xml")
From code, you can create an XPathDocument from your XML file, then get a Navigator and finally run custom XPath queries on it, like so:
// Get's the value of the <icon> tag for a hero
var node = myNavigator.SelectSingleNode("/Legion/Hero/" + nameOfHero + "/Icon");
var icon = node.Value;
// To get all the nodes for that hero, you could do
var nodeIter = myNavigator.Select("/Legion/Hero/" + nameOfHero)
var sb = new StringBuilder();
while (nodeIter.MoveNext())
{
sb.AppendLine(nodeIter.Current.Name + " = " + nodeIter.Current.Value);
}
MessageBox.Show(sb.ToString());
See this kb article for an example.
DISCLAIMER: I copied and pasted the code from my code and did some refactoring in this window. It may not compile on first run but that may mean that it takes 10 minutes to get it to where it needs to be.
I would strongly recommend that you use XML deserialization. It's object oriented, type-safe, and just flat out slick.
Try this:
1) Create a series of classes: One for Hero, Legion, Witchslayer, and Andromeda.
Here is an example of the Andromeda class:
using System.Xml.Serialization;
[XmlRoot( "Andromeda" )]
public class Andromeda
{
[XmlElement( "Damage" )]
public String Damage
{
get;set;
}
[XmlElement( "Armor" )]
public double Armor
{
get;set;
}
}
The Hero class should contain an instance of Legion and Legion should contain the rest to mimic the layout of the XML packet.
2) Use the XmlSerializer to deserialize the data:
XmlSerializer xmlSerializer = new XmlSerializer( typeof( Hero ) );
using ( StringReader reader = new StringReader( xmlDataString ) )
{
Hero hero = ( Hero ) xmlSerializer.Deserialize( reader );
}
If you set it up right, you'll be left with a hero instance that contains the nested objects and all of the data. Cool, huh?

Categories