I have not done anything in Tag for treenode. Then it is maybe very simple question. I googled through internet and could not find something helpful.
I am giving assigning some Tags for tree nodes via
public class NodeTag
{
public NodeTag(string name, string ID)//, bool component, string script, bool child) //,ref parrent
{
NodeName = name;
NodeID = ID;
}
public string NodeName { get; set; }
public string NodeID { get; set; }
}
Now I would like to call nodes using it Tag. I want to access to the NodeName. I tried as
var value = node.Tag;
But it gives me both fields as one should expected. I have an error if I use
var value = node.Tag.NodeID;
Would you please help me?
A Tag property has the Object type to provide ability to store any object as a node tag. You must cast an object stored in a Tag property to the required type, especially to the NodeTag.
var id = ((NodeTag)node.Tag).NodeID
You can add a few extension methods to make it reader friendly
public static int GetNodeID(this Node node)
{
return ((NodeTag)node?.Tag).NodeID;
}
Now you can use it
var id = node.GetNodeID();
var value = node.Tag as NodeTag;
string node_ID = value.NodeID;
Related
I am trying to get the name of an XML tag into a class property when performing XML deserialization. I need the name as a property since multiple XML tags share the same class. The XML and associated classes are defined below.
I have an XML response which I receive in the format:
<Data totalExecutionTime="00:00:00.0467241">
<ItemNumber id="1234" order="0" createdDate="2017-03-24T12:07:09.07" modifiedDate="2018-08-29T16:59:19.127">
<Value modifiedDate="2017-03-24T12:07:12.77">ABC1234</Value>
<Category id="5432" parentID="9876" itemOrder="0" modifiedDate="2017-03-24T12:16:23.687">The best category</Category>
... <!-- like 100 other elements -->
</ItemNumber>
</Data>
Deserialize done as follows:
XmlSerializer serializer = new XmlSerializer(typeof(ItemData));
using (TextReader reader = new StringReader(response))
{
ItemData itemData = (ItemData)serializer.Deserialize(reader);
}
And a class for the top level, ItemData:
[Serializable]
[XmlRoot("Data")]
public class ItemData
{
[XmlAttribute("totalExecutionTime")]
public string ExecutionTime { get; set; }
[XmlElement("ItemNumber", Type = typeof(ItemBase))]
public List<ItemBase> Items { get; set; }
}
ItemBase is defined as:
[Serializable]
public class ItemBase
{
[XmlElement("Value")]
public virtual ItemProperty ItemNumber { get; set; } = ItemProperty.Empty;
[XmlElement("ItemName")]
public virtual ItemProperty Category { get; set; } = ItemProperty.Empty;
... // like 100 other properties
}
And finally ItemProperty:
public class ItemProperty : IXmlSerializable
{
public static ItemProperty Empty { get; } = new ItemProperty();
public ItemProperty()
{
this.Name = string.Empty;
this.Value = string.Empty;
this.Id = 0;
this.Order = 0;
}
public string Name { get; set; }
[XmlText] // no effect while using IXmlSerializable
public string Value { get; set; }
[XmlAttribute("id")] // no effect while using IXmlSerializable
public int Id { get; set; }
[XmlAttribute("itemOrder")] // no effect while using IXmlSerializable
public int Order { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
reader.MoveToContent();
string name = reader.Name;
this.Name = name;
string val = reader.ReadElementString();
this.Value = val;
if (reader.HasAttributes)
{
string id = reader.GetAttribute("id");
this.Id = Convert.ToInt32(id);
string itemOrder = reader.GetAttribute("itemOrder");
this.Order = Convert.ToInt32(itemOrder);
string sequence = reader.GetAttribute("seq");
this.Sequence = Convert.ToInt32(sequence);
}
// it seems the reader doesn't advance to the next element after reading
if (reader.NodeType == XmlNodeType.EndElement && !reader.IsEmptyElement)
{
reader.Read();
}
}
public void WriteXml(XmlWriter writer)
{
throw new NotImplementedException();
}
}
The point of implementing the IXmlSerializable interface is because ultimately I need the name of the XML tag that is stored as an ItemProperty and that information is not captured when using the XML class/property attributes. I believe this is the case since the attributes determine which class to use for the deserialization and normally each XML tag would have an associated class. I don't want to go that direction since there are such a large number of different tags that may be in the response and they all share similar attributes. Hence the ItemProperty class.
I've tried also passing the name of the tag via a parameterized constructor in the ItemProperty and setting the name property there, but when deserialization is performed, it uses the default constructor and then sets the property values, so that is not an option.
Reflection doesn't work either since the class is always ItemProperty and therefore doesn't have a unique name.
Maybe how I'm structuring the XML attributes could be done differently to achieve what I'm trying to do, but I don't see it.
I'm open to any way to solve this problem, but I'm pretty sure it entails implementing IXmlSerializable.ReadXml(). I know it is the job of the XmlReader to read the entirety of the XML and advance the reader to the end of the text, but I'm a little unclear on how to do that.
TLDR: How do I properly implement IXmlSerializable.ReadXml() while capturing the XML tag name, tag value, and all attributes into the class properties?
Edit: with the updated ReadXml method, I get all the data needed at the ItemProperty level, but class ItemData, the Items list only ever has one item. I assume because I am not advancing the reader properly.
From the documentation for IXmlSerializable.ReadXml(XmlReader):
When this method is called, the reader is positioned on the start tag that wraps the information for your type. ... When this method returns, it must have read the entire element from beginning to end, including all of its contents. Unlike the WriteXml method, the framework does not handle the wrapper element automatically. Your implementation must do so. Failing to observe these positioning rules may cause code to generate unexpected runtime exceptions or corrupt data.
Your ReadXml() can be modified to meet these requirements as follows:
public void ReadXml(XmlReader reader)
{
reader.MoveToContent();
this.Name = reader.LocalName; // Do not include the prefix (if present) in the Name.
if (reader.HasAttributes)
{
var id = reader.GetAttribute("id");
if (id != null)
// Since id is missing from some elements you might want to make it nullable
this.Id = XmlConvert.ToInt32(id);
var order = reader.GetAttribute("itemOrder");
if (order != null)
// Since itemOrder is missing from some elements you might want to make it nullable
this.Order = XmlConvert.ToInt32(order);
string sequence = reader.GetAttribute("seq");
//There is no Sequence property?
//this.Sequence = Convert.ToInt32(sequence);
}
// Read element value.
// This method reads the start tag, the contents of the element, and moves the reader past the end element tag.
// thus there is no need for an additional Read()
this.Value = reader.ReadElementContentAsString();
}
Notes:
You are calling ReadElementString() whose documentation states:
We recommend that you use the ReadElementContentAsString() method to read a text element.
As suggested, I modified your ReadXml() to use this method. In turn, its documentation states:
This method reads the start tag, the contents of the element, and moves the reader past the end element tag.
Thus this method should leave the XmlReader positioned exactly as required by ReadXml(), ensuring the reader is advanced properly.
The XML attributes of each ItemProperty element must be processed before that element's content is read, since reading the content advances the reader past the element start -- and its attributes.
Utilities from the XmlConvert class should be used to parse and format XML primitives so that numerical and date/time values are not erroneously localized.
You probably don't want to include the namespace prefix (if any) in the Name property.
Demo fiddle here.
I have this class
class UserData
{
public UserData() { }
public string Name { get; set; }
public string Val { get; set; }
}
I have a normal ListBox. Im selecting Data from MySql.
private void ListUsers(string server)
{
List<UserData> ls = new List<UserData>();
foreach(dynamic obj in _data)
{
if(obj.servername == server)
{
ls.Add(new UserData() { Name = obj.username, Val = obj.password });
}
}
UserList.Sorted = true;
UserList.DisplayMember = "Name";
UserList.ValueMember = "Val";
UserList.DataSource = ls;
}
When debugging the ls it contains
[0]
Name => "Test",
Val => "12345"
[1]
Name => "Test2",
Val => "54321"
Now sometimes there ist only 1 postition in that list. If this happens, I want to select that entry or at least the Name and paste this into a textbox.
But for some reason I cant achive this. And Google didnt brought any results. At least non that suites to my problem.
I tried
rdpUserList.Items[0].ToString();
but this brings me ProjectName.UserData and not Test.
What is the right way to select the first Item in a list that was generated by a datasource ?
You're calling ToString() on an instance of the type ProjectName.UserData, which gives you its type name.
You want to access that instance's Name property instead.
If rdpUserList is a List<UserData>, you want this:
rdpUserList.Items[0].Name
If instead it's a datasource, you need to cast the item in order to access its properties:
((ProjectName.UserData)rdpUserList.Items[0]).Name
There are two approaches to this issue. The first is a logical problem; you need to call the Name property and not the ToString() method. Note that using this way, you need to cast to your object type.
((UserData)rdpUserList.Items[0]).Name
The second option is to override the ToString() method so you can call the name the way you tried.
class UserData
{
public UserData() { }
public string Name { get; set; }
public string Val { get; set; }
public override string ToString()
{ return this.Name; }
}
and then call with
rdpUserList.Items[0].ToString()
Sorry for previous post, I did not test before I answered,
try the following if you want, I just like linq :)
var item = rdpUserList.Items.OfType<ListItem>().First();
Class1 t = new Class1(){Id=Convert.ToInt32(item.Value), description = item.Text};
OR for just the name
string name = rdpUserList.Items.OfType<ListItem>().First().Text;
Could you please help me to find the solution to deserialize xml file which contains an empty tag?
Example is here:
<Report>
<ItemsCount></ItemsCount>
</Report>
And I want to deserialize it into object of class like:
public class Report{
public int? ItemsCount { get;set;}
}
my xml schema which i'm using in deserialization is:
[XmlRoot]
public partial class Report
{
private int? itemsCount;
[XmlElement(IsNullable = true)]
public int? ItemsCount {
get
{
return itemsCount;
}
set
{
itemsCount = value;
}
}
It works well if the ItemsCount tag is missing at all, but if it is exist and is empty at the same moment, in that case it throwing the exception regarding lines there this tag is located in xml.
I saw a lot of links here while trying to find the solution, but without success.
And also, i don't want to just ignore the tag for all the cases, i want to get a null value instead then it is empty.
XmlSerializer is trying to convert string.Empty value of tag to integer and failing. Change your property as below to convert data type to string:
[XmlElement]
public string ItemsCount {
get
{
return itemsCount;
}
set
{
itemsCount = value;
}
This will set property Itemscount to empty in the above case.
For null value for the above property the xml should be as below:
<ItemsCount xs:Nil='true'/>
How about this approach?
Define the class as follows:
public class Report
{
[XmlIgnore]
public int? ItemsCount { get; set; }
}
Due to the XmlIgnore attribute, this tag will be treated as unknown.
When creating the serializer add the event handler:
var xs = new XmlSerializer(typeof(Report));
xs.UnknownElement += Xs_UnknownElement;
In the event handler interpret an empty string as null:
private void Xs_UnknownElement(object sender, XmlElementEventArgs e)
{
var report = (Report)e.ObjectBeingDeserialized;
if (e.Element.InnerText == string.Empty)
report.ItemsCount = null;
else
report.ItemsCount = int.Parse(e.Element.InnerText);
}
Use the serializer as usual:
Report report;
using (var fs = new FileStream("test.xml", FileMode.Open))
{
report = (Report)xs.Deserialize(fs);
}
To my understanding, the described behaviour is correct; if the tag ItemsCount is missing, its value is null; if it is empty, its value cannot be converted from "" to a value of int?. That being said, it would be possible to implement some custom parsing into the accessors of ItemsCount, which would have to be of type string. However, this seems more like a workaround to me. If possible, the document should be changed to begin with.
I have an ObservableCollection<ParameterNodeEntity>.
ParameterNodeEntity is a custom class:
public class ParameterNodeEntity
{
public ParameterNodeEntity()
{
Nodes = new ObservableCollection<ParameterNodeEntity>();
Parameters = new ObservableCollection<ParameterEntity>();
}
public string Name { get; set; }
public string Path { get; set; }
public ObservableCollection<ParameterNodeEntity> Nodes { get; set; }
public ObservableCollection<ParameterEntity> Parameters { get; set; }
}
As you can see, it can contain both items from its own type (ParameterNodeEntity) and Parameters (ParameterEntity), as well as having a name and a path.
The ParameterEntity looks like this:
public abstract class ParameterEntity
{
public ParameterEntity(string name)
{
Name = name;
}
public string Name { get; set; }
public string Path { get; set; }
}
Both classes have a Path property.
As an example:
Node1
- Node2
- Node3
- Parameter1
- Parameter2
- Node4
- Node5
Node 4 would have the path Node1.Node2.Node3.Node4. Parameter1 would have the path Node1.Node2.Node3.(Parameter)Parameter1.
What I'm trying to do is to remove a specific item by its path. I tried the following:
public void DeleteParameterNode(ObservableCollection<ParameterNodeEntity> collection, string path)
{
collection.Remove(collection.SingleOrDefault(i => i.Path == path));
}
This works for the item at the highest level, but not for the others.
I appreciate any help and advice.
You don't have any code to navigate to a given node from the top level given an arbitrarily deep path. There is no way for ObservableCollection (or any other piece of code) to know what "Node1.Node2.Node3" even means.
It's no wonder it works for the top level but not the others. In your top-level collection, there is an object pathed "Node1", but the object pathed "Node1.Node2" is actually in the "Node1".Nodes collection, not the top level one.
You have to add code to parse your path and navigate the object tree (possibly recursively) in order to remove the right item from the right collection.
Unfortunately, ObservableCollection does not have any possibility to find a specific object inside a multi-level collection.
Using this approach will deliver the desired object:
private ParameterNodeEntity _searchNodeResult;
public void SearchByPath(ParameterNodeEntity nodeEntity, string path)
{
bool found = false;
if (nodeEntity.Path != path)
{
foreach (ParameterNodeEntity subNode in nodeEntity.Nodes)
{
if (!found)
{
SearchByPath(subNode, path);
}
}
}
else
{
_searchNodeResult = nodeEntity;
found = true;
}
}
SearchByPath requires two parameters: First, the top-level object and second, the parameter to find the desired object.
This is not a very beautiful approach, but in my case it works because the searched-for item definitely exists in all cases.
_searchNodeResult will hold the found object.
Have you tried this:
public void DeleteParameterNode(ObservableCollection<ParameterNodeEntity> collection, string path)
{
foreach (var item in collection.Where(i => i.Path == path))
{
collection.Remove(item);
}
}
I would like to populate the DescriptionAttribute of a property, from a string that will be initialized from an xml file. The property will be used in a propertygrid.
The main thing is getting the descriptions from an xml file. How to get it into a const string that i can then use as the DescriptionAttribute of a property.
I have tried a few things with no success so any help would be appreciated.
or is there another wey to assign the xml values to the description? A typeconverter perhaps? Just point me to the right direction please.
public class1
{
string BaboonDescription = "";
string TigerDescription = "";
const string SnakeDescription = "A snake is bla bla bla";
// method that extracts the descriptions from the xml file.
public void PopulateFromXml(string xmlfile)
{
XDocument xDoc = XDocument.Load(xmlfile);
var items = from i in xDoc.Descendants("item")
select i;
foreach (var item in items)
{
switch (item.Attribute("name").Value)
{
case "Baboon":
BaboonDescription = item.Value; // Assigns BaboonDescription the description from xml.
break;
case "Tiger":
TigerDesscription = item.Value; // Assigns TigerDescription the description from xml.
break;
default:
break;
}
}
}
}
public class2 : class1
{
[Description(BaboonDescription)] // problem here. Telling me that I need const string. But i have to get the strings from an xml.
public string Baboon { get; set; }
[Description("tiger is bla bla")] // this one works but I want the description from the xml.
public string Tiger { get; set; }
[Description(SnakeDescription)] // this also works but I want the description from the xml.
public string Snake { get; set; }
}
DescriptionAttribute cannot be compiled and be dynamic at the same time.
Check out my answer to this SO question that demonstrates how to build a dynamic type descriptor: Optimize class for PropertyGrid
With the DynamicTypeDescriptor class, you can build a wrapper class with description attributes you want.