Windows Management Instrumentation - инфраструктура управления операционной системой, страница 9

В данный момент можно выбрать два пути развития проекта: заполнить список файлов в правой части формы, или ввести реакции на события файловой системы. Мы выбераем второй путь. Вы помните, что в классе уже присутствуют два наблюдателя (объекта класса 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)

{