Опубликован: 25.03.2010 | Уровень: для всех | Доступ: платный
Лекция 15:

Стандартные элементы управления

Совместная работа элементов TreeView и ListView в Проводнике файловой системы

При выполнении последнего примера видно, что войти в дочерний каталог можно, но вновь вернуться в родительский каталог нельзя. Чтобы сделать программу, совсем похожую на Проводник Windows, давайте объединим два последних примера, представленные классами DirectoryTreeView и DirectoryListView. Для порядка поместим коды классов в отдельных файлах проекта. Вот какая программа в итоге получилась.

using System;
using System.Drawing;
using System.Windows.Forms;
// Пространство имен для работы с каталогами
using System.IO;
    
namespace Test
{
    // Пользовательский класс построения дерева каталогов
    // как расширение библиотечного класса TreeView
    class DirectoryTreeView : TreeView
    {
        public DirectoryTreeView()  // Конструктор
        {
            // Создаем список иконок из внедренного ресурса
            ImageList imageList = new ImageList();
            // Порядок соблюдать - от него зависят индексы списка
            imageList.Images.Add(new Icon(GetType(),
                "Resource.CLSDFOLD.ICO"));  //0 - свернут
            imageList.Images.Add(new Icon(GetType(),
                "Resource.OPENFOLD.ICO"));  //1 - развернут
            imageList.Images.Add(new Icon(GetType(),
                "Resource.35FLOPPY.ICO"));  //2 - гибкий диск
            imageList.Images.Add(new Icon(GetType(),
                "Resource.CDDRIVE.ICO"));   //3 - жесткий диск
            imageList.Images.Add(new Icon(GetType(),
                "Resource.DRIVENET.ICO"));  //4 - папки
    
            // Используем наследуемые от TreeView свойства
            // Присоединяем список к свойству ImageList элемента TreeView
            this.ImageList = imageList;
            // В дереве в каждый момент времени может быть выделен только один узел
            this.ImageIndex = 0;        // Когда узел просто отображен (Collapsed)
            this.SelectedImageIndex = 1;// Когда узел выделен (Open)
    
            // Сканируем логические диски компьютера
            DriveInfo[] drives = DriveInfo.GetDrives(); // Статический метод
    
            // Строим дерево просмотра логических дисков и каталогов
            TreeNode selectedNode = null;
            foreach (DriveInfo drive in drives)
            {
                // Создаем очередной корневой узел для диска
                TreeNode nodeDrive = new TreeNode(drive.RootDirectory.Name);
                if (drive.RootDirectory.Name == @"C:\")
                    selectedNode = nodeDrive;
    
                // Назначаем узлу пиктограмму в зависимости от типа диска
                // Одинаковые изображения для свернутого и развернутого состояний
                if (drive.DriveType == DriveType.Removable)
                    nodeDrive.ImageIndex = nodeDrive.SelectedImageIndex = 2;
                else if (drive.DriveType == DriveType.CDRom)
                    nodeDrive.ImageIndex = nodeDrive.SelectedImageIndex = 3;
                else
                    nodeDrive.ImageIndex = nodeDrive.SelectedImageIndex = 4;
    
                // Добавляем узел диска в унаследованный TreeView
                this.Nodes.Add(nodeDrive);
                // Добавляем к корневому узлу подкаталоги ближайшего слоя
                // с помощью нашей теперь уже нерекурсивной функции
                AddDirectories(nodeDrive);
            }
    
            if (selectedNode != null)
                this.SelectedNode = selectedNode;
        }
    
        // Нерекурсивная функция добавления узлов подкаталогов
        void AddDirectories(TreeNode node)
        {
            node.Nodes.Clear(); // Очищаем коллекцию узлов на случай повторного
            // раскрытия уже ранее раскрытого и сформированного узла
    
            // Формируем полный путь для текущего каталога (узла)
            DirectoryInfo dirInfo = new DirectoryInfo(node.FullPath);
            // Объявляем ссылку на массив подкаталогов
            DirectoryInfo[] arrayDirInfo;
    
            // Если есть подкаталоги - безопасный код
            try
            {
                arrayDirInfo = dirInfo.GetDirectories();
            }
            catch
            {
                // Подкаталогов нет - выходим
                return;
            }
    
            // Добавляем к текущему узлу дочерний узел для каждого каталога
            foreach (DirectoryInfo dir in arrayDirInfo)
                node.Nodes.Add(new TreeNode(dir.Name));
        }
    
        // Переопределяем обработчик базового класса TreeView,
        // который срабатывает перед раскрытием пользователем
        // текущего узла, формируя ближайший слой дочерних узлов налету
        protected override void OnBeforeExpand(TreeViewCancelEventArgs args)
        {
            base.OnBeforeExpand(args);
    
            this.BeginUpdate(); // Замораживаем перерисовку дерева
    
            // Быстренько создаем ближайший слой подузлов для каждого
            // подузла раскрываемого узла, чтобы отображались
            // значки + на узлах слоя после раскрытия текущего
            foreach (TreeNode node in args.Node.Nodes)
                AddDirectories(node);
    
            this.EndUpdate();   // Освобождаем перерисовку дерева
        }
    }
}
Листинг 15.10 . Файл DirectoryTreeView.cs

using System;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
    
using System.Diagnostics;// Для класса Process
    
namespace Test
{
    // Расширение библиотечного класса ListView
    // для отображения файлов текущего каталога
    class DirectoryListView : ListView
    {
        public DirectoryListView()  // Конструктор
        {
        // Добавляем столбцы к ListView с нужной атрибутикой
        this.Columns.Add("Имя", 150, HorizontalAlignment.Left);
        this.Columns.Add("Размер", 100, HorizontalAlignment.Right);
        this.Columns.Add("Тип", 100, HorizontalAlignment.Left);
        this.Columns.Add("Изменен", 150, HorizontalAlignment.Left);
    
        // Заготавливаем две коллекции для значков файлов
        this.SmallImageList = new ImageList();
        this.LargeImageList = new ImageList();
        this.SmallImageList.ImageSize = new Size(16, 16);// По умолчанию
        this.LargeImageList.ImageSize = new Size(32, 32);
        }
    
