Если установить панель Object Browser на просмотр библиотеки .NET Framework и найти абстрактный класс System.Windows.Forms.ButtonBase, то можно увидеть, что он производит три кнопочных элемента управления:

Все эти три стандартных элемента управления имеют индивидуальное визуальное представление и функциональность (поведение), но каждый из них способен реагировать на пользовательский щелчок генерацией события Click. Элемент Button обычно используется для инициирования действий, CheckBox применяется для установки условий (опций), а RadioButton работает в группе с такими же элементами для выбора пользователем одного из нескольких взаимоисключающих вариантов.
Кнопочные элементы управления наследуют, в частности, от класса Control свойство Text, задающее надпись, а от ButtonBase свойство Image, позволяющее установить отображаемое фоновый рисунок - растр или метафайл. Изображение на кнопках можно задавать и из списка ImageList, если создать этот список и присоединить его к одноименному свойству кнопочного элемента, выбрав из списка нужный рисунок установкой свойств ImageIndex или ImageKey в кнопочном элементе. Такой механизм позволяет динамически менять изображения на множестве кнопочных элементов, подключая из фиксированного арсенала изображений нужные в зависимости от возникающей ситуации.
Если кнопочный элемент управления должен одновременно показывать и текст и рисунок, то наследуемое от ButtonBase свойство TextImageRelation управляет их последовательностью вывода. Ему можно присвоить одно из значений одноименного перечисления TextImageRelation из того же пространства имен System.Windows.Forms:
Расположение текста и рисунка относительно клиентской области кнопочного элемента устанавливается в свойствах TextAlign и ImageAlign одним из значений перечисления System.Drawing.ContentAlignment
Мы рассмотрели некоторые общие свойства, передаваемые кнопочным элементам их общим предком - абстрактным классом ButtonBase. Абстрактный класс не может производить объекты, а может использоваться только в цепочке наследования как базовый. Теперь посмотрим на некоторые члены, которые добавляют сами кнопочные элементы.
Свойство DialogResult устанавливает статус кнопки для диалоговых окон, принимая одно из значений перечисления System.Windows.Forms.DialogResult:
По умолчанию установлено значение None.
Свойство Appearance (вид) может принимать значения одноименного перечисления System.Windows.Forms.Appearance:
Свойство CheckAlign ожидает значения из перечисления System.Drawing.ContentAlignment, которое мы уже рассматривали, и устанавливает взаимное расположение маркера флажка и надписи Text. По умолчанию установлено MiddleLeft - маркер флажка и текст выровнены по горизонтали и маркер находится слева от текста.
Булево свойство Checked отслеживает текущее состояние флажка от действий пользователя или устанавливает его программно, а при изменении состояния извне или программно генерируется событие CheckedChanged.
Булево свойство AutoCheck управляет способностью флажка изменять свое состояние при щелчке пользователя. При установке этого свойства в false управлять состоянием можно программно в обработчике события CheckedChanged, переключая состояние булевым свойством Checked.
Иногда может понадобиться использование трех состояний CheckBox. В этом случае нужно установить свойство ThreeState в состояние true и пользоваться уже не свойством Checked и событием CheckedChanged, а событием CheckStateChanged и свойством CheckState. Свойство CheckState ожидает значения перечисления System.Windows.Forms.CheckState:
Этот элемент управления традиционно применяется для работы в составе группы таких же элементов с целью дать пользователю возможность выбора только одного из взаимоисключающих вариантов. Он также, как и флажок, имеет свойство Appearance, позволяющее изменить его визуальное представление, и свойство CheckAlign для изменения взаимного положения маркера и текста. Как и у флажка, у радиокнопки есть булево свойство Checked, отслеживающее или устанавливающее текущее состояние, при изменении которого генерируется событие CheckedChanged.
Если радиокнопка включена, то щелчок пользователя на ней ее состояние не изменит, но сработает событие Click, наследуемое от класса Control. Если кнопка выключена и свойство AutoCheck установлено в true (по умолчанию), то щелчок пользователя на ней переключит ее состояние на включено, а все другие переключатели группы выключаться. Обычно родителем для группы радиокнопок используют элемент управления GroupBox, хотя родителем может выступать Form, Panel и любой другой элемент управления, имеющий коллекцию Controls, наследуемую от класса Control и порожденную классом Control.ControlCollection.
Приведем программу, в которой управление цветом фона клиентской области формы меняется в зависимости от выбранного переключателя.
// Файл Program.cs
namespace Test
{
// Инструкции могут располагаться и внутри пространства имен
using System;
using System.Drawing;
using System.Windows.Forms;
// Класс приложения
class MyClass : Form
{
public MyClass()
{
// Текст заголовка окна
this.Text = "Работа радиокнопок";
// Локальный массив стандартных цветов
const int ROWS = 7, COLS = 3;
Object[,] colors = new Object[ROWS, COLS]
{
{Color.Red, "Красный", "Каждый" },
{Color.Orange, "Оранжевый", "Охотник"},
{Color.Yellow, "Желтый", "Желает" },
{Color.Green, "Зеленый", "Знать" },
{Color.Blue, "Голубой", "Где" },
{Color.DarkBlue,"Синий", "Сидит" },
{Color.Violet, "Фиолетовый", "Фазан" }
};
// Порождаем группу радиокнопок и отдаем их форме
// Используем размер шрифта для величины отступа
int y = Font.Height;
int x = y; // Отступ от левого края
// клиентской области родителя (формы)
for (int i = 0; i < ROWS; i++)
{
RadioButton radio = new RadioButton();// Создали
radio.Parent = this; // Отдали родителю (форме)
// Настраиваем, пока ссылаемся на отданный объект
radio.Location = new Point(x, y); // Позиционировали
// Формируем надпись
String str = String.Format("{0, -11}",
((Color)colors[i, 0]).Name);
str += String.Format("-{0, -9}",
(String)colors[i, 1]);
str += String.Format(" ({0})",
(String)colors[i, 2]);
radio.Text = str;
radio.AutoSize = true; // Подстраивать размер под текст
radio.Tag = colors[i, 0]; // Сохраняем локальную ссылку
// на цвет для передачи обработчику
y += radio.Height; // Координата для следующей радиокнопки
// Подписываем переключатель на событие CheckedChanged
// Для всех радиокнопок будет один обработчик
radio.CheckedChanged += RadioButtonOnCheckedChanged;
radio.Font = new Font("Arial", 10);// Назначили свой шрифт
}
}
void RadioButtonOnCheckedChanged(object sender, EventArgs e)
{
if (sender is RadioButton)
{
// Приводим типы, чтобы добыть свойство Tag
RadioButton radio = (RadioButton)sender;
this.BackColor = (Color)radio.Tag;
}
}
}
// Запуск
class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyClass());
}
}
}
Некоторые элементы управления, которые наследуют класс ScrollableControl, имеют встроенные полосы прокрутки. В одних полосы прокрутки появляются автоматически в случае, когда содержимое элемента выходит за рамки отображаемой области. В других полосы прокрутки включаются в режим постоянного отображения установкой свойства AutoScroll=true.
Полосы прокрутки можно задавать элементам управления и вручную, присоединяя к ним экземпляры классов HScrollBar и VScrollBar, производных от абстрактного класса ScrollBar. Этот абстрактный класс содержит все необходимое для горизонтальной и вертикальной полос прокрутки.
Визуальное представление полосы прокрутки соответствует ее внутреннему состоянию, определяемому некоторой последовательностью целых чисел и позиционированием внутри этой последовательности. Основные характеристики этого ряда целых чисел задаются свойствами

