В данный момент можно выбрать два пути развития проекта: заполнить список файлов в правой части формы, или ввести реакции на события файловой системы. Мы выбераем второй путь. Вы помните, что в классе уже присутствуют два наблюдателя (объекта класса FileSystemWatcher). Так как дерево пока умеет отображать только папки, то задействуем наблюдателя за папками (dirWatcher).
Важными свойствами наблюдателя являются Path и EnableRaisingEvents. Первое — позволяет задать файловый путь к папке или диску, за содержимым которой должен наблюдать dirWatcher, второе — просто включает или выключает его. Оба этих свойства еще спят — не заданы (если вы идете моим путем).
Суровым ограничением наблюдателя является то, что он не может следить за всей файловой системой. Самой крупной единицей слежения, по-видимому, является логический диск. Если так, то этот диск не должен быть жестко зафиксирован. Очевидно, что он должен изменяться по мере того, как пользователь осуществляет навигацию по дереву.
Теперь надо решить, в каком месте программы и как следует установить свойство Path? Так как дерево заполняется постепенно, то бесполезно следить за папками, информация о которых в нем отсутствует. Из этого делаем вывод: свойство Path должно зависеть от действий пользователя и наиболее удобным местом, где следует корректировать свойство Path (на мой взгляд) является реакция на событие AfterExpand.
private void tree_AfterExpand (object sender, TreeViewEventArgs e)
{
tree.EndUpdate(); Cursor = Cursors.Default;
if (Текуший узел (узнайте его из параметра) — не корневой)
{
dirWatcher.Path = Из узла узнайте путь и вычлените из файлового пути логический диск
(используйте для этого метод класса Path) Все в одной строке кода!!
dirWatcher.EnableRaisingEvents = true;
}
}
Корневой узел следует обойти — он явно все портит. Итак, мы выставили сторожевую собаку по кличке dirWatcher из породы FileSystemWatcher. Мы приказали ей сторожить один из логических дисков. Какой? Она залает, когда произойдут какие-либо изменения в этом (раскрытом в последнюю очередь) диске. При этом она (dirWatcher, конечно) вызовет одну из существующих, но пока не реализованных нами функций: dirWatcher_Changed или dirWatcher_Renamed. Пора за них браться.
В качестве упражнения укажите то место в программе, где наша собака получила наставление (делегата с заданием). Как создан делегат? Какой тип имеет его задание? Сколько действующих лиц (наблюдателей, или собак, делегатов, заданий) уже декларировано? Как изменить количество каждого?
private void dirWatcher_Changed (object sender, FileSystemEventArgs e)
{
switch (в зависимости от типа изменений (узнайте из параметра))
{
case WatcherChangeTypes.Created:
// Пока не знаем, что делать
break;
case WatcherChangeTypes.Deleted: // Если удаляемый узел существует
if (NodeExists (с файловым путем (узнайте его из параметра))
searchedNode.Remove();
break;
}
}
}
Логика реакции довольно прозрачна, но надо разработать функцию NodeExists. Очевидно она должна взвращать true или false, в зависимости от того, есть ли узел с указанным путем в нашем дереве или нет. Вы помните, что дерево заполняется постепенно? Если удаляемый (кем-то и где-то) файл расположен в файловой системе глубоко, то в нашем дереве его может и не быть.
Итак, нам надо искать узел в дереве. Поиск в дереве — это излюбленная тема многих учебников по алгоритмам и структурам данных. Рекурсия здесь прямо напрашивается. Если вы сами напишите рекурсивную функцию, то получите большое удовольствие (это правда, я проверял). Рекурсивная функция сильно загружает стек, так как в нем хранятся все локальные переменные промежуточных вызовов. Параметры функции — это локальные переменные, поэтому рекурсивная функция должна иметь как можно меньше параметров.
Другой важный момент: при разработке рекурсивной процедуры следует задать ее глубину — барьер, при достижении которого рекурсия заканчивается. В нашем случае считайте, что барьером является найденный узел. Если ссылка searchedNode (уже есть в классе) не нулевая, то узел найден и пора возвращаться. Учитывая сказанное, напишем функцию NodeExists (она еще не рекурсивная).
private bool NodeExists (string name)
{
searchPath = name; // Устанавливаем искомый путь
searchedNode = null; // Устанавливаем барьер. (Пока узел не найден)
FindRecursive (root); // Эта функция должна искать узел (рекурсивно)
return searchedNode != null; // Нашли или не нашли
}
Теперь создайте функцию рекурсивного поиска. Она должна вызывать чувство эстетического удовольствия.
private void FindRecursive (TreeNode node)
{
foreach (Для каждого узла из коллекции узлов, вложенных в node)
{
if (Уже найден, выходим)
return;
string cur = Полный файловый путь текущего узла (узнайте из самого узла)
(но выкиньте из него"My Computer"/ Здесь понадобится rootLength
if (Текущий равен искомому) // Нашли
{
searchedNode = Запоминаем узел;
return;
}
if (искомый путь начинается со строки cur, то стоит зайти внутрь узла)
// Рекурсивный (или автовызов) с одним параметром
}
}
Если вы справились с задачей, то приложение должно следить за удалением папок. Чтобы проверить это, запустите обычный Explorer, создайте несколько папок в разных директориях, запустите наше приложение, убедитесь, что оно видит тестовые папки, удалите папку в Explorer'е и убедитесь, что наше дерево (в нашей DLL, которой пользуется наша EXE) получает известие от делегата, засланного в событие Changed, которое возбуждает наблюдатель dirWatcher, который получил известие об этом событии от операционной системы Windows. Не знаю, как вам, но мне это нравится.
Мы провели достаточно большую работу и пользуясь ее плодами, теперь можем легко организовать реакцию на событиt Renamed наблюдателя dirWatcher. Проверьте тем же способом, должно работать. Для переименования удобно пользоваться клавишей F2.
private void dirWatcher_Renamed (object sender, RenamedEventArgs e)
{
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.