        // Базовое поле для свойства
        string strDirectory;
    
        public string Directory // Свойство
        {
            // Аксессоры
            get { return strDirectory; }
            set
            {
            // Принимаем текущий каталог
            strDirectory = value;
    
            // Очищаем коллекции строк и значков
            this.Items.Clear();
            this.SmallImageList.Images.Clear();
            this.LargeImageList.Images.Clear();
    
            // Загружаем иконку каталога
            Icon icon = new Icon(this.GetType(), 
		"Resource.Folder.ico");
            this.SmallImageList.Images.Add(icon);// Индекс значка 0
            this.LargeImageList.Images.Add(icon);// Индекс значка 0
    
            // Создаем объект DirectoryInfo для текущего каталога
            DirectoryInfo dirInfo = new DirectoryInfo(strDirectory);
    
            // Формируем информацию о подкаталогах
            foreach (DirectoryInfo dir in dirInfo.GetDirectories())
              {
              // Создаем объект строки таблицы и первую ячейку "Имя"
              ListViewItem item = new ListViewItem(dir.Name);
              // Позиционируем строку на добавленный значок
              item.ImageIndex = 0;
              item.Tag = "dir";   // Пометили
              item.SubItems.Add("");// Вторая ячейка "Размер"
              item.SubItems.Add("Папка");// Третья ячейка "Тип"
              // Четвертая ячейка "Изменен"
              item.SubItems.Add(dir.LastAccessTime.ToString());
    
              Items.Add(item);// Добавили в коллекцию
              }
    
            int imageIndex = 1; // Счетчик индексов значков
    
            // Формируем информацию о файлах
            foreach (FileInfo file in dirInfo.GetFiles())
              {
              // Создаем объект строки таблицы и первую ячейку "Имя"
              ListViewItem item = new ListViewItem(file.Name);
              // Комбинируем полный путь к файлу
              string path = Path.Combine(file.DirectoryName, file.Name);
              // Получаем значек, ассоциированный с типом файла
              icon = Icon.ExtractAssociatedIcon(path);
              // Добавляем значек в коллекцию иконок элемента ListView
              this.SmallImageList.Images.Add(icon);// Индекс значка imageIndex
              this.LargeImageList.Images.Add(icon);// Индекс значка imageIndex
              // Позиционируем строку на добавленный значок
              item.ImageIndex = imageIndex++;
    
              // Вторая ячейка "Размер" с форматом
              string str = String.Format("{0},{1} КБ",
                        file.Length / 1000, file.Length % 1000);
              item.SubItems.Add(str);
    
              // Третья ячейка "Тип"
              string extension = Path.GetExtension(path).ToUpper() == 
			".EXE" ?
                        "Программа" : "Документ";
                    item.SubItems.Add(extension);
    
                    // Четвертая ячейка "Изменен"
                    item.SubItems.Add(file.LastWriteTime.ToString());
    
                    Items.Add(item);// Добавили в коллекцию
                }
            }
        }
    