Обычно LargeChange приравнивают к соответствующему размеру отображаемой области документа. Если размер клиентской области меняется в свободно плавающем окне, то величину LargeChange пересчитывают так, чтобы она соответствовала новому размеру видимой области документа. При прокрутке документа операционная система копирует содержимое экрана из одного положения клиентской области в другое, используя при этом упакованные в классы ScrollableControl и ScrollBar системные функции Win32 API: ScrollWindow, ScrollWindowEx, ScrollDC. Каждый раз для прорисовки нового состояния клиентской области при прокрутке автоматически вызывается метод OnPaint().
В классе ScrollBar имеются всего два события: ValueChanged и Scroll. Событие ValueChanged генерируется при изменении значения Value либо программно, либо действиями пользователя. Однако, если значение Value достигло границы числового диапазона, генерация события ValueChanged прекращается. Ниже приведен пример, в котором иллюстрируется обработка события ValueChanged
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
// Класс приложения
class MyClass : Form
{
// Создаем вертикальную полосу прокрутки, которую
// потом настроим и будем отслеживать
VScrollBar vScroll = new VScrollBar();
public MyClass() // Конструктор
{
// Текст заголовка окна
this.Text = "Полоса прокрутки";
// Привязываем к родителю-форме
vScroll.Parent = this;
// Устанавливаем параметры привязки полосы прокрутки
vScroll.Height = this.ClientSize.Height;
vScroll.Location = new Point(this.ClientSize.Width -
vScroll.Width, 0);
// Регистрируем событие
vScroll.ValueChanged += new EventHandler(vScroll_ValueChanged);
}
// На случай изменения размеров формы
protected override void OnSizeChanged(EventArgs e)
{
// Пересчитываем параметры привязки полосы прокрутки
vScroll.Height = this.ClientSize.Height;
vScroll.Location = new Point(this.ClientSize.Width -
vScroll.Width, 0);
// Инициируем перерисовку
this.Invalidate();
}
// Отслеживает действия с полосой прокрутки
void vScroll_ValueChanged(object sender, EventArgs e)
{
// Инициируем перерисовку
this.Invalidate();
}
// Перегружаем метод OnPaint(), унаследованный от класса Control
protected override void OnPaint(PaintEventArgs e)
{
// Создаем и настраиваем точку привязки текстового блока
StringFormat strfrm = new StringFormat();
strfrm.Alignment = StringAlignment.Center;// Центр по горизонтали
strfrm.LineAlignment = StringAlignment.Near;
// Точка привязки на форме
PointF point = new PointF(this.ClientSize.Width / 2,
vScroll.Value * this.ClientSize.Height / vScroll.Maximum);
// Рисуем текст
Graphics gr = e.Graphics;// Контекст экрана
gr.DrawString("Value=" +
vScroll.Value.ToString(), // Выводимый текст
new Font("Arial", 14, FontStyle.Bold),
// Устанавливаем шрифт
new SolidBrush(Color.Red), // Цвет шрифта
point, // Точка привязки на форме
strfrm); // Передаем точку привязки текстового блока
}
}
// Запуск
class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyClass());
}
}
}
Событие Scroll генерируется в любом случае взаимодействия пользователя с полосой прокрутки, даже если тот пытается выйти за пределы диапазона. Это событие несет в себе намного больше информации о состоянии полосы прокрутки и действиях пользователя. Если мы подпишемся на это событие, то в обработчик будет передаваться объект ScrollEventArgs с информацией, которая находится в свойствах:
| - SmallDecrement = 0 | Мышь: | пользователь щелкнул на верхней (левой) кнопке со стрелкой |
| Клавиатура: | пользователь нажал "стрелку вверх" ("стрелку влево") | |
| - SmallIncrement = 1 | Мышь: | пользователь щелкнул на нижней (правой) кнопке со стрелкой |
| Клавиатура: | пользователь нажал "стрелку вниз " ("стрелку вправо") | |
| - LargeDecrement = 2 | Мышь: | пользователь щелкнул на верхней (левой) области между бегунком и кнопкой со стрелкой |
| Клавиатура: | пользователь нажал клавишу PageUp | |
| - LargeIncrement = 3 | Мышь: | пользователь щелкнул на нижней (правой) области между бегунком и кнопкой со стрелкой |
| Клавиатура: | пользователь нажал клавишу PageDown | |
| - ThumbPosition = 4 | Мышь: | пользователь отпустил бегунок |
| - ThumbTrack = 5 | Мышь: | пользователь захватил бегунок (или перемещает его) |
| - First = 6 | Клавиатура: | пользователь нажал клавишу Home |
| - Last = 7 | Клавиатура: | пользователь нажал клавишу End |
| - EndScroll = 8 | Прокрутка завершена | |
Если операционная система сильно загружена и скролируется большой объем информации, то часть событий полосы прокрутки может игнорироваться, но обязательно будет перехвачено последнее событие Scroll с признаком Type=EndScroll и значение Value станет равным NewValue.
Ниже приведен код программы с заблокированной полосой прокрутки ("Хоть видит око, но зуб не ймет" - Крылов, басня)
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
// Класс приложения
class MyClass : Form
{
// Создаем вертикальную полосу прокрутки, которую
// потом настроим и будем отслеживать
VScrollBar vScroll = new VScrollBar();
public MyClass() // Конструктор
{
// Текст заголовка окна
this.Text = "Заблокированная прокрутка";
// Привязываем к родителю-форме
vScroll.Parent = this;
// Устанавливаем параметры привязки полосы прокрутки
vScroll.Height = this.ClientSize.Height;
vScroll.Location = new Point(this.ClientSize.Width - vScroll.Width, 0);
// Регистрируем событие Scroll
vScroll.Scroll += new ScrollEventHandler(vScroll_Scroll);
}
void vScroll_Scroll(object sender, ScrollEventArgs e)
{
// Запрещаем изменение Value
e.NewValue = e.OldValue;
}
// На случай изменения размеров формы
protected override void OnSizeChanged(EventArgs e)
{
// Пересчитываем параметры привязки полосы прокрутки
vScroll.Height = this.ClientSize.Height;
vScroll.Location = new Point(this.ClientSize.Width -
vScroll.Width, 0);
}
}
// Запуск
class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyClass());
}
}
}
Попробуйте запустить эту программу - полоса прокрутки не реагирует на действия пользователя.
Элемент TrackBar, реализуемый одноименным классом, по своей функциональности похож на класс ScrollBar. Часть свойств и событий этих двух классов совпадает как по названию, так и назначению. Некоторые из них перечислены в таблице. Причем не все свойства элемента TrackBar доступны в режиме проектирования, зато все доступны в программном режиме на этапе выполнения.
| Свойство | Пояснения |
|---|---|
| Value | Главная регулируемая величина, определяющая текущее состояние головки бегунка. По умолчанию начальное значение равно значению свойства Minimum |
| SmallChange | Минимальная дискретность изменения величины Value при управлении стрелками или перемещением головки бегунка с захватом мышью. По умолчанию установлено 1. |
| LargeChange | Максимальная дискретность изменения величины Value при нажатии клавиш PageUp или PageDown, или щелчках мышью на шкале элемента. По умолчанию установлено 5. |
| Minimum | Нижний предел значения Value. По умолчанию 0. |
| Maximum | Верхний предел значения Value. По умолчанию 10. |
| TickFrequency | Свойство целого типа, определяющее частоту делений на шкале ползунка. Расчитывается с использованием значений свойств Minimum и Maximum |
| TickStyle | Определяет форму ползунка при отображении пользователю. По умолчанию установлено значение BottomRight, то есть при горизонтальном ползунке метки с делениями располагаются снизу от него, а при вертикальном - справа |
| Orientation | Horizontal - горизонтальный ползунок, Vertical - вертикальный ползунок |
Вот простая программа, иллюстрирующая работу ползунка TrackBar
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
// Класс приложения
class MyClass : Form
{
// Создаем ползунок
TrackBar track = new TrackBar();
public MyClass() // Конструктор
{
// Текст заголовка окна
this.Text = "Ползунок TrackBar";
// Отключим изменение размеров окна, чтобы
// не пересчитывать расположение элементов
this.MaximizeBox = false;
//this.FormBorderStyle = FormBorderStyle.FixedSingle;
this.FormBorderStyle = FormBorderStyle.Fixed3D;// Красивше!
// В центре экрана
this.StartPosition = FormStartPosition.CenterScreen;
// Привязываем к форме
track.Parent = this;
// Настраиваем ползунок
track.Location = new Point(8, 104);
track.Size = new Size(272, 53);
track.Minimum = 0;
track.Maximum = 50;
track.Orientation = Orientation.Horizontal;
track.LargeChange = 5;
track.SmallChange = 1;
track.TickFrequency = 5;
track.TickStyle = TickStyle.BottomRight;
// Установим начальную позицию
track.Value = (track.Maximum - track.Minimum) / 2;
// Регистрируем событие изменения Value
track.ValueChanged += new EventHandler(track_ValueChanged);
}
// Отслеживает действия с ползунком
void track_ValueChanged(object sender, EventArgs e)
{
// Инициируем перерисовку
this.Invalidate();
}
// Перегружаем метод OnPaint(), унаследованный от класса Control
protected override void OnPaint(PaintEventArgs e)
{
// Точка привязки текстового блока
StringFormat strfrm = new StringFormat();
strfrm.Alignment = StringAlignment.Center;// Центр по горизонтали
strfrm.LineAlignment = StringAlignment.Center; // Центр по вертикали
// Точка привязки на форме
PointF point = new PointF(this.ClientSize.Width / 2,
this.ClientSize.Height / 3);// Граница верхней трети
// Рисуем текст
Graphics gr = e.Graphics;// Контекст экрана
gr.DrawString("Value=" +
track.Value.ToString(), // Выводимый текст
new Font("Arial", 14, FontStyle.Bold), // Устанавливаем шрифт
new SolidBrush(Color.Red), // Цвет шрифта
point, // Точка привязки на форме
strfrm); // Передаем точку привязки текстового блока
}
}
// Запуск
class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyClass());
}
}
}
А вот снимок экрана для этой программы
В библиотеке .NET Framework 2.0 в пространстве имен { } System.Windows.Forms имеется абстрактный класс TextBoxBase, который обеспечивает производные от него классы множеством возможностей по редактированию текста. Цепочка наследования этих классов выглядит так

