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