Boris Makhlin
Boris Makhlin

Reputation: 318

Hierarchical Data In TreeView. Find an node in tree

I have a TreeView in which I display Hierarchical Data.

Model

class Node : INotifyPropertyChanged
{
    public string Name { get; set; }
    public ObservableCollection<Node> Nodes { get; set; } = new ObservableCollection<Node>();
}

Storage of the nodes:

class NodesStorage : ObservableCollection<Node>
{
    public NodesStorage()
    {
        var node = new Node { Name = "1" };
        node.Nodes.Add(new Node { Name = "1a" });
        node.Nodes.Add(new Node { Name = "1b" });
        node.Nodes.Add(new Node { Name = "1c" });

        var node1 = new Node { Name = "2" };
        node1.Nodes.Add(new Node { Name = "2a" });
        node1.Nodes.Add(new Node { Name = "2b" });
        node1.Nodes.Add(new Node { Name = "2c" });

        this.Add(node);
        this.Add(node1);
    }
}

ViewModel

class MainWindowViewModel
{
    public MainWindowViewModel()
    {
        Nodes = new NodesStorage();
    }

    public ObservableCollection<Node> Nodes;
}

View

<!--... Set DataContext ...-->

<TreeView ItemsSource="{Binding Path=Nodes}">
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Path=Nodes}">
            <TextBlock Text="{Binding Name}"/>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

<!--...-->

Question

And now I want to make the method that returns true, if the tree contains the node with the folowing name and false in another case:

public static bool IsTreeContains(Node node, string itemName)
{
    if (node.Name == itemName)
        return true;

    foreach (var n in node.Nodes)
    {
        return IsTreeContains(n, itemName);
    }

    // And what should I write here?
}

I think I can use backtracking here. But it's a realy bad idea.
What do you think?

Upvotes: 0

Views: 593

Answers (2)

Sathish Guru V
Sathish Guru V

Reputation: 1489

I could sense that you are doing "kind of" Depth First Search in the Tree.

To escape the recursion, you need two conditions in success and in failure and the result has to be propagated to the caller through recursion.

public static bool IsTreeContains(Node node, string itemName)
{
    // If the current Node matches
    if (node.Name == itemName)
        return true;

    // End of the Tree or the Node Collection
    if (node.Nodes.Count == 0)
        return false;

    foreach (var n in node.Nodes)
    {
        if (IsTreeContains(n, itemName))
            return true;
    }

    return false;
}

I think I just completed your solution, but there are many ways to solve this algorithmically.

Hope this helps.

EDIT: Corrected the correct algorithm based on the comments.

EDIT 1: Corrected the loop runs only on the First Child Node. Thanks @BionicCode.

Upvotes: 3

ganchito55
ganchito55

Reputation: 3607

You have a problem with your recursive method:

foreach (var n in node.Nodes)
{
    return IsTreeContains(n, itemName);
}

That returns makes that the loop is only executed once.

Here is my code:

public static bool IsTreeContains(Node node, string itemName)
{
    if (node.Name == itemName)
        return true;

    foreach (var n in node.Nodes)
    {
        if(IsTreeContains(n, itemName))
            return true;          
    }

    return false;
}

First exit condition, then if the item is found stop iterating, if not, continue iterating. Finally if the node and every n-child is different of the item searched, return false.

I hope this can help you.

Upvotes: 1

Related Questions