Из рисунка видно, что существуют 5 классов, способных порождать текстовые элементы управления:
MaskedTextBoxRichTextBoxTextBoxDataGridTextBoxDataGridViewTextBoxEditingControlМы рассмотрим первые три класса как наиболее простые. Каждый из них имеет свойство Text типа string, в котором хранится редактируемая в элементе информация. Большинство других свойств принимают булево значение и определяют режим работы элемента.
Этот элемент удобно применить в том случае, когда нужно контролировать пользовательский ввод. Приведем пример с вводом телефонного номера. Вот какое поле ввода можно было бы предоставить пользователю
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
// Класс приложения
class MyClass : Form
{
// Объявляем ссылку на элемент поля ввода телефона
MaskedTextBox phone;
public MyClass() // Конструктор
{
// Код из конструктора переносим в обработчик
this.Load += new EventHandler(MyClass_Load);
}
void MyClass_Load(object sender, EventArgs e)
{
// Текст заголовка окна
this.Text = "Элемент MaskedTextBox";
// Отключаем размеры
this.MaximizeBox = false;
this.FormBorderStyle = FormBorderStyle.FixedSingle;
// Создаем элемент маскированного поля ввода телефона
phone = new MaskedTextBox();
phone.Parent = this;// Привязываем к форме
// Настраиваем маскированное поле ввода
int x = (this.ClientSize.Width - phone.Width) / 2;
int y = this.ClientSize.Height / 3 - phone.Height;
phone.Location = new Point(x, y);
phone.Mask = "00-00-00";
Font font = new Font("Arial", 12, FontStyle.Bold);
phone.Font = font;
phone.ForeColor = Color.Blue;// Синий
// Регистрируем событие, когда символ не может быть принят полем
phone.MaskInputRejected += phone_MaskInputRejected;
// Создаем и настраиваем текстовую метку
Label label = new Label();
label.Text = "Введите номер телефона";
label.Parent = this;
label.Width = this.ClientSize.Width;
label.Font = font;
label.ForeColor = Color.FromArgb(255, 0, 0);// Красный
// Позиционируем метку над полем ввода
label.Location = new Point(10, phone.Location.Y -
label.Height - 10);
}
ToolTip toolTip = new ToolTip();// Содаем объект всплывающей подсказки
void phone_MaskInputRejected(object sender, MaskInputRejectedEventArgs e)
{
if (phone.MaskFull)
{
toolTip.ToolTipTitle = "Ввод отклонен -
поле заполнено";
// Позиционируем всплывающую подсказку относительно поля ввода
toolTip.Show("Вы пытаетесь ввести" +
"\n" + "слишком длинный номер",
phone, // Привязка к элементу
-phone.Location.X,// Сдвинули в начало окна
phone.Height, // Сдвинули к низу поля ввода
3000);// Подсказка ждет 3 секунды
}
else if (e.Position >= phone.Mask.Length)
{
toolTip.ToolTipTitle = "Ввод отклонен -
выход за границу";
// Позиционируем всплывающую подсказку относительно поля ввода
toolTip.Show("Не могу добавить новый
символ\nза границей поля ввода",
phone, // Привязка к элементу
-phone.Location.X,// Сдвинули в начало окна
phone.Height, // Сдвинули к низу поля ввода
3000);// Подсказка ждет 3 секунды
}
else
{
toolTip.ToolTipTitle = "Ввод отклонен -
нужны цифры";
// Позиционируем всплывающую подсказку относительно поля ввода
toolTip.Show(
"Номер телефона должен состоять
только из цифр (0-9)",
phone, // Привязка к элементу
-phone.Location.X,// Сдвинули в начало окна
phone.Height, // Сдвинули к низу поля ввода
3000);// Подсказка ждет 3 секунды
}
}
}
// Запуск
class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyClass());
}
}
}
Результат выполнения выглядит так
![]() |
Граждане студенты! Это мой телефон, но не вздумайте звонить. |
Элементы строкового свойства Mask могут быть такими
| Маскирующий элемент | Пояснения |
|---|---|
| 0 | Ожидает любую цифру от 0 до 9 |
| 9 | Ожидается цифра или пробел |
| # | Ожидается цифра, пробел, знаки + или - |
| L | Ожидаются ASCII -символы от a до z или от A до Z |
| ? | Ожидаются любые ASCII -символы |
Ожидается любой символ. Если свойство AsciiOnly равно true, то работает как элемент "L" |
|
| C | Ожидается любой неуправляющий символ. Если свойство AsciiOnly равно true, то работает как элемент "?" |
| A | Ожидает любые алфавитно-цифровые символы. Если AsciiOnly равно true, то работает как элемент "L" |
И так далее по MSDN … |
|
Пример программы
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
// Класс приложения
class MyClass : Form
{
// Объявления полей
public MyClass() // Конструктор
{
this.Text = "Как быстро летит время!";
this.FormBorderStyle = FormBorderStyle.FixedSingle;
// Замыкаем размеры
this.MaximizeBox = false;
this.StartPosition = FormStartPosition.CenterScreen;
// Адресовали один объект
Label lbl = new Label(); // Первая надпись
lbl.Parent = this; // Отдали форме
lbl.Text = "Начальная дата: ";
lbl.AutoSize = true;
lbl.Location = new Point(Font.Height, Font.Height);
int xDateTimePicker = lbl.Right;
// Этой же ссылкой адресовали другой объект
lbl = new Label(); // Вторая надпись
lbl.Parent = this; // Отдали форме
lbl.Text = "Целевая дата: ";
lbl.AutoSize = true;
// Позиционируем ниже
lbl.Location = new Point(Font.Height, Font.Height * 3);
// Позиция правее правого
xDateTimePicker = Math.Max(xDateTimePicker, lbl.Right);
// Этой же ссылкой адресовали еще один объект
lbl = new Label();
lbl.Parent = this;
lbl.Text = "Прошло времени:";
lbl.AutoSize = true;
// Позиционируем еще ниже
lbl.Location = new Point(Font.Height, Font.Height * 5);
// Позиция правее правого
xDateTimePicker = Math.Max(xDateTimePicker, lbl.Right);
// Первое поле даты
dtpBegin = new DateTimePicker();
dtpBegin.Parent = this;
dtpBegin.Location = new Point(xDateTimePicker, Font.Height);
// Второе поле даты
dtpEnd = new DateTimePicker();
dtpEnd.Parent = this;
dtpEnd.Location = new Point(xDateTimePicker, Font.Height * 3);
// Регистрируем общий обработчик
dtpBegin.ValueChanged += DateTimePickerValueChanged;
dtpEnd.ValueChanged += DateTimePickerValueChanged;
lblResult1 = new Label();
lblResult1.Parent = this;
lblResult1.AutoSize = true;
// X пересчитываем в обработчике
lblResult1.Location = new Point(0, Font.Height * 7);
// Формируем фонт для меток результата
Font fnt = new Font("Arial", 12, FontStyle.Bold);
lblResult1.Font = fnt;
lblResult1.ForeColor = Color.Red;
lblResult2 = new Label();
lblResult2.Parent = this;
lblResult2.AutoSize = true;
// X пересчитываем в обработчике
lblResult2.Location = new Point(0, Font.Height * 9);
lblResult2.Font = fnt;
lblResult2.ForeColor = Color.Blue;
// Изменяем размеры окна
this.ClientSize = new Size(dtpEnd.Right + Font.Height,
Font.Height * 11);
}
void DateTimePickerValueChanged(object sender, EventArgs e)
{
String str;
if (dtpBegin.Value > dtpEnd.Value)
return;
else
{
DateTime dtBegin = dtpBegin.Value;
DateTime dtEnd = dtpEnd.Value;
int iYears = dtEnd.Year - dtBegin.Year;
int iMonths = dtEnd.Month - dtBegin.Month;
int iDays = dtEnd.Day - dtBegin.Day;
if (iDays < 0)
{
iDays += DateTime.DaysInMonth(dtEnd.Year, 1 +
(dtEnd.Month + 10) % 12);
iMonths -= 1;
}
if (iMonths < 0)
{
iMonths += 12;
iYears -= 1;
}
str = String.Format("{0} - лет; {1} -
месяцев; {2} - дней",
iYears, iMonths, iDays);
}
lblResult1.Text = str;
// По горизонтали позиционируем в центр
int x = (this.ClientSize.Width - lblResult1.Width) / 2;
int y = lblResult1.Location.Y;
lblResult1.Location = new Point(x, y);
// Получаем разницу во времени
TimeSpan time = dtpEnd.Value - dtpBegin.Value;
str = String.Format("({0} дней); ({1} часов)",
(long)time.TotalDays,
(long)time.TotalHours);
// По горизонтали позиционируем в центр
lblResult2.Text = str;
x = (this.ClientSize.Width - lblResult2.Width) / 2;
y = lblResult2.Location.Y;
lblResult2.Location = new Point(x, y);
}
}
// Запуск
class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyClass());
}
}
}
Результат выполнения

