Search code examples
c#wpftreeviewnodeschildren

Adding a child node in the middle of a list of other children


This question is a follow up to this question. Basically what I want to do is add nodes to my treeView in numerical order. Right now I am working with a list of child nodes. Each child has a DisplayName of a numerical value. In my program I have a loop that checks if the node being added has a DisplayName that is less than the DisplayName of the current child in the loop. If the new child's DisplayName is less than the child being checked I would like to add the new node before the already existing one. I am having trouble figuring out a method to do this.

Here is my code:

var node = Data.GetAllChildren(x => x.Children).Distinct().ToList().First(x => x.identify == 'B');

//Get # of children -- if children exist
if (node.Children.Count() > 0)
{
    newChildTitle = int.Parse(nodeName.Value); //node to add

    for (int i = 0; i < node.Children.Count(); i++)
    {
        currentChildTitle = int.Parse(node.Children.ElementAt(i).DisplayName.Value); //current node value

        if (newChildTitle == currentChildTitle) //Check if location already exists
        {
            MessageBox.Show("Sorry, that location name already exists under this category.", "Duplicate Location Name", MessageBoxButton.OK, MessageBoxImage.Warning);
            break;
        }
        else if (newChildTitle < currentChildTitle) //If new location value is less, add before current location in Tree
        {
            //CODE WOULD GO HERE**        
            break;
        }
    }
}
else //if no children exist, just add
    node.Children.Add(CreateBlockingLocation(model, blockTreeCollection, nodeName));

XAML of the TreeView (the TreeView is bound to an ObservableCollection):

<!-- Tree view items & Functions -->
    <TreeView Name="Tree_One" IsEnabled="{Binding DataContext.DataTreeEnabled, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" ItemsSource="{Binding DataTree.Data}" />

Data and Properties of the ObservableCollection<HierarchicalVM> that the TreeView is bound to:

private HierarchicalVM _parent;
private ObservableCollection<HierarchicalVM> _children;

public HierarchicalVM()
{
    Commands = new ObservableCollection<Command>();
}

//Relationship Properties
public HierarchicalVM Parent
{
    get { return _parent; }
    set
    {
        _parent = value;
        NotifyPropertyChange(() => Parent);
    }
}
public ObservableCollection<HierarchicalVM> Children
{
   get { return _children ?? (_children = new ObservableCollection<HierarchicalVM>()); }
   set { _children = value; }
}

Current Solution attempt:

//Gets parent item (Data is the complete Tree)
var node = Data.GetAllChildren(x => x.Children).Distinct().ToList().First(x => x.identify == 'B'); 

if (!node.Children.Any(n => n.numValue == newNumValue))
{
    //Error is on this line: **Operator '.' cannot be applied to operand of type 'void'
    node.Children = node.Children.Add(newNode).Orderby(n => n.numValue);
}

Solution

  • Ok, assuming you already have a reference to the Parent object (not the DataTreeItem UI Element), there are two ways I can think of to accomplish this. This is off the top of my head with a little bit of checking against MSDN, so I apologize for any bugs.

    Parent parent = //however you are getting the parent
    if (!parent.Children.Any(n => n.DisplayName == newNode.DisplayName))
    {
       parent.Children.Add(newNode)
       parent.Children = new ObservableCollection<ViewModel>(parent.Children.OrderBy(n => n.DisplayName));
       //OR,
       parent.Children.InsertItem(parent.Children.IndexOf(parent.Children.OrderBy(n => n.DisplayName).Last(n => n.DisplayName < newNode.DisplayName)), newNode);
    }
    

    The first method just adds the new item and resorts the list, which gets you the same result as an in-place insert. The second actually does a in-place insert, but is more complicated because you have to find the index to insert at.

    If the list is already sorted you could remove the OrderBy in the second method. This also assumes that DisplayName is an int and a property of each child node (which from the code you posted it isn't, but it should be).

    Clearly I don't understand your architecture, so I will explain how I think it should be, and then perhaps you can discover where your code is broken.

    We have a object type called Parent, which contains a collection called children, like so:

    class Parent<T>
    {
       ObservableCollection<T> Children;
    }
    

    Now we have an collection of Parent called Data in the view model that the TreeView is bound to. Somehow, we are getting a single instance of this Parent class (which definitely shouldn't be void, the fact that your "node.Children" object is should be a HUGE red flag).

    If you agree with what I said above, the insert code I posted should work.

    Please let me know if I can clarify or add to this answer, or help in any other way.