C# Receive certain object from recursive ObservableCollection - c#

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);
}
}

Related

Reference known property in (List<someClass>)someObject

have tried some searches. Probably my lack of knowledge that I'm not using the right search terms or perhaps just not understanding the answers.
I have a method that is being passed an object, which I want to output a particular value to a text file.
I already know the object will be a List< someClass > of a few different possible classes (customers/employees/items etc). But all of the classes contain the same string property (e.g.) string idNumber.
So something like this:
public static void OutputFile(object myInput)
{
foreach (someGenericObject in (List<anyType>)myInput)
{
string textToOutput = someGenericObject.idNUmber;
//output the text to somewhere else here
}
}
I feel like as long as I know that it will always contain a this "idNumber" property regardless of the type, that I should be able to reference it somehow. But I just can't seem to get my head around it?
The error I typically get is something like:
Cannot cast List< Employee > to List< object > etc.
Thanks in advance
As I suggested in the comments, if you have the ability to modify these classes, you can have them all inherit from an interface IHasIdNumber with an idNumber property.
Your method would then become:
public static void OutputFile(IEnumerable<IHasIdNumber> myInput)
{
foreach (var item in myInput)
{
string textToOutput = item.idNUmber;
//output the text to somewhere else here
}
}
There are a few ways you can solve this.
Recommended way: Implement common interface:
public interface INumberable { // I'm sure you can come up with a better name...
string IDNumber { get; set; }
}
And then all the possible classes that can be passed into the method will implement INumberable. Your method can then look like this:
public static void OutputFile(List<INumerable> myInput)
{
foreach (var someGenericObject in myInput)
{
string textToOutput = someGenericObject.idNUmber;
//output the text to somewhere else here
}
}
Not-so-recommended way: Reflection:
Type t = someGenericObject.GetType();
var p = t.GetProperty("idNumber");
string theStringYouWant = (string)p.GetValue(someGenericObject);
Note that this is not very safe.
You can use [dynamic].
foreach (var someGenericObject in (dynamic)myInput)
{
//...
}
If all your classes have the same property you want to access in foreach loop you can do in via interface.
public interface ISmth {
int MyProperty { get; set; }
}
public class Student : ISmth {
public int MyProperty { get; set; }
}
public class Employee : ISmth {
public int MyProperty { get; set; }
}
public static void DoSmth(object myObj) {
foreach(ISmth item in (List<object>)myObj) {
Console.Write(item.MyProperty);
}
}
List<Student> stdList = new List<Student>();
DoSmth(stdList.Cast<object>().ToList());

C# class for a graph Node

I tried creating a Binary tree assuming it will have max of 2 child nodes. But for a graph it will have one or more graph nodes connected to it. How can I create a simple class for a graph node like the below which I created for a tree. The reason for suggestion is because I am trying for a simple code to find all nodes info tied to a particular node.
class TreeNode
{
public int value { get; set; }
public TreeNode leftNode { get; set; }
public TreeNode rightNode { get; set; }
}
In case of graph, any node can have arbitrary many edges (neighbor nodes), so you have to use a collection, say List<T>:
// TValue - let's generalize
// (e.g. you may want ot have double or string value associated with the node)
class GraphNode<TValue> {
private List<GraphNode<TValue>> m_Connected = new List<GraphNode<TValue>>();
public TValue value { get; set; }
// get (and no set) - we don't want to assign the collection as whole
// if we want to add/remove a neighbor we'll call Neighbors.Add, Neighbors.Remove
public List<GraphNode<TValue>> Neighbors {
get {
return m_Connected;
}
}
}

calling/using the given attribute

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;

Method with generic return type but not generic input. Is this possible?