Если дату задает не пользователь, а программа, то вначале нужно сформировать структуру DateTime, затем ее присвоить свойству Value элемента DateTimePicker. Свойство DateTimePicker. Format, которое ожидает один из членов перечисления DateTimePickerFormat, определяет формат отображения содержимого поля элемента DateTimePicker. Свойства Value типа DateTime можно вычитать друг из друга. Полученная разница типа TimeSpan имеет, в том числе, свойства TotalDays, TotalHours.
Элемент управления TreeView отображает информацию в виде иерархического дерева с узловыми элементами типа TreeNode. Каждый узел TreeNode может быть родителем других узлов того же типа. Непосредственные потомки текущего узла-родителя указаны в свойстве Nodes типа TreeNodeCollection. По умолчанию узлы TreeNode дерева TreeView свернуты и имеют слева значок " + ", щелчок на котором разворачивает узел и появляется значок " - ". Программное управление сворачиванием/разворачиванием узлов выполняется методами Expand(), ExpandAll(), Collapse(), Toggle() объекта TreeNode, а его булевы свойства IsExpanded и IsSelected позволяют выяснить, развернут или выделен соответствующий узел.
Элемент TreeView имеет набор событий, которые реагируют на действия пользователя, например: BeforeSelect, AfterSelect, BeforeExpand, AfterExpand, BeforeCollapse, AfterCollapse. В обработчиках этих события можно организовать нужную логику работы дерева просмотра.
Приведем пример простой программы построения дерева, отображающего дерево каталогов диска C: текущего компьютера.
using System;
using System.Drawing;
using System.Windows.Forms;
// Пространство имен для работы с каталогами
using System.IO;
namespace Test
{
// Класс приложения
class MyClass : Form
{
public MyClass() // Конструктор
{
this.Text = "Простое дерево каталогов";
this.FormBorderStyle = FormBorderStyle.FixedSingle;
// Замыкаем размеры
this.MaximizeBox = false;
this.StartPosition = FormStartPosition.CenterScreen;
// Создаем объект дерева
TreeView tree = new TreeView();
// Привязываем к форме
tree.Parent = this;
// Разварачиваем на всю форму
tree.Dock = DockStyle.Fill;
// Создаем корневой узел
TreeNode nodeDriveC = new TreeNode("C:\\");
// Добавляем корневой узел к дереву просмотра
tree.Nodes.Add(nodeDriveC);
// Развертываем корневой узел
nodeDriveC.Expand();
// Считываем дерево каталогов
AddDirectories(nodeDriveC);
}
// Рекурсивный метод
void AddDirectories(TreeNode node)
{
// Для текущего узла node получаем полный путь к корню дерева
string strPath = node.FullPath;
// Создаем объект текущего каталога
DirectoryInfo dirInfo = new DirectoryInfo(strPath);
// Объявляем ссылку на массив подкаталогов текущего каталога
DirectoryInfo[] arrayDirInfo;
try
{
// Пытаемся получить список подкаталогов
arrayDirInfo = dirInfo.GetDirectories();
}
catch
{
// Подкаталогов нет, выходим из рекурсии
return;
}
// Добавляем прочитанные подкаталоги как узлы в дерево просмотра
foreach (DirectoryInfo dir in arrayDirInfo)
{
// Создаем новый узел с именем подкаталога
TreeNode nodeDir = new TreeNode(dir.Name);
// Добавляем его как дочерний к текущему узлу
node.Nodes.Add(nodeDir);
// Развертываем узел
//nodeDir.Expand();
// Делаем дочерний узел текущим и спускаемся рекурсивно ниже
AddDirectories(nodeDir);
}
}
}
// Запуск
class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyClass());
}
}
}
Если запустить эту программу, то можно заметить, что хоть она и выполняет все правильно, но очень долго (ужасно долго!) сканирует файловую систему диска (непростая работа).
Вот ее внешний вид
Спроектируем более совершенную версию программы для той же задачи построения дерева каталогов. Чтобы все было красиво, нужно извлечь иконки для соответствующих узлов из архива библиотеки VS2005ImageLibrary.zip, поставляемой в составе Visual Studio 2005.

Можно взять упомянутые иконки и из каталога Source данной работы.
Для включения значков в ресурс проекта, чтобы при компиляции они были внедрены в исполнимую сборку exe, нужно сделать следующее:
Можно таким же образом внедрить в сборку иконку самой формы приложения (например, " 1.ICO " в каталоге Source )
Окончательный код усовершенствованной программы просмотра дерева каталогов будет таким
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(); // Освобождаем перерисовку дерева
}
}
// Класс приложения
class MyClass : Form
{
public MyClass() // Конструктор
{
this.Text = "Улучшенное дерево каталогов";
this.FormBorderStyle = FormBorderStyle.FixedSingle;
// Замыкаем размеры
this.MaximizeBox = false;
this.StartPosition = FormStartPosition.CenterScreen;
this.Width += this.Width / 3; // Чуток расширили форму
// Внедряем в сборку ресурс иконки формы
this.Icon = new Icon(GetType(), "Resource.1.ICO");
// Создаем экземпляр пользовательского класса дерева
DirectoryTreeView myTree = new DirectoryTreeView();
// Привязываем к форме
myTree.Parent = this;
// Разварачиваем на всю форму
myTree.Dock = DockStyle.Fill;
}
}
// Запуск
class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyClass());
}
}
}
В старой программе с помощью рекурсивной функции, вызов которой был помещен в конструктор формы, мы вначале сканировали все дерево каталогов компьютера и тратили на это значительное время. Это существенно задерживало появление окна приложения при его запуске. В усовершенствованной программе в дереве просмотра для каждого раскрытого узла хранятся только два ближайших слоя дочерних узлов. При раскрытии пользователем очередного узла вначале срабатывает обработчик, которому передается коллекция дочерних узлов раскрываемого узла. В обработчике для каждого узла готовой раскрыться коллекции на лету создается новый слой дочерних узлов, готовых для раскрытия.
В новой программе мы всю работу разделили на этапы. Вначале создается два слоя - слой устройств и ближайший слой подчиненных им каталогов. При раскрытии узла устройства перед отображением уже вычисленного дочернего слоя вычисляется следующий в глубину слой для каждого дочернего узла, готовый для отображения при далнейшем раскрытии. Таким образом, при очередном шаге в глубину дерева вычисляется только один новый дочерний слой. Для пользователя эта задержка не очень заметна.
Вот внешний вид результата выполнения улучшенной версии программы чтения дерева каталогов
В Проводнике Windows используется именно этот элемент управления для отображения информации текущего каталога. Он может отражать подробную информацию в разных формах, определяемых свойством View типа одноименного перечисления System.Windows.Forms.View, которое содержит значения
При установке View=Details свойство Columns класса ColumnHeaderCollection должно содержать коллекцию объектов типа ColumnHeader, которые определяют заголовок таблицы. Эти объекты можно создавать по отдельности, затем добавлять к коллекции с помощью метода Add() свойства Columns, либа сразу сразу использовать перегрузку этого метода, задавая в нем нужные аргументы. Свойство Text определяет название заголовка столбца.
При установке View=Details свойство Items класса ListViewItemCollection содержит коллекцию объектов типа ListViewItem, которые представляют отдельные строки в таблице. Каждая строка, в свою очередь, является коллекцией SubItems типа ListViewSubItemCollection, состоящей из отдельных объектов типа ListViewSubItem, каждый из которых имеет набор свойств, определяющих атрибуты содержания и формы. Эти дополнительные объекты определяют ячейки таблицы в соответствующей строке. Строки могут маркироваться значками SmallImageList и LargeImageList типа ImageList.
В качестве иллюстрации создадим
Вначале нужно извлечь файл значка Folder.ico из библиотечного архива Visual Studio 2005 (или взять из прилагаемого каталога Source данной работы)

и добавить его к проекту командой Project/Add Existing Item

Не забудьте пометить в свойствах этого файла параметр Build Action как Embedded Resource.
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 / 1024, file.Length % 1024);
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 { } // Ничего не делаем
}
}
}
}
// Класс приложения
class MyClass : Form
{
public MyClass() // Конструктор
{
this.Text = "Отображение содержимого каталога";
this.FormBorderStyle = FormBorderStyle.FixedSingle;
// Замыкаем размеры
this.MaximizeBox = false;
this.StartPosition = FormStartPosition.CenterScreen;
this.Width *= 2; // Увеличили ширину формы
// Внедряем в сборку ресурс иконки формы
this.Icon = new Icon(GetType(), "Resource.1.ICO");
// Создаем экземпляр расширения класса ListView
DirectoryListView myList = new DirectoryListView();
// Привязываем к форме
myList.Parent = this;
// Разварачиваем на всю форму
myList.Dock = DockStyle.Fill;
// Устанавливаем режим "Таблица"
myList.View = View.Details;
// Устанавливаем текущим корневой каталог
myList.Directory = "C:\\";
}
}
// Запуск
class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyClass());
}
}
}
Результат выполнения программы примера будет таким
При выполнении последнего примера видно, что войти в дочерний каталог можно, но вновь вернуться в
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(); // Освобождаем перерисовку дерева
}
}
}
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 { } // Ничего не делаем
}
}
}
}
}
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();
}
}
}
using System.Windows.Forms;
namespace Test
{
// Запуск
class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyExplorer());
}
}
}
Внешнее представление программы будет таким

