Реакцию на событие Change рассмотрим позже, а сейчас решим проблему поиска того или иного узла в дереве. Во всех событиях, кроме Created, нам необходимо искать конкретный узел в дереве, структура ветвей которого лишь приближенно соответствует реальной структуре файловой системы компьютера. Все рассмотренные реакции содержат вызов метода FindNode, который должен отыскать среди ветвей объекта TreeView определенный узел.
Поиск в дереве — излюбленная тема многих учебников по алгоритмам и структурам данных. Рекурсия здесь прямо напрашивается. Если вы сами напишите рекурсивную функцию, то получите большое удовольствие (это правда, я проверял). Недостатком рекурсивных процедур является тот факт, что они сильно загружают стек, в котором хранятся все локальные переменные промежуточных вызовов. Параметры функции — это тоже локальные переменные, поэтому рекурсивная функция должна иметь их как можно меньше. Думаю, что в нашем случае использование рекурсии вполне оправдано и работает достаточно эффективно.
При разработке рекурсивной процедуры следует задать ее глубину — барьер, при достижении которого рекурсия заканчивается. В нашем случае барьером является найденный узел, или обнаружение пустой ветви. В нашем случае все непустые узлы дерева помечены свойством Tag==true, а пустые Tag==null, и это позволяет задать барьер.
Метод FindNode всего лишь подготавливает вход в рекурсивную процедуру FindRecursive и формирует адрес искомого узла в переменной pathToFind, которая определена в классе FileSystemControl.
TreeNode FindNode(string path)
{
pathToFind = (rootName + '\\' + path).TrimEnd('\\');
return FindRecursive(tree.Nodes[0]);
}
Вы помните, что дерево заполняется постепенно? Если кем-то и где-то созданная или удаляемая папка расположена в файловой системе глубоко, то в нашем дереве ее может и не быть. Цепь рекурсивных вызовов позволяет углубиться в дерево, начиная от корня и заканчивая узлом, который надо найти. FindRecursive должна возвратить либо ссылку на обнаруженный узел, либо null, в зависимости от того, есть ли узел с указанным путем в нашем дереве или нет. Учитывая сказанное, рассмотрим код метода FindRecursive.
TreeNode FindRecursive(TreeNode node)
{
foreach (TreeNode n in node.Nodes)
{
if (pathToFind.Equals(n.FullPath, StringComparison.InvariantCultureIgnoreCase))
return n;
if (pathToFind.StartsWith(n.FullPath + '\\') && n.Tag != null)
return FindRecursive(n);
}
return null;
}
Как видите, метод вызывает сам себя до тех пор, пока не доберется до нужного узла. Если он обнаружит целину (n.Tag == null), то рекурсия завершается и мы возвращаем null. Запустив приложение в данный момент, вы обнаружите, что оно следит за появлением новых папок, за их удалением и изменинем имени. Чтобы проверить это, временно закомментируйте нереализованные ветви, запустите Windows Explorer, создайте несколько папок в разных директориях одного из дисков, запустите приложение и убедитесь, что оно реагирует на появление новых папок.
Удалите одну из папок в Explorer'е и убедитесь, что дерево, живущее в Library.dll, которая обслуживает Client.exe, получает известие от одного из делегатов списка делегатов события Deleted, которое возбуждает один из наблюдателей FileSystemWatcher, который получил известие об этом событии от операционной системы Windows. Проверьте также реакцию на создание, удаление и переименование файлов.
Недостатком наблюдателей в предыдущих версиях .NET было то, что они не учитывали регистра символов. Это, видимо, объясняется тем, что файловая система Windows традиционно не учитывает регистр. Мы и сейчас не можем в одном месте создать две папки с именами: folder и Folder. Но попытки приблизиться к грамотному человеческому языку предпринимаются давно, поэтому Windows Explorer показывает объекты файловой системы с учетом регистра и наблюдатели теперь также учитывают регистр.
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.