Suppose we have a NodeData class:
public class NodeData<T>
{
public string Name;
public T Value;
public NodeData(string name, T value)
{
this.Name = name;
this.Value = value;
}
}
And a base Node class and child classes that have several properties with type NodaData:
public class Node
{
public List<NodeData<T>> listOutputs<T>()
{
var fieldInfos = GetType().GetFields();
var list = new List<NodeData<T>>();
foreach (var item in fieldInfos)
{
Type t = item.FieldType;
string name = item.Name;
if (t == typeof(NodeData<T>))
{
var output = new NodeData<T>(name, default(T));
list.Add(output);
}
}
return list;
}
}
public class TestNode : Node {
public NodeData<int> data;
public NodeData<double> data2;
public NodeData<double> data3;
public TestNode ()
{
data = new NodeData<int>("test", 111);
data2 = new NodeData<double>("test", 113);
}
}
As you can see there is a method which lists all outputs with type T in the Node class So I can find what are the output fields of the child class in runtime:
TestNode node = new TestNode ();
var list = node.listOutputs<int>(); // this returns data
But I need to know how to use this method to list all NodeOutputs of any type T. In this example int and double. Do I need to add a method with this signature public List<NodeData<T>> listOutputs() // should return all properties data, data2, data3. Is it possible to have method like this? return type is generic but there is no type argument for method.
Even after your edit(s) it is not entirely clear what you are trying to achieve but here are my assumptions:
-You want to have some kind of Node object that acts as a container for different types of NodeData elements.
-You want to be able to return one list from this Node object that contains all NodeData elements stored in the Node container, regardless of the NodeData objects' type.
Instead of returning a List> object from the listOutputs methods, just return the non-generic version of the List object. Then you don't have to deal with T in the method call.
The logic that loops through the objects in the non-generic list can then examine the type to process the contained NodeData objects correctly.
Important note: My proposed solution is by no means pretty but I think it answers the question. In my opinion something is already seriously flawed from an OO point of view in the presented code (e.g. use of reflection) and a better solution would have to start by changing the underlying data structures. But that can only be done if we have more information how this is to be used, e.g. what kind of logic consumes the returned list.
You can create a base interface that will be used to return the generic data.
public interface INodeData
{
string Name { get; }
}
public class NodeData<T> : INodeData
{
public string Name { get; private set; }
public T Value { get; private set; }
public NodeData(string name, T value)
{
this.Name = name;
this.Value = value;
}
}
I modified the function to return a list of the interface. Doing this you won't depend on T.
public class Node
{
public List<INodeData> listOutputs()
{
var fieldInfos = GetType().GetFields();
var list = new List<INodeData>();
foreach (var item in fieldInfos)
{
INodeData data = GetType().GetField(item.Name).GetValue(this) as INodeData;
list.Add(data);
}
return list;
}
}
If you test the method, it should return the fields in a list. To work with a specific type, you can make use of is before using the type you search for.
public class TestNode : Node
{
public NodeData<int> data;
public NodeData<double> data2;
public NodeData<double> data3;
public TestNode()
{
data = new NodeData<int>("test", 111);
data2 = new NodeData<double>("test", 113);
}
}
private static void Main(string[] args)
{
TestNode node = new TestNode();
var list = node.listOutputs(); // this returns data
}
This may well be an XY problem, in that you probably want to rethink how you are designing your classes because using reflection in this way doesn't seem right. But give the problem you've presented, I'd tackle it like this:
public abstract class NodeDataBase
{
public string Name { get; set; }
public NodeData(string name)
{
this.Name = name;
}
// this isn't actually needed, but might be helpful
public abstract object GetValue();
}
public class NodeData<T> : NodeDataBase
{
public T Value { get; set; }
public NodeData(string name, T value) : base(name)
{
this.Value = value;
}
public override object GetValue()
{
return Value;
}
}
And now your method signature would be:
public List<NodeDataBase> listOutputs()
And with the list returned, you can use the GetValue method to get the actual values without needing to cast to the right generic type to be able to get at the Value property.
You could also just have a return type of List<object>, but then you'll have to cast each member of that list to the right generic type before you can access it's properties.
You can also avoid that nasty reflection code, instead of having data, data1, and data2, you could simply do this in your Node class:
public class Node
{
public List<NodeDataBase> Data { get; protected set; }
public Node()
{
Data = new List<NodeDataBase>();
}
}
And now you don't even need your listOutputs method because you can just get the list from the node (unless you actually wanted a copy, but that's fairly trivial to implement).
And you TestNode would be just:
public class TestNode : Node {
public TestNode ()
{
Data.Add(new NodeData<int>("test", 111));
Data.Add(new NodeData<double>("test", 113));
}
}

Iteration of an object, that may have parent, or child of it's own same type

I have a class that may have a parent, or list of children of the same type of it's own. The following code snippet should explain my scenario.
public abstract class X{
public virtual List<X> ChildItems { get; set; }
public virtual X ParentItem { get; set; }
}
I would like to know if there is a particularly efficient method to traverse the objects from an object of type X, checking if the object has a parent, or children starting from bottom up.
public static void SaveSetup(X obj) {
//logic here
}
Any help is appreciated.
What you are dealing with is a tree structure (or possibly many disconnected tree structures). A tree structure has a root element. Usually, a tree structure is traversed starting from the root. If you want to start from any element in the tree, I suggest you to first get the root element and then traverse in the usual manner.
The easiest way to traverse a recursive structure is to use recursive method, i.e., a method that calls itself.
public abstract class X
{
public virtual List<X> ChildItems { get; set; }
public virtual X ParentItem { get; set; }
// Method for traversing from top to bottom
public void Traverse(Action<X> action)
{
action(this);
foreach (X item in ChildItems) {
item.Traverse(action);
}
}
// Get the root (the top) of the tree starting at any item.
public X GetRootItem()
{
X root = this;
while (root.ParentItem != null) {
root = root.ParentItem;
}
return root;
}
}
Now you can save the setup with
X root = item.GetRootItem();
root.Traverse(SaveSetup);
Example with lambda expression. Prints every item of the tree assuming that ToString() has been overridden to return a meaningful string.
root.Traverse(x => Console.WriteLine(x));
Traverse from given object to root (ParentItem = null)
public static void SaveSetup(X obj) {
while (obj != null)
{
// logic here
obj = obj.ParentItem;
}
}

Categories