Итак, мы видим, что спроектированный нами MyExplorer имеет такую же (ну, почти такую!) функциональность, что и стандартный Проводник Windows.
Если установить панель Object Browser на просмотр библиотеки .NET Framework и найти абстрактный класс System.Windows.Forms.ButtonBase, то можно увидеть, что он производит три кнопочных элемента управления:

Все эти три стандартных элемента управления имеют индивидуальное визуальное представление и функциональность (поведение), но каждый из них способен реагировать на пользовательский щелчок генерацией события Click. Элемент Button обычно используется для инициирования действий, CheckBox применяется для установки условий (опций), а RadioButton работает в группе с такими же элементами для выбора пользователем одного из нескольких взаимоисключающих вариантов.
Кнопочные элементы управления наследуют, в частности, от класса Control свойство Text, задающее надпись, а от ButtonBase свойство Image, позволяющее установить отображаемое фоновый рисунок - растр или метафайл. Изображение на кнопках можно задавать и из списка ImageList, если создать этот список и присоединить его к одноименному свойству кнопочного элемента, выбрав из списка нужный рисунок установкой свойств ImageIndex или ImageKey в кнопочном элементе. Такой механизм позволяет динамически менять изображения на множестве кнопочных элементов, подключая из фиксированного арсенала изображений нужные в зависимости от возникающей ситуации.
Если кнопочный элемент управления должен одновременно показывать и текст и рисунок, то наследуемое от ButtonBase свойство TextImageRelation управляет их последовательностью вывода. Ему можно присвоить одно из значений одноименного перечисления TextImageRelation из того же пространства имен System.Windows.Forms:
Расположение текста и рисунка относительно клиентской области кнопочного элемента устанавливается в свойствах TextAlign и ImageAlign одним из значений перечисления System.Drawing.ContentAlignment
Мы рассмотрели некоторые общие свойства, передаваемые кнопочным элементам их общим предком - абстрактным классом ButtonBase. Абстрактный класс не может производить объекты, а может использоваться только в цепочке наследования как базовый. Теперь посмотрим на некоторые члены, которые добавляют сами кнопочные элементы.
Свойство DialogResult устанавливает статус кнопки для диалоговых окон, принимая одно из значений перечисления System.Windows.Forms.DialogResult:
По умолчанию установлено значение None.
Свойство Appearance (вид) может принимать значения одноименного перечисления System.Windows.Forms.Appearance:
Свойство CheckAlign ожидает значения из перечисления System.Drawing.ContentAlignment, которое мы уже рассматривали, и устанавливает взаимное расположение маркера флажка и надписи Text. По умолчанию установлено MiddleLeft - маркер флажка и текст выровнены по горизонтали и маркер находится слева от текста.
Булево свойство Checked отслеживает текущее состояние флажка от действий пользователя или устанавливает его программно, а при изменении состояния извне или программно генерируется событие CheckedChanged.
Булево свойство AutoCheck управляет способностью флажка изменять свое состояние при щелчке пользователя. При установке этого свойства в false управлять состоянием можно программно в обработчике события CheckedChanged, переключая состояние булевым свойством Checked.
Иногда может понадобиться использование трех состояний CheckBox. В этом случае нужно установить свойство ThreeState в состояние true и пользоваться уже не свойством Checked и событием CheckedChanged, а событием CheckStateChanged и свойством CheckState. Свойство CheckState ожидает значения перечисления System.Windows.Forms.CheckState:
Этот элемент управления традиционно применяется для работы в составе группы таких же элементов с целью дать пользователю возможность выбора только одного из взаимоисключающих вариантов. Он также, как и флажок, имеет свойство Appearance, позволяющее изменить его визуальное представление, и свойство CheckAlign для изменения взаимного положения маркера и текста. Как и у флажка, у радиокнопки есть булево свойство Checked, отслеживающее или устанавливающее текущее состояние, при изменении которого генерируется событие CheckedChanged.
Если радиокнопка включена, то щелчок пользователя на ней ее состояние не изменит, но сработает событие Click, наследуемое от класса Control. Если кнопка выключена и свойство AutoCheck установлено в true (по умолчанию), то щелчок пользователя на ней переключит ее состояние на включено, а все другие переключатели группы выключаться. Обычно родителем для группы радиокнопок используют элемент управления GroupBox, хотя родителем может выступать Form, Panel и любой другой элемент управления, имеющий коллекцию Controls, наследуемую от класса Control и порожденную классом Control.ControlCollection.
Приведем программу, в которой управление цветом фона клиентской области формы меняется в зависимости от выбранного переключателя.
// Файл Program.cs
namespace Test
{
// Инструкции могут располагаться и внутри пространства имен
using System;
using System.Drawing;
using System.Windows.Forms;
// Класс приложения
class MyClass : Form
{
public MyClass()
{
// Текст заголовка окна
this.Text = "Работа радиокнопок";
// Локальный массив стандартных цветов
const int ROWS = 7, COLS = 3;
Object[,] colors = new Object[ROWS, COLS]
{
{Color.Red, "Красный", "Каждый" },
{Color.Orange, "Оранжевый", "Охотник"},
{Color.Yellow, "Желтый", "Желает" },
{Color.Green, "Зеленый", "Знать" },
{Color.Blue, "Голубой", "Где" },
{Color.DarkBlue,"Синий", "Сидит" },
{Color.Violet, "Фиолетовый", "Фазан" }
};
// Порождаем группу радиокнопок и отдаем их форме
// Используем размер шрифта для величины отступа
int y = Font.Height;
int x = y; // Отступ от левого края
// клиентской области родителя (формы)
for (int i = 0; i < ROWS; i++)
{
RadioButton radio = new RadioButton();// Создали
radio.Parent = this; // Отдали родителю (форме)
// Настраиваем, пока ссылаемся на отданный объект
radio.Location = new Point(x, y); // Позиционировали
// Формируем надпись
String str = String.Format("{0, -11}",
((Color)colors[i, 0]).Name);
str += String.Format("-{0, -9}",
(String)colors[i, 1]);
str += String.Format(" ({0})",
(String)colors[i, 2]);
radio.Text = str;
radio.AutoSize = true; // Подстраивать размер под текст
radio.Tag = colors[i, 0]; // Сохраняем локальную ссылку
// на цвет для передачи обработчику
y += radio.Height; // Координата для следующей радиокнопки
// Подписываем переключатель на событие CheckedChanged
// Для всех радиокнопок будет один обработчик
radio.CheckedChanged += RadioButtonOnCheckedChanged;
radio.Font = new Font("Arial", 10);// Назначили свой шрифт
}
}
void RadioButtonOnCheckedChanged(object sender, EventArgs e)
{
if (sender is RadioButton)
{
// Приводим типы, чтобы добыть свойство Tag
RadioButton radio = (RadioButton)sender;
this.BackColor = (Color)radio.Tag;
}
}
}
// Запуск
class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyClass());
}
}
}

Некоторые элементы управления, которые наследуют класс ScrollableControl, имеют встроенные полосы прокрутки. В одних полосы прокрутки появляются автоматически в случае, когда содержимое элемента выходит за рамки отображаемой области. В других полосы прокрутки включаются в режим постоянного отображения установкой свойства AutoScroll=true.
Полосы прокрутки можно задавать элементам управления и вручную, присоединяя к ним экземпляры классов HScrollBar и VScrollBar, производных от абстрактного класса ScrollBar. Этот абстрактный класс содержит все необходимое для горизонтальной и вертикальной полос прокрутки.
Визуальное представление полосы прокрутки соответствует ее внутреннему состоянию, определяемому некоторой последовательностью целых чисел и позиционированием внутри этой последовательности. Основные характеристики этого ряда целых чисел задаются свойствами

