1

I have a requirement to search something on list from current selected item in multilevel treeview. I have 4 Levels in my tree view and searching nodes on 4th level which match some condition.Look at below image: enter image description here

Now in this image i am searching Red Block which having 1 inside it(Failed one). Below is my code to search:

foreach (var Project in Projects)
{
    foreach (var Devices in Project.TestRuns)
    {
        foreach (var Category in Devices.TestModuleCategories)
        {
            foreach (var TestModule in Category.TestModules)
            {
                foreach (var statement in TestModule.TestModuleStatementList)
                {
                    if (statement.TestsFailed == 1 && !statement.IsSearched)
                    {
                        TestModule.TestModuleStatementList.ForEach(f => { f.IsCurrentFocused = false; });
                        statement.IsCurrentFocused = true;
                        statement.IsSearched = true;
                        TreeViewItem item = GetTreeViewItem(ViewTestDataTree, statement);
                        item.IsExpanded = true;
                        //ViewTestData.SetSelectedItem(ref ViewTestDataTree, item);
                        //item.Focus();
                        return;
                    }
                    statement.IsCurrentFocused = false;
                }

            }
        }
    }
}

Projects.ForEach(a => a.TestRuns.ForEach(b => b.TestModuleCategories.ForEach(c => c.TestModules.ForEach(d => d.TestModuleStatementList.ForEach(f => { f.IsSearched = false; })))));

Now problem is this i am running foreach loop everytime when searching but i want that if user is middle of list anywhere then search should start from there to last of the list and also remaining list from top. Is there any mechanism to start anywhere from a collection and search all nodes from down to up?

Camilo Terevinto
  • 31,141
  • 6
  • 88
  • 120
Pankaj Bhatia
  • 202
  • 1
  • 11
  • 1
    why dont you try with normal for(int i=startFirst; i< collection.Count ;i++) ? Start can be the position where you want to start and then use another loop to check from top till the previous start variable i – na th Mar 07 '18 at 10:07
  • If you are already doing a `foreach` on `Projects`, why are you then calling `ForEach` on everything? Is that a re-write or what? – Camilo Terevinto Mar 07 '18 at 10:08
  • I am using foreach on all because Projects has a collection of TestRuns and TestRuns have collection further till TestModuleStatementList and i want to do searchingon TestModuleStatementList. Now i want to start search from where user currently has focus on treeview so is there anyway to start foreach loop to start from a particular collection and then loop through complete list? – Pankaj Bhatia Mar 07 '18 at 10:11
  • 1
    I think this is the most nested `foreach` loops I've ever seen –  Mar 07 '18 at 10:26
  • @PankajBhatia again! this is why there is standard for loop. for(int i=startIndex; i< collection.Count ;i++). ofcourse you need the index number. See this https://stackoverflow.com/questions/1211608/possible-to-iterate-backwards-through-a-foreach – na th Mar 07 '18 at 10:26
  • @na th I know i can use for loop by saving index but it will take me to save index for every collection here and also 2 loops will be needed 1 for going down from that index and then 1 for upto that index thats why i am ignoring and trying to find some other solution. – Pankaj Bhatia Mar 07 '18 at 10:29
  • @MattBeldon Can u please suggest some way here to avoid so much nested loops? – Pankaj Bhatia Mar 07 '18 at 10:30
  • @PankajBhatia yes u need 2 loops. but it wont take more time. It is the same performance overhead, because what counts is the number of items you traverse. Otherwise there is no other efficient way to this with foreach. It is not created for such operations. – na th Mar 07 '18 at 10:35
  • @nath But there is another problem that when user has selected any other item in list with mouse click then i dont know how to find index for that item in collection. Is there any way where i can get index of child and all parents by selected item in TreeView? – Pankaj Bhatia Mar 07 '18 at 10:38
  • @PankajBhatia i have another suggestion..y dont u save all treeviewitem and statement object references in a dictionary? then you can easily get the treeViewItem that has ur conditions? – na th Mar 07 '18 at 10:40
  • @nath I am making search function which will highlight next fail item to user and when he press F3 again. I don't think using Dictionary will provide any solution. i am also searching that how can i find all parents/Grand Parents index from child in treeview. – Pankaj Bhatia Mar 07 '18 at 10:45
  • Just divide your foreach loops into methods which are returning integer as index. In that case u should be using for loops rather than foreach – Ugur Mar 07 '18 at 11:16

3 Answers3

2

For each loop doesn't provide access by index (it only holds a pointer to the current item and the next item in the collection), so it will start from the beginning every time.

JohnnyMyMan
  • 98
  • 1
  • 4
2

I would propose to encapsulate your foreach loops in a method which returns a IEnumerable instance of the search results:

IEnumerable<Statement> Search(/* put parameters here*/)
{
     Foreach(...)
         Foreach(...)
            Foreach(...)
              Foreach(...)
              {
                  if(...)
                     yield return statement;
              }
}

And in your main search management, keep an instance of IEnumerator object, in order to switch directly to the next search result:

 IEnumerator<Statement> searchResults;
 void NextResult()
 {
     if (searchResults == null)
         searchResults = Search( /* parameters...*/).GetEnumerator();

      if (!searchResults.MoveNext())
     {
         // In this situation we have reached the end of the tree
         // Restart from the beginning
         searchResults.Reset();
         searchResults.MoveNext();
     }

     var statement = searchResults.Current;
     if (statement != null)
     {
         // ...
         // Actions regarding current statement
         // ...
     }
 }

Of course the solution should be adapted to take into account search criteria changes.

Oxald
  • 837
  • 4
  • 10
1

You could create another list to flatten your tree:

List<TestModuleStatement> statements = new List<TestModuleStatement>(); // guessing at class name

Projects
    .ForEach(a => a.TestRuns
    .ForEach(b => b.TestModuleCategories
    .ForEach(c => c.TestModules
    .ForEach(d => d.TestModuleStatementList
    .ForEach(f => { statements.Add(f); })))));

Then you can use a while loop that will start at the current position + 1 and wrap around to start at the beginning and finish before the current position:

int selectedIndex = statements.FindIndex(f => f.IsCurrentFocussed);
int searchPos = (selectedIndex + 1) % statements.Count;
while (searchPos != selectedIndex)
{
    if (statement.TestsFailed == 1 && !statement.IsSearched)
    {
        statements.ForEach(f => { f.IsCurrentFocused = false; });
        statement.IsCurrentFocused = true;
        statement.IsSearched = true;
        TreeViewItem item = GetTreeViewItem(ViewTestDataTree, statement);
        item.IsExpanded = true;
        return;
    }
    searchPos = (selectedIndex + 1) % statements.Count;
}
Steve Harris
  • 5,014
  • 1
  • 10
  • 25