        // Переопределяем обработчик класса Control нажатия кнопки мыши
        protected override void OnMouseDown(MouseEventArgs e)
        {
        base.OnMouseDown(e);
    
        // Циклически меняем свойство View на одно из 5 значений перечисления
        // Для смены представления задействуем правую кнопку мыши
        if (e.Button == MouseButtons.Right)
          this.View = (View)(((int)this.View + 1) % 5);// Деление по модулю 5
        }
    
        // Переопределяем обработчик класса ListView активации его элемента
        // с помощью двойного щелчка левой кнопки мыши или клавиши Enter
        protected override void OnItemActivate(EventArgs e)
        {
        base.OnItemActivate(e);
    
        if ((string)SelectedItems[0].Tag == 
		"dir")// Используем введенную ранее метку
            {
            // При щелчке на папке входим в нее
            this.Directory = Path.Combine(this.Directory, SelectedItems[0].Text);
            }
            else
            {
                // Иначе безопасно запускаем программу, 
                // ассоциированную с выделенным файлом или файлами
                foreach (ListViewItem item in this.SelectedItems)
                {
                    try
                    {
                        Process.Start(Path.Combine(this.Directory, item.Text));
                    }
                    catch { } // Ничего не делаем
                }
            }
        }
    }
}
Листинг 15.11 . Файл DirectoryListView.cs

using System;
using System.Drawing;
using System.Windows.Forms;
    
namespace Test
{
    // Класс приложения
    class MyExplorer : Form
    {
        // Объявляем ссылки-поля
        DirectoryTreeView tree;
        DirectoryListView list;
    
        public MyExplorer()    // Конструктор
        {
            // Настраиваем форму
            this.Text = "Наш элементарный Проводник";
            this.StartPosition = FormStartPosition.CenterScreen;
            this.Width *= 2;    // Увеличили ширину формы
            // Внедряем в сборку ресурс иконки формы
            this.Icon = new Icon(GetType(), "Resource.1.ICO");
    
            // Создаем экземпляр класса DirectoryTreeView
            tree = new DirectoryTreeView();
            // Привязываем к форме
            tree.Parent = this;
            // Приклеиваем слева
            tree.Dock = DockStyle.Left;
            // Регистрируем обработчик выделения узла
            tree.AfterSelect += new TreeViewEventHandler(tree_AfterSelect);
    
            // Создаем экземпляр класса DirectoryListView
            list = new DirectoryListView();
            // Привязываем к форме
            list.Parent = this;
            // Приклеиваем справа
            list.Dock = DockStyle.Right;
            // Устанавливаем начальный режим "Таблица"
            list.View = View.Details;
    
            // Предусматриваем поддержку изменения размеров формы
            WidthControl();
            this.Resize += new EventHandler(ElementaryExplorer_Resize);
        }
    
        // Делим форму между элементами
        void WidthControl()
        {
            int width = this.ClientSize.Width;
            tree.Width = width / 4;
            list.Width = width - tree.Width;
        }
    
        // При щелчке на каталоге показываем содержимое в list
        void tree_AfterSelect(object sender, TreeViewEventArgs e)
        {
            list.Directory = e.Node.FullPath;
        }
    
        void ElementaryExplorer_Resize(object sender, System.EventArgs e)
        {
            // Делим форму между элементами
            WidthControl();
        }
    }
}
Листинг 15.12 . Файл MyExplorer.cs

using System.Windows.Forms;
        
namespace Test
{
    // Запуск
    class Program
    {
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.Run(new MyExplorer());
        }
    }
}
Листинг 15.13 . Файл Program.cs

Внешнее представление программы будет таким


Итак, мы видим, что спроектированный нами MyExplorer имеет такую же (ну, почти такую!) функциональность, что и стандартный Проводник Windows.

Максим Филатов
Максим Филатов

Прошел курс. Получил код Dreamspark. Ввожу код на сайте, пишет:

Срок действия этого кода проверки уже истек. Проверьте, правильно ли введен код. У вас осталось две попытки. Вы также можете выбрать другой способ проверки или предоставить соответствующие документы, подтверждающие ваш академический статус.

 

Как активировать код?

Денис Пашков
Денис Пашков
Россия
Татьяна Ковалюк
Татьяна Ковалюк
Украина, Киев, Киевский политехнический институт, 1974