Обычно LargeChange приравнивают к соответствующему размеру отображаемой области документа. Если размер клиентской области меняется в свободно плавающем окне, то величину LargeChange пересчитывают так, чтобы она соответствовала новому размеру видимой области документа. При прокрутке документа операционная система копирует содержимое экрана из одного положения клиентской области в другое, используя при этом упакованные в классы ScrollableControl и ScrollBar системные функции Win32 API: ScrollWindow, ScrollWindowEx, ScrollDC. Каждый раз для прорисовки нового состояния клиентской области при прокрутке автоматически вызывается метод OnPaint().
В классе ScrollBar имеются всего два события: ValueChanged и Scroll. Событие ValueChanged генерируется при изменении значения Value либо программно, либо действиями пользователя. Однако, если значение Value достигло границы числового диапазона, генерация события ValueChanged прекращается. Ниже приведен пример, в котором иллюстрируется обработка события ValueChanged
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
// Класс приложения
class MyClass : Form
{
// Создаем вертикальную полосу прокрутки, которую
// потом настроим и будем отслеживать
VScrollBar vScroll = new VScrollBar();
public MyClass() // Конструктор
{
// Текст заголовка окна
this.Text = "Полоса прокрутки";
// Привязываем к родителю-форме
vScroll.Parent = this;
// Устанавливаем параметры привязки полосы прокрутки
vScroll.Height = this.ClientSize.Height;
vScroll.Location = new Point(this.ClientSize.Width -
vScroll.Width, 0);
// Регистрируем событие
vScroll.ValueChanged += new EventHandler(vScroll_ValueChanged);
}
// На случай изменения размеров формы
protected override void OnSizeChanged(EventArgs e)
{
// Пересчитываем параметры привязки полосы прокрутки
vScroll.Height = this.ClientSize.Height;
vScroll.Location = new Point(this.ClientSize.Width -
vScroll.Width, 0);
// Инициируем перерисовку
this.Invalidate();
}
// Отслеживает действия с полосой прокрутки
void vScroll_ValueChanged(object sender, EventArgs e)
{
// Инициируем перерисовку
this.Invalidate();
}
// Перегружаем метод OnPaint(), унаследованный от класса Control
protected override void OnPaint(PaintEventArgs e)
{
// Создаем и настраиваем точку привязки текстового блока
StringFormat strfrm = new StringFormat();
strfrm.Alignment = StringAlignment.Center;// Центр по горизонтали
strfrm.LineAlignment = StringAlignment.Near;
// Точка привязки на форме
PointF point = new PointF(this.ClientSize.Width / 2,
vScroll.Value * this.ClientSize.Height / vScroll.Maximum);
// Рисуем текст
Graphics gr = e.Graphics;// Контекст экрана
gr.DrawString("Value=" +
vScroll.Value.ToString(), // Выводимый текст
new Font("Arial", 14, FontStyle.Bold),
// Устанавливаем шрифт
new SolidBrush(Color.Red), // Цвет шрифта
point, // Точка привязки на форме
strfrm); // Передаем точку привязки текстового блока
}
}
// Запуск
class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyClass());
}
}
}
Событие Scroll генерируется в любом случае взаимодействия пользователя с полосой прокрутки, даже если тот пытается выйти за пределы диапазона. Это событие несет в себе намного больше информации о состоянии полосы прокрутки и действиях пользователя. Если мы подпишемся на это событие, то в обработчик будет передаваться объект ScrollEventArgs с информацией, которая находится в свойствах:
| - SmallDecrement = 0 | Мышь: | пользователь щелкнул на верхней (левой) кнопке со стрелкой |
| Клавиатура: | пользователь нажал "стрелку вверх" ("стрелку влево") | |
| - SmallIncrement = 1 | Мышь: | пользователь щелкнул на нижней (правой) кнопке со стрелкой |
| Клавиатура: | пользователь нажал "стрелку вниз " ("стрелку вправо") | |
| - LargeDecrement = 2 | Мышь: | пользователь щелкнул на верхней (левой) области между бегунком и кнопкой со стрелкой |
| Клавиатура: | пользователь нажал клавишу PageUp | |
| - LargeIncrement = 3 | Мышь: | пользователь щелкнул на нижней (правой) области между бегунком и кнопкой со стрелкой |
| Клавиатура: | пользователь нажал клавишу PageDown | |
| - ThumbPosition = 4 | Мышь: | пользователь отпустил бегунок |
| - ThumbTrack = 5 | Мышь: | пользователь захватил бегунок (или перемещает его) |
| - First = 6 | Клавиатура: | пользователь нажал клавишу Home |
| - Last = 7 | Клавиатура: | пользователь нажал клавишу End |
| - EndScroll = 8 | Прокрутка завершена | |
Если операционная система сильно загружена и скролируется большой объем информации, то часть событий полосы прокрутки может игнорироваться, но обязательно будет перехвачено последнее событие Scroll с признаком Type=EndScroll и значение Value станет равным NewValue.
Ниже приведен код программы с заблокированной полосой прокрутки ("Хоть видит око, но зуб не ймет" - Крылов, басня)
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
// Класс приложения
class MyClass : Form
{
// Создаем вертикальную полосу прокрутки, которую
// потом настроим и будем отслеживать
VScrollBar vScroll = new VScrollBar();
public MyClass() // Конструктор
{
// Текст заголовка окна
this.Text = "Заблокированная прокрутка";
// Привязываем к родителю-форме
vScroll.Parent = this;
// Устанавливаем параметры привязки полосы прокрутки
vScroll.Height = this.ClientSize.Height;
vScroll.Location = new Point(this.ClientSize.Width - vScroll.Width, 0);
// Регистрируем событие Scroll
vScroll.Scroll += new ScrollEventHandler(vScroll_Scroll);
}
void vScroll_Scroll(object sender, ScrollEventArgs e)
{
// Запрещаем изменение Value
e.NewValue = e.OldValue;
}
// На случай изменения размеров формы
protected override void OnSizeChanged(EventArgs e)
{
// Пересчитываем параметры привязки полосы прокрутки
vScroll.Height = this.ClientSize.Height;
vScroll.Location = new Point(this.ClientSize.Width -
vScroll.Width, 0);
}
}
// Запуск
class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyClass());
}
}
}
Попробуйте запустить эту программу - полоса прокрутки не реагирует на действия пользователя.
Элемент TrackBar, реализуемый одноименным классом, по своей функциональности похож на класс ScrollBar. Часть свойств и событий этих двух классов совпадает как по названию, так и назначению. Некоторые из них перечислены в таблице. Причем не все свойства элемента TrackBar доступны в режиме проектирования, зато все доступны в программном режиме на этапе выполнения.
| Свойство | Пояснения |
|---|---|
| Value | Главная регулируемая величина, определяющая текущее состояние головки бегунка. По умолчанию начальное значение равно значению свойства Minimum |
| SmallChange | Минимальная дискретность изменения величины Value при управлении стрелками или перемещением головки бегунка с захватом мышью. По умолчанию установлено 1. |
| LargeChange | Максимальная дискретность изменения величины Value при нажатии клавиш PageUp или PageDown, или щелчках мышью на шкале элемента. По умолчанию установлено 5. |
| Minimum | Нижний предел значения Value. По умолчанию 0. |
| Maximum | Верхний предел значения Value. По умолчанию 10. |
| TickFrequency | Свойство целого типа, определяющее частоту делений на шкале ползунка. Расчитывается с использованием значений свойств Minimum и Maximum |
| TickStyle | Определяет форму ползунка при отображении пользователю. По умолчанию установлено значение BottomRight, то есть при горизонтальном ползунке метки с делениями располагаются снизу от него, а при вертикальном - справа |
| Orientation | Horizontal - горизонтальный ползунок, Vertical - вертикальный ползунок |
Вот простая программа, иллюстрирующая работу ползунка TrackBar
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
// Класс приложения
class MyClass : Form
{
// Создаем ползунок
TrackBar track = new TrackBar();
public MyClass() // Конструктор
{
// Текст заголовка окна
this.Text = "Ползунок TrackBar";
// Отключим изменение размеров окна, чтобы
// не пересчитывать расположение элементов
this.MaximizeBox = false;
//this.FormBorderStyle = FormBorderStyle.FixedSingle;
this.FormBorderStyle = FormBorderStyle.Fixed3D;// Красивше!
// В центре экрана
this.StartPosition = FormStartPosition.CenterScreen;
// Привязываем к форме
track.Parent = this;
// Настраиваем ползунок
track.Location = new Point(8, 104);
track.Size = new Size(272, 53);
track.Minimum = 0;
track.Maximum = 50;
track.Orientation = Orientation.Horizontal;
track.LargeChange = 5;
track.SmallChange = 1;
track.TickFrequency = 5;
track.TickStyle = TickStyle.BottomRight;
// Установим начальную позицию
track.Value = (track.Maximum - track.Minimum) / 2;
// Регистрируем событие изменения Value
track.ValueChanged += new EventHandler(track_ValueChanged);
}
// Отслеживает действия с ползунком
void track_ValueChanged(object sender, EventArgs e)
{
// Инициируем перерисовку
this.Invalidate();
}
// Перегружаем метод OnPaint(), унаследованный от класса Control
protected override void OnPaint(PaintEventArgs e)
{
// Точка привязки текстового блока
StringFormat strfrm = new StringFormat();
strfrm.Alignment = StringAlignment.Center;// Центр по горизонтали
strfrm.LineAlignment = StringAlignment.Center; // Центр по вертикали
// Точка привязки на форме
PointF point = new PointF(this.ClientSize.Width / 2,
this.ClientSize.Height / 3);// Граница верхней трети
// Рисуем текст
Graphics gr = e.Graphics;// Контекст экрана
gr.DrawString("Value=" +
track.Value.ToString(), // Выводимый текст
new Font("Arial", 14, FontStyle.Bold), // Устанавливаем шрифт
new SolidBrush(Color.Red), // Цвет шрифта
point, // Точка привязки на форме
strfrm); // Передаем точку привязки текстового блока
}
}
// Запуск
class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyClass());
}
}
}
А вот снимок экрана для этой программы

В библиотеке .NET Framework 2.0 в пространстве имен { } System.Windows.Forms имеется абстрактный класс TextBoxBase, который обеспечивает производные от него классы множеством возможностей по редактированию текста. Цепочка наследования этих классов выглядит так

Из рисунка видно, что существуют 5 классов, способных порождать текстовые элементы управления:
MaskedTextBoxRichTextBoxTextBoxDataGridTextBoxDataGridViewTextBoxEditingControlМы рассмотрим первые три класса как наиболее простые. Каждый из них имеет свойство Text типа string, в котором хранится редактируемая в элементе информация. Большинство других свойств принимают булево значение и определяют режим работы элемента.
Этот элемент удобно применить в том случае, когда нужно контролировать пользовательский ввод. Приведем пример с вводом телефонного номера. Вот какое поле ввода можно было бы предоставить пользователю
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
// Класс приложения
class MyClass : Form
{
// Объявляем ссылку на элемент поля ввода телефона
MaskedTextBox phone;
public MyClass() // Конструктор
{
// Код из конструктора переносим в обработчик
this.Load += new EventHandler(MyClass_Load);
}
void MyClass_Load(object sender, EventArgs e)
{
// Текст заголовка окна
this.Text = "Элемент MaskedTextBox";
// Отключаем размеры
this.MaximizeBox = false;
this.FormBorderStyle = FormBorderStyle.FixedSingle;
// Создаем элемент маскированного поля ввода телефона
phone = new MaskedTextBox();
phone.Parent = this;// Привязываем к форме
// Настраиваем маскированное поле ввода
int x = (this.ClientSize.Width - phone.Width) / 2;
int y = this.ClientSize.Height / 3 - phone.Height;
phone.Location = new Point(x, y);
phone.Mask = "00-00-00";
Font font = new Font("Arial", 12, FontStyle.Bold);
phone.Font = font;
phone.ForeColor = Color.Blue;// Синий
// Регистрируем событие, когда символ не может быть принят полем
phone.MaskInputRejected += phone_MaskInputRejected;
// Создаем и настраиваем текстовую метку
Label label = new Label();
label.Text = "Введите номер телефона";
label.Parent = this;
label.Width = this.ClientSize.Width;
label.Font = font;
label.ForeColor = Color.FromArgb(255, 0, 0);// Красный
// Позиционируем метку над полем ввода
label.Location = new Point(10, phone.Location.Y -
label.Height - 10);
}
ToolTip toolTip = new ToolTip();// Содаем объект всплывающей подсказки
void phone_MaskInputRejected(object sender, MaskInputRejectedEventArgs e)
{
if (phone.MaskFull)
{
toolTip.ToolTipTitle = "Ввод отклонен -
поле заполнено";
// Позиционируем всплывающую подсказку относительно поля ввода
toolTip.Show("Вы пытаетесь ввести" +
"\n" + "слишком длинный номер",
phone, // Привязка к элементу
-phone.Location.X,// Сдвинули в начало окна
phone.Height, // Сдвинули к низу поля ввода
3000);// Подсказка ждет 3 секунды
}
else if (e.Position >= phone.Mask.Length)
{
toolTip.ToolTipTitle = "Ввод отклонен -
выход за границу";
// Позиционируем всплывающую подсказку относительно поля ввода
toolTip.Show("Не могу добавить новый
символ\nза границей поля ввода",
phone, // Привязка к элементу
-phone.Location.X,// Сдвинули в начало окна
phone.Height, // Сдвинули к низу поля ввода
3000);// Подсказка ждет 3 секунды
}
else
{
toolTip.ToolTipTitle = "Ввод отклонен -
нужны цифры";
// Позиционируем всплывающую подсказку относительно поля ввода
toolTip.Show(
"Номер телефона должен состоять
только из цифр (0-9)",
phone, // Привязка к элементу
-phone.Location.X,// Сдвинули в начало окна
phone.Height, // Сдвинули к низу поля ввода
3000);// Подсказка ждет 3 секунды
}
}
}
// Запуск
class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyClass());
}
}
}
Результат выполнения выглядит так
![]() |
Граждане студенты! Это мой телефон, но не вздумайте звонить. |
Элементы строкового свойства Mask могут быть такими
| Маскирующий элемент | Пояснения |
|---|---|
| 0 | Ожидает любую цифру от 0 до 9 |
| 9 | Ожидается цифра или пробел |
| # | Ожидается цифра, пробел, знаки + или - |
| L | Ожидаются ASCII -символы от a до z или от A до Z |
| ? | Ожидаются любые ASCII -символы |
Ожидается любой символ. Если свойство AsciiOnly равно true, то работает как элемент "L" |
|
| C | Ожидается любой неуправляющий символ. Если свойство AsciiOnly равно true, то работает как элемент "?" |
| A | Ожидает любые алфавитно-цифровые символы. Если AsciiOnly равно true, то работает как элемент "L" |
И так далее по MSDN … |
|
Пример программы
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
// Класс приложения
class MyClass : Form
{
// Объявления полей
public MyClass() // Конструктор
{
this.Text = "Как быстро летит время!";
this.FormBorderStyle = FormBorderStyle.FixedSingle;
// Замыкаем размеры
this.MaximizeBox = false;
this.StartPosition = FormStartPosition.CenterScreen;
// Адресовали один объект
Label lbl = new Label(); // Первая надпись
lbl.Parent = this; // Отдали форме
lbl.Text = "Начальная дата: ";
lbl.AutoSize = true;
lbl.Location = new Point(Font.Height, Font.Height);
int xDateTimePicker = lbl.Right;
// Этой же ссылкой адресовали другой объект
lbl = new Label(); // Вторая надпись
lbl.Parent = this; // Отдали форме
lbl.Text = "Целевая дата: ";
lbl.AutoSize = true;
// Позиционируем ниже
lbl.Location = new Point(Font.Height, Font.Height * 3);
// Позиция правее правого
xDateTimePicker = Math.Max(xDateTimePicker, lbl.Right);
// Этой же ссылкой адресовали еще один объект
lbl = new Label();
lbl.Parent = this;
lbl.Text = "Прошло времени:";
lbl.AutoSize = true;
// Позиционируем еще ниже
lbl.Location = new Point(Font.Height, Font.Height * 5);
// Позиция правее правого
xDateTimePicker = Math.Max(xDateTimePicker, lbl.Right);
// Первое поле даты
dtpBegin = new DateTimePicker();
dtpBegin.Parent = this;
dtpBegin.Location = new Point(xDateTimePicker, Font.Height);
// Второе поле даты
dtpEnd = new DateTimePicker();
dtpEnd.Parent = this;
dtpEnd.Location = new Point(xDateTimePicker, Font.Height * 3);
// Регистрируем общий обработчик
dtpBegin.ValueChanged += DateTimePickerValueChanged;
dtpEnd.ValueChanged += DateTimePickerValueChanged;
lblResult1 = new Label();
lblResult1.Parent = this;
lblResult1.AutoSize = true;
// X пересчитываем в обработчике
lblResult1.Location = new Point(0, Font.Height * 7);
// Формируем фонт для меток результата
Font fnt = new Font("Arial", 12, FontStyle.Bold);
lblResult1.Font = fnt;
lblResult1.ForeColor = Color.Red;
lblResult2 = new Label();
lblResult2.Parent = this;
lblResult2.AutoSize = true;
// X пересчитываем в обработчике
lblResult2.Location = new Point(0, Font.Height * 9);
lblResult2.Font = fnt;
lblResult2.ForeColor = Color.Blue;
// Изменяем размеры окна
this.ClientSize = new Size(dtpEnd.Right + Font.Height,
Font.Height * 11);
}
void DateTimePickerValueChanged(object sender, EventArgs e)
{
String str;
if (dtpBegin.Value > dtpEnd.Value)
return;
else
{
DateTime dtBegin = dtpBegin.Value;
DateTime dtEnd = dtpEnd.Value;
int iYears = dtEnd.Year - dtBegin.Year;
int iMonths = dtEnd.Month - dtBegin.Month;
int iDays = dtEnd.Day - dtBegin.Day;
if (iDays < 0)
{
iDays += DateTime.DaysInMonth(dtEnd.Year, 1 +
(dtEnd.Month + 10) % 12);
iMonths -= 1;
}
if (iMonths < 0)
{
iMonths += 12;
iYears -= 1;
}
str = String.Format("{0} - лет; {1} -
месяцев; {2} - дней",
iYears, iMonths, iDays);
}
lblResult1.Text = str;
// По горизонтали позиционируем в центр
int x = (this.ClientSize.Width - lblResult1.Width) / 2;
int y = lblResult1.Location.Y;
lblResult1.Location = new Point(x, y);
// Получаем разницу во времени
TimeSpan time = dtpEnd.Value - dtpBegin.Value;
str = String.Format("({0} дней); ({1} часов)",
(long)time.TotalDays,
(long)time.TotalHours);
// По горизонтали позиционируем в центр
lblResult2.Text = str;
x = (this.ClientSize.Width - lblResult2.Width) / 2;
y = lblResult2.Location.Y;
lblResult2.Location = new Point(x, y);
}
}
// Запуск
class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyClass());
}
}
}
Результат выполнения

Если дату задает не пользователь, а программа, то вначале нужно сформировать структуру DateTime, затем ее присвоить свойству Value элемента DateTimePicker. Свойство DateTimePicker. Format, которое ожидает один из членов перечисления DateTimePickerFormat, определяет формат отображения содержимого поля элемента DateTimePicker. Свойства Value типа DateTime можно вычитать друг из друга. Полученная разница типа TimeSpan имеет, в том числе, свойства TotalDays, TotalHours.
Элемент управления TreeView отображает информацию в виде иерархического дерева с узловыми элементами типа TreeNode. Каждый узел TreeNode может быть родителем других узлов того же типа. Непосредственные потомки текущего узла-родителя указаны в свойстве Nodes типа TreeNodeCollection. По умолчанию узлы TreeNode дерева TreeView свернуты и имеют слева значок " + ", щелчок на котором разворачивает узел и появляется значок " - ". Программное управление сворачиванием/разворачиванием узлов выполняется методами Expand(), ExpandAll(), Collapse(), Toggle() объекта TreeNode, а его булевы свойства IsExpanded и IsSelected позволяют выяснить, развернут или выделен соответствующий узел.
Элемент TreeView имеет набор событий, которые реагируют на действия пользователя, например: BeforeSelect, AfterSelect, BeforeExpand, AfterExpand, BeforeCollapse, AfterCollapse. В обработчиках этих события можно организовать нужную логику работы дерева просмотра.
Приведем пример простой программы построения дерева, отображающего дерево каталогов диска C: текущего компьютера.
using System;
using System.Drawing;
using System.Windows.Forms;
// Пространство имен для работы с каталогами
using System.IO;
namespace Test
{
// Класс приложения
class MyClass : Form
{
public MyClass() // Конструктор
{
this.Text = "Простое дерево каталогов";
this.FormBorderStyle = FormBorderStyle.FixedSingle;
// Замыкаем размеры
this.MaximizeBox = false;
this.StartPosition = FormStartPosition.CenterScreen;
// Создаем объект дерева
TreeView tree = new TreeView();
// Привязываем к форме
tree.Parent = this;
// Разварачиваем на всю форму
tree.Dock = DockStyle.Fill;
// Создаем корневой узел
TreeNode nodeDriveC = new TreeNode("C:\\");
// Добавляем корневой узел к дереву просмотра
tree.Nodes.Add(nodeDriveC);
// Развертываем корневой узел
nodeDriveC.Expand();
// Считываем дерево каталогов
AddDirectories(nodeDriveC);
}
// Рекурсивный метод
void AddDirectories(TreeNode node)
{
// Для текущего узла node получаем полный путь к корню дерева
string strPath = node.FullPath;
// Создаем объект текущего каталога
DirectoryInfo dirInfo = new DirectoryInfo(strPath);
// Объявляем ссылку на массив подкаталогов текущего каталога
DirectoryInfo[] arrayDirInfo;
try
{
// Пытаемся получить список подкаталогов
arrayDirInfo = dirInfo.GetDirectories();
}
catch
{
// Подкаталогов нет, выходим из рекурсии
return;
}
// Добавляем прочитанные подкаталоги как узлы в дерево просмотра
foreach (DirectoryInfo dir in arrayDirInfo)
{
// Создаем новый узел с именем подкаталога
TreeNode nodeDir = new TreeNode(dir.Name);
// Добавляем его как дочерний к текущему узлу
node.Nodes.Add(nodeDir);
// Развертываем узел
//nodeDir.Expand();
// Делаем дочерний узел текущим и спускаемся рекурсивно ниже
AddDirectories(nodeDir);
}
}
}
// Запуск
class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyClass());
}
}
}
Если запустить эту программу, то можно заметить, что хоть она и выполняет все правильно, но очень долго (ужасно долго!) сканирует файловую систему диска (непростая работа).
Вот ее внешний вид

Спроектируем более совершенную версию программы для той же задачи построения дерева каталогов. Чтобы все было красиво, нужно извлечь иконки для соответствующих узлов из архива библиотеки VS2005ImageLibrary.zip, поставляемой в составе Visual Studio 2005.

Можно взять упомянутые иконки и из каталога Source данной работы.
Для включения значков в ресурс проекта, чтобы при компиляции они были внедрены в исполнимую сборку exe, нужно сделать следующее:
Можно таким же образом внедрить в сборку иконку самой формы приложения (например, " 1.ICO " в каталоге Source )
Окончательный код усовершенствованной программы просмотра дерева каталогов будет таким
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(); // Освобождаем перерисовку дерева
}
}
// Класс приложения
class MyClass : Form
{
public MyClass() // Конструктор
{
this.Text = "Улучшенное дерево каталогов";
this.FormBorderStyle = FormBorderStyle.FixedSingle;
// Замыкаем размеры
this.MaximizeBox = false;
this.StartPosition = FormStartPosition.CenterScreen;
this.Width += this.Width / 3; // Чуток расширили форму
// Внедряем в сборку ресурс иконки формы
this.Icon = new Icon(GetType(), "Resource.1.ICO");
// Создаем экземпляр пользовательского класса дерева
DirectoryTreeView myTree = new DirectoryTreeView();
// Привязываем к форме
myTree.Parent = this;
// Разварачиваем на всю форму
myTree.Dock = DockStyle.Fill;
}
}
// Запуск
class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyClass());
}
}
}
В старой программе с помощью рекурсивной функции, вызов которой был помещен в конструктор формы, мы вначале сканировали все дерево каталогов компьютера и тратили на это значительное время. Это существенно задерживало появление окна приложения при его запуске. В усовершенствованной программе в дереве просмотра для каждого раскрытого узла хранятся только два ближайших слоя дочерних узлов. При раскрытии пользователем очередного узла вначале срабатывает обработчик, которому передается коллекция дочерних узлов раскрываемого узла. В обработчике для каждого узла готовой раскрыться коллекции на лету создается новый слой дочерних узлов, готовых для раскрытия.
В новой программе мы всю работу разделили на этапы. Вначале создается два слоя - слой устройств и ближайший слой подчиненных им каталогов. При раскрытии узла устройства перед отображением уже вычисленного дочернего слоя вычисляется следующий в глубину слой для каждого дочернего узла, готовый для отображения при далнейшем раскрытии. Таким образом, при очередном шаге в глубину дерева вычисляется только один новый дочерний слой. Для пользователя эта задержка не очень заметна.
Вот внешний вид результата выполнения улучшенной версии программы чтения дерева каталогов

В Проводнике Windows используется именно этот элемент управления для отображения информации текущего каталога. Он может отражать подробную информацию в разных формах, определяемых свойством View типа одноименного перечисления System.Windows.Forms.View, которое содержит значения
При установке View=Details свойство Columns класса ColumnHeaderCollection должно содержать коллекцию объектов типа ColumnHeader, которые определяют заголовок таблицы. Эти объекты можно создавать по отдельности, затем добавлять к коллекции с помощью метода Add() свойства Columns, либа сразу сразу использовать перегрузку этого метода, задавая в нем нужные аргументы. Свойство Text определяет название заголовка столбца.
При установке View=Details свойство Items класса ListViewItemCollection содержит коллекцию объектов типа ListViewItem, которые представляют отдельные строки в таблице. Каждая строка, в свою очередь, является коллекцией SubItems типа ListViewSubItemCollection, состоящей из отдельных объектов типа ListViewSubItem, каждый из которых имеет набор свойств, определяющих атрибуты содержания и формы. Эти дополнительные объекты определяют ячейки таблицы в соответствующей строке. Строки могут маркироваться значками SmallImageList и LargeImageList типа ImageList.
В качестве иллюстрации создадим
Вначале нужно извлечь файл значка Folder.ico из библиотечного архива Visual Studio 2005 (или взять из прилагаемого каталога Source данной работы)

и добавить его к проекту командой Project/Add Existing Item

Не забудьте пометить в свойствах этого файла параметр Build Action как Embedded Resource.
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 / 1024, file.Length % 1024);
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 { } // Ничего не делаем
}
}
}
}
// Класс приложения
class MyClass : Form
{
public MyClass() // Конструктор
{
this.Text = "Отображение содержимого каталога";
this.FormBorderStyle = FormBorderStyle.FixedSingle;
// Замыкаем размеры
this.MaximizeBox = false;
this.StartPosition = FormStartPosition.CenterScreen;
this.Width *= 2; // Увеличили ширину формы
// Внедряем в сборку ресурс иконки формы
this.Icon = new Icon(GetType(), "Resource.1.ICO");
// Создаем экземпляр расширения класса ListView
DirectoryListView myList = new DirectoryListView();
// Привязываем к форме
myList.Parent = this;
// Разварачиваем на всю форму
myList.Dock = DockStyle.Fill;
// Устанавливаем режим "Таблица"
myList.View = View.Details;
// Устанавливаем текущим корневой каталог
myList.Directory = "C:\\";
}
}
// Запуск
class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyClass());
}
}
}
Результат выполнения программы примера будет таким

При выполнении последнего примера видно, что войти в дочерний каталог можно, но вновь вернуться в
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(); // Освобождаем перерисовку дерева
}
}
}
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 { } // Ничего не делаем
}
}
}
}
}
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();
}
}
}
using System.Windows.Forms;
namespace Test
{
// Запуск
class Program
{
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MyExplorer());
}
}
}
Внешнее представление программы будет таким

Итак, мы видим, что спроектированный нами MyExplorer имеет такую же (ну, почти такую!) функциональность, что и стандартный Проводник Windows.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.