Hi, I've a blazor hybrid maui application and I have put the treeview.

I have created a service to search for files in the tree structure and a text input for the search. My problem is that when I select a file in the search zone the nodes are filtered where the file is located but I also see the other files in this directory;

do you have any idea where I made the mistake or did I forget something? thank you for your help.

Structure of the treeview before the searching

Structure of the treeview after the search

Hereafter the method

public async Task<List<FileNode>> SearchTreeNodesAsync(List<FileNode> nodes, string searchTerm, List<string> debugMessages)
    // Effacez les anciens messages de débogage avant chaque recherche
    debugMessages.Clear();
    debugMessages.Add($"Début de la recherche pour le terme : {searchTerm}");
    if (string.IsNullOrWhiteSpace(searchTerm))
        debugMessages.Add("Le terme de recherche est vide, retour de tous les nœuds.");
        return nodes;
    var result = new List<FileNode>();
    foreach (var node in nodes)
        debugMessages.Add($"Recherche dans le nœud : {node.Name}");
        // Vérifie si le nom du nœud contient le terme de recherche
        bool isMatch = node.Name.Contains(searchTerm, StringComparison.OrdinalIgnoreCase);
        if (isMatch)
            debugMessages.Add($"Correspondance trouvée : {node.Name}");
            // Si le nœud correspond, ajoute-le directement au résultat sans enfants
            result.Add(new FileNode
                Name = node.Name,
                Path = node.Path,
                IsDirectory = node.IsDirectory,
                Children = new List<FileNode>() // Pas besoin de charger les enfants ici
        else if (node.IsDirectory)
            debugMessages.Add($"Le nœud {node.Name} est un dossier, chargement des enfants...");
            // Charge dynamiquement les enfants si nécessaire
            if (node.Children == null || node.Children.Count == 0)
                node.Children = await LoadSubdirectoriesAsync(node.Path);
                node.Children.AddRange(await LoadDirectoryAsync(node.Path));
                debugMessages.Add($"Enfants chargés pour le dossier : {node.Name}, Nombre d'enfants : {node.Children.Count}");
            // Recherche parmi les enfants du dossier
            var filteredChildren = await SearchTreeNodesAsync(node.Children, searchTerm, debugMessages);
            if (filteredChildren.Count > 0)
                debugMessages.Add($"Des enfants correspondent au terme de recherche dans le dossier : {node.Name}");
                // Crée un nouveau dossier avec uniquement les enfants correspondants
                result.Add(new FileNode
                    Name = node.Name,
                    Path = node.Path,
                    IsDirectory = true,
                    Children = filteredChildren // Ajoute uniquement les enfants qui correspondent
                debugMessages.Add($"Aucun enfant correspondant trouvé dans le dossier : {node.Name}");
    debugMessages.Add($"Recherche terminée, nombre de résultats trouvés : {result.Count}");
    return result;

razor page

  private async Task SearchInTree(int treeNumber)
      switch (treeNumber)
          case 1:
              // Utilisez le debugMessages pour capturer chaque étape de la recherche
              filteredTree1 = await FileService.SearchTreeNodesAsync(tree1, searchTerm1, debugMessages);
              if (!filteredTree1.Any())
                  debugMessages.Add("Aucun résultat trouvé.");
                  debugMessages.Add($"{filteredTree1.Count} résultat(s) trouvé(s)");
        break;           
      StateHasChanged();    
											

Hi @sblb,

Consider updating the search logic to include only nodes that match the search term, ensuring that unnecessary child nodes are not added to the results. Additionally, optimize the child node loading logic to load only those that meet the search criteria.

Here is the modified sample code, please let me know if it works.

public async Task<List<FileNode>> SearchTreeNodesAsync(List<FileNode> nodes, string searchTerm, List<string> debugMessages)
    debugMessages.Clear();
    debugMessages.Add($"Début de la recherche pour le terme : {searchTerm}");
    if (string.IsNullOrWhiteSpace(searchTerm))
        debugMessages.Add("Le terme de recherche est vide, retour de tous les nœuds.");
        return nodes;
    var result = new List<FileNode>();
    foreach (var node in nodes)
        debugMessages.Add($"Recherche dans le nœud : {node.Name}");
        bool isMatch = node.Name.Contains(searchTerm, StringComparison.OrdinalIgnoreCase);
        if (isMatch)
            debugMessages.Add($"Correspondance trouvée : {node.Name}");
            result.Add(new FileNode
                Name = node.Name,
                Path = node.Path,
                IsDirectory = node.IsDirectory,
                Children = new List<FileNode>() 
        else if (node.IsDirectory)
            debugMessages.Add($"Le nœud {node.Name} est un dossier, chargement des enfants...");
            if (node.Children == null || node.Children.Count == 0)
                node.Children = await LoadSubdirectoriesAsync(node.Path);
                node.Children.AddRange(await LoadDirectoryAsync(node.Path));
                debugMessages.Add($"Enfants chargés pour le dossier : {node.Name}, Nombre d'enfants : {node.Children.Count}");
            var filteredChildren = await SearchTreeNodesAsync(node.Children, searchTerm, debugMessages);
            if (filteredChildren.Count > 0)
                debugMessages.Add($"Des enfants correspondent au terme de recherche dans le dossier : {node.Name}");
                result.Add(new FileNode
                    Name = node.Name,
                    Path = node.Path,
                    IsDirectory = true,
                    Children = filteredChildren
                debugMessages.Add($"Aucun enfant correspondant trouvé dans le dossier : {node.Name}");
    debugMessages.Add($"Recherche terminée, nombre de résultats trouvés : {result.Count}");
    return result;

Best Regards

Jason

Hi @sblb,

Updated it, please check again. By the way, if you could provide a minimal sample for us checking, then we can help you investigate the issue further, it also can save your time in this issue.

Best Regards

Jason

public string Name { get; set; } public string Path { get; set; } public bool IsDirectory { get; set; } public List<FileNode> Children { get; set; } = new List<FileNode>(); public class FileManagerService public async Task<List<FileNode>> LoadDirectoryAsync(string path, int page = 1, int pageSize = 50) var nodes = new List<FileNode>(); // Charger les répertoires var directories = Directory.GetDirectories(path) .Skip((page - 1) * pageSize) .Take(pageSize) .Select(dir => new FileNode Name = Path.GetFileName(dir), Path = dir, IsDirectory = true foreach (var dir in directories) nodes.Add(dir); // Charger les fichiers var files = Directory.GetFiles(path) .Skip((page - 1) * pageSize) .Take(pageSize) .Select(file => new FileNode Name = Path.GetFileName(file), Path = file, IsDirectory = false foreach (var file in files) nodes.Add(file); return await Task.FromResult(nodes); public async Task<List<FileNode>> LoadSubdirectoriesAsync(string path, int page = 1, int pageSize = 50) var nodes = new List<FileNode>( Directory.GetDirectories(path) .Skip((page - 1) * pageSize) .Take(pageSize) .Select(dir => new FileNode Name = Path.GetFileName(dir), Path = dir, IsDirectory = true return await Task.FromResult(nodes); public async Task<bool> MoveFileOrDirectoryAsync(string sourcePath, string targetPath) // Si targetPath est un dossier, combinez avec le nom du fichier source string destinationPath = targetPath; if (Directory.Exists(targetPath)) // targetPath est un dossier, donc on ajoute le nom du fichier source var fileName = Path.GetFileName(sourcePath); destinationPath = Path.Combine(targetPath, fileName); // Vérifie si un fichier ou un dossier existe déjà au chemin de destination if (File.Exists(destinationPath) || Directory.Exists(destinationPath)) Console.WriteLine("Le fichier ou répertoire de destination existe déjà : " + destinationPath); return false; // Déplace le fichier ou le dossier if (Directory.Exists(sourcePath)) Directory.Move(sourcePath, destinationPath); else if (File.Exists(sourcePath)) File.Move(sourcePath, destinationPath); Console.WriteLine("Le fichier ou répertoire source n'existe pas : " + sourcePath); return false; return true; catch (Exception ex) Console.WriteLine("Erreur lors du déplacement : " + ex.Message); return false; public async Task<List<FileNode>> SearchTreeNodesAsync(List<FileNode> nodes, string searchTerm, List<string> debugMessages) debugMessages.Clear(); debugMessages.Add($"Début de la recherche pour le terme : {searchTerm}"); if (string.IsNullOrWhiteSpace(searchTerm)) debugMessages.Add("Le terme de recherche est vide, retour de tous les nœuds."); return nodes; var result = new List<FileNode>(); foreach (var node in nodes) debugMessages.Add($"Recherche dans le nœud : {node.Name}"); bool isMatch = node.Name.Contains(searchTerm, StringComparison.OrdinalIgnoreCase); if (isMatch) debugMessages.Add($"Correspondance trouvée : {node.Name}"); result.Add(new FileNode Name = node.Name, Path = node.Path, IsDirectory = node.IsDirectory, Children = new List<FileNode>() else if (node.IsDirectory) debugMessages.Add($"Le nœud {node.Name} est un dossier, chargement des enfants..."); if (node.Children == null || node.Children.Count == 0) node.Children = await LoadSubdirectoriesAsync(node.Path); node.Children.AddRange(await LoadDirectoryAsync(node.Path)); debugMessages.Add($"Enfants chargés pour le dossier : {node.Name}, Nombre d'enfants : {node.Children.Count}"); var filteredChildren = await SearchTreeNodesAsync(node.Children, searchTerm, debugMessages); if (filteredChildren.Count > 0) debugMessages.Add($"Des enfants correspondent au terme de recherche dans le dossier : {node.Name}"); result.Add(new FileNode Name = node.Name, Path = node.Path, IsDirectory = true, Children = filteredChildren debugMessages.Add($"Aucun enfant correspondant trouvé dans le dossier : {node.Name}"); debugMessages.Add($"Recherche terminée, nombre de résultats trouvés : {result.Count}"); return result;

Razor pages

    <Card Style="width: 40%;">
        <CardHeader>
            <h4>Répertoire 1</h4>
        </CardHeader>
        <CardBody>
            <TextEdit @bind-Text="searchTerm1" Placeholder="Rechercher..." Style="width: 100%; margin-bottom: 10px;" />
            <Button Size="Size.Small" Color="Color.Primary" IconName="fas fa-search" Clicked="@(() => SearchInTree(1))" Style="margin-bottom: 10px;">Chercher</Button>
            <Button Size="Size.Small" Color="Color.Primary" IconName="fas fa-sync" Clicked="@(() => ReloadTree(1))" Style="margin-bottom: 10px;">Rafraîchir</Button>
            <div class="tree-container">
                <TreeView TNode="FileNode" Nodes="@filteredTree1"
                          HasChildNodes="@(node => node.IsDirectory)"
                          GetChildNodes="@(node => LoadSubnodesSync(node.Path))"
                          SelectedNodeChanged="OnSelectedNodeChanged1"
                          SelectionMode="TreeViewSelectionMode.Single">
                    <NodeContent> 
                        @if (context.IsDirectory)
                          <i class="fas fa-folder" style="color: #FFD700; margin-right: 5px;"></i>
                            <input type="checkbox" @onchange="(e => OnNodeCheckedChanged(context, e))" style="margin-right: 5px;" />
                           <i class="fas fa-file" style="color: gray; margin-right: 5px;"></i>
                        <span>@context.Name</span>                    
                    </NodeContent>
                </TreeView>
        </CardBody>
        <CardFooter>
            <div class="d-flex justify-content-between">
                <Button Size="Size.Small" Color="Color.Primary" Clicked="@(() => ChangePage(1, -1))" Disabled="@(_page1 == 1)">Précédent</Button>
                <Button Size="Size.Small" Color="Color.Primary" Clicked="@(() => ChangePage(1, 1))">Suivant</Button>
        </CardFooter>
    </Card>
    <div class="d-flex flex-column justify-content-center gap-2">
        <Button Color="Color.Primary" Size="Size.Small" IconName="fas fa-arrow-right" Clicked="MoveToDirectory2"><i class="fas fa-arrow-right"></i></Button>
        <Button Color="Color.Primary" Size="Size.Small" IconName="fas fa-arrow-left" Clicked="MoveFromDirectory2"><i class="fas fa-arrow-left"></i></Button>
    <!-- Répertoire 2 -->
    <Card Style="width: 40%;">
        <CardHeader>
            <h4>Répertoire 2</h4>
        </CardHeader>
        <CardBody>
            <TextEdit @bind-Text="searchTerm2" Placeholder="Rechercher..." Style="width: 100%; margin-bottom: 10px;" />
            <Button Size="Size.Small" Color="Color.Primary" IconName="fas fa-sync" Clicked="@(() => ReloadTree(2))" Style="margin-bottom: 10px;">Rafraîchir</Button>
            <div class="tree-container">
                <TreeView TNode="FileNode" Nodes="@filteredTree2"
                          HasChildNodes="@(node => node.IsDirectory)"
                          GetChildNodes="@(node => LoadSubnodesSync(node.Path))"
                          SelectedNodeChanged="OnSelectedNodeChanged2"
                          SelectionMode="TreeViewSelectionMode.Single">
                    <NodeContent>                       
                                @if (context.IsDirectory)
                                    <i class="fas fa-folder" style="color: #FFD700; margin-right: 5px;"></i>
                                    <i class="fas fa-file" style="color: gray; margin-right: 5px;"></i>
                                <span>@context.Name</span>                          
                    </NodeContent>
                </TreeView>
        </CardBody>
        <CardFooter>
            <div class="d-flex justify-content-between">
                <Button Size="Size.Small" Color="Color.Primary" Clicked="@(() => ChangePage(2, -1))" Disabled="@(_page2 == 1)">Précédent</Button>
                <Button Size="Size.Small" Color="Color.Primary" Clicked="@(() => ChangePage(2, 1))">Suivant</Button>
        </CardFooter>
    </Card>
    <div class="d-flex flex-column justify-content-center gap-2">
        <Button Color="Color.Primary" Size="Size.Small" IconName="fas fa-arrow-right" Clicked="MoveToDirectory3"><i class="fas fa-arrow-right"></i></Button>
        <Button Color="Color.Primary" Size="Size.Small" IconName="fas fa-arrow-left" Clicked="MoveFromDirectory3"><i class="fas fa-arrow-left"></i></Button>
    <!-- Répertoire 3 -->
    <Card Style="width: 40%;">
        <CardHeader>
            <h4>Répertoire 3</h4>
        </CardHeader>
        <CardBody>
            <TextEdit @bind-Text="searchTerm3" Placeholder="Rechercher..." Style="width: 100%; margin-bottom: 10px;" />
            <Button Size="Size.Small" Color="Color.Primary" IconName="fas fa-sync" Clicked="@(() => ReloadTree(3))" Style="margin-bottom: 10px;">Rafraîchir</Button>
            <div class="tree-container">
                <TreeView TNode="FileNode" Nodes="@filteredTree3"
                          HasChildNodes="@(node => node.IsDirectory)"
                          GetChildNodes="@(node => LoadSubnodesSync(node.Path))"
                          SelectedNodeChanged="OnSelectedNodeChanged3"
                          SelectionMode="TreeViewSelectionMode.Single">
                    <NodeContent>                        
                                @if (context.IsDirectory)
                                    <i class="fas fa-folder" style="color: #FFD700; margin-right: 5px;"></i>
                                    <i class="fas fa-file" style="color: gray; margin-right: 5px;"></i>
                                <span>@context.Name</span>
                    </NodeContent>
                </TreeView>
        </CardBody>
        <CardFooter>
            <div class="d-flex justify-content-between">
                <Button Size="Size.Small" Color="Color.Primary" Clicked="@(() => ChangePage(3, -1))" Disabled="@(_page3 == 1)">Précédent</Button>
                <Button Size="Size.Small" Color="Color.Primary" Clicked="@(() => ChangePage(3, 1))">Suivant</Button>
        </CardFooter>
    </Card>
@if (statusMessage != null)
    <div class="alert alert-danger mt-3">@statusMessage</div>
    <h5>Messages de Débogage</h5>
        @foreach (var message in debugMessages)
            <li>@message</li>
@code {
    private List<string> debugMessages = new(); // Liste des messages de débogage  
    private string searchTerm1 = string.Empty;
    private string? statusMessage = null;     
    private List<FileNode> selectedFiles = new();    
    private string? searchResultMessage1 = null;          
    private List<FileNode> tree1 = new();     
    private List<FileNode> filteredTree1 = new();    
      private int _page1 = 1; 
      private string  rootDireectroyPrelimianire = @"C:\\App\\rep_test_à_supprimer\\SMS_PRELIMINAIRE";
    private string rootDirectroySerie = @"C:\\App\\rep_test_à_supprimer\\ARTICLE";
    private string rootDireectroyHistorique = @"C:\\App\\rep_test_à_supprimer\\Historique";
    protected override async Task OnInitializedAsync()
        await ReloadTree(1);
        // Initialise les vues filtrées avec tous les éléments
        filteredTree1 = tree1;
    private void OnNodeCheckedChanged(FileNode node, ChangeEventArgs e)
        if ((bool)e.Value)
            // Ajoute le fichier à la liste des fichiers sélectionnés
            selectedFiles.Add(node);
            // Retire 
            selectedFiles.Remove(node);
    private List<FileNode> LoadSubnodesSync(string path, int page = 1, int pageSize = 50)
            var nodes = new List<FileNode>(FileService.LoadDirectoryAsync(path, page, pageSize).Result);
            return nodes;
        catch (Exception ex)
            Console.WriteLine($"Erreur lors du chargement des sous-nœuds : {ex.Message}");
            return new List<FileNode>();
    private async Task ReloadTree(int treeNumber, int page = 1)
        switch (treeNumber)
            case 1:
                tree1 = await FileService.LoadDirectoryAsync(rootDireectroyPrelimianire,page);
                filteredTree1 = new List<FileNode>(tree1);
                searchResultMessage1 = null;
                break;
       }     }     
 private async Task SearchInTree(int treeNumber)
        switch (treeNumber)
            case 1:
                // Utilisez 
                filteredTree1 = await FileService.SearchTreeNodesAsync(tree1, searchTerm1, debugMessages);
                if (!filteredTree1.Any())
                    debugMessages.Add("Aucun résultat trouvé.");
                    debugMessages.Add($"{filteredTree1.Count} résultat(s) trouvé(s)");
                break;
        StateHasChanged();
    private void OnSelectedNodeChanged1(FileNode node)
        selectedNode1 = node;
											

Hi @sblb,

Thanks for your sharing, currently provided code doesn't work, so you may need to provide more code, such as related components.

Thanks again for your patience.

Best Regards

Jason