5.7. Общие компоненты Часть 1

5.7. Общие компоненты

Давайте пробежимся по компонентам и посмотрим на их основные свойства и методы использования, чтобы впоследствии было проще с ними работать. Я разбил рассмотрение компонентов на три больших раздела — в соответствии с их поло-жением на панели Toolbox. В этом разделе речь пойдет о компонентах из группы Common Controls.

Мы будем рассматривать компоненты в том порядке, в котором они находятся в группе, т. е. по алфавиту. Некоторые из них в нашем рассмотрении могут быть пропущены по разным причинам.

Если вы впервые сталкиваетесь с программированием и впервые работаете с .NET, то я рекомендовал бы больше поиграть со свойствами рассматриваемых ком-понентов. Что я под этим понимаю? Нужно пробовать устанавливать различные значения свойств, которые мы будем рассматривать, и, запуская приложение, смот-реть на результат работы. Я мог бы расписывать вам каждое свойство очень по-дробно, но ничего не заменит реальной практики, которую вы можете получить, если сами будете пытаться, пробовать и изучать.

Мы станем рассматривать свойства в основном с точки зрения дизайна, но не забывайте, что это просто свойства компонентов, а, значит, вы можете изменять их в коде программы. Впоследствии мы будем чаще обращаться к свойствам и мето-дам компонентов именно в коде.

5.7.1. Button

Создайте новый проект и перетащите на его форму кнопку Button. Расположите ее ближе к левому верхнему углу окна. Выделите кнопку и найдите свойство Text. Это свойство отвечает за текст, который будет отображаться на поверхности кноп-ки. По умолчанию свойство равно имени компонента, а имя компонента именуется как buttonN, где N — это номер компонента с таким именем, т. е. самая первая кноп-ка на форме получит имя button1.

Мы ранее договорились, что компоненты не должны иметь бессмысленных имен, поэтому давайте изменим свойство Name кнопки, чтобы оно обрело смысл. Какое разумное имя выбрать? Имя должно отражать то, что будет выполнено по нажатию кнопки, а что она будет делать, мы пока не решили, поэтому дадим ей чуть более умное, но, все еще, бессмысленное имя. Напишите в свойстве Name имя myFirstButton. Дизайнер объявит в своем модуле соответствующую переменную, и для доступа к кнопке мы должны будем писать в коде myFirstButton.

Давайте попробуем сделать в нашем примере что-нибудь действующее. Просто поставить кнопку — маловато. Щелкните по ней двойным щелчком, чтобы создать обработчик события Click, который будет вызываться каждый раз, когда вы щелк-нете мышью по кнопке. Это, наверно, самое популярное свойство такого компо-нента, и именно этот обработчик создают программисты в большинстве случаев.

Любое свойство можно изменять и во время выполнения программы. Давайте по нажатию кнопки увеличивать значение позиции кнопки, т. е. увеличивать левую и верхнюю позиции (свойства Left и Top соответственно). Итак, в методе для обра-ботчика события пишем:

   private void button1_Click(object sender, EventArgs e)
   {
     myFirstButton.Left += 5;
     myFirstButton.Top += 5;
   }

Банальное увеличение целочисленного свойства объекта (объектом выступает кнопка), и больше добавить к этому коду просто нечего.

Для чего чаще всего используется кнопка? Конечно же, чтобы на нее нажимали пользователи, поэтому в реальных приложениях, как правило, приходится изме-нять свойство имени, свойство текста и отлавливать событие Click. Еще одно свой-ство, которое может вас заинтересовать: Image, через него вы можете задать кнопке какую-либо картинку.

Что ж, остановимся и посмотрим, как устанавливается картинка. Выделите свойство Image кнопки в панели Properties. Щелкните по появившейся в строке свойства кнопке, и вы должны увидеть окно загрузки изображения Select Resource (рис. 5.13).

Рис. 5.13. Окно загрузки изображения

В этом окне слева можно увидеть два переключателя:

  • Local resource — хранить картинку как локальный ресурс. Изображение бу-дет сохранено в файле ресурсов формы и окажется доступным только для кноп-ки. Если вы захотите использовать такое же изображение на другой кнопке или в меню, вам придется загружать изображение еще раз, а это значит, что ресурсы будут дублироваться, и размер программы неоправданно увеличится;
  • Project resource file — ресурс будет сохранен в файле ресурсов проекта, ко-торый находится в папке Properties. Можно изменить файл ресурсов, предлага-емый по умолчанию, если их несколько. Нужно только выбрать имя файла в вы-падающем списке. Сохранение картинки в файле ресурсов проекта означает, что в любом другом элементе управления в этом же проекте не нужно загружать изображение снова, — достаточно выбрать его имя в списке под переключате-лем Project resource file. На рис. 5.13 в моем файле ресурсов уже есть картинка с именем help, которую я загружал ранее, и теперь ее можно просто выбрать и назначить другим элементам.

Чтобы загрузить картинку и добавить ее в выбранный файл ресурсов (локаль-ный или выбранный), нажмите кнопку Import. Чтобы назначить выбранное/загру-женное изображение, нажмите кнопку OK.

Теперь очень важный момент — положение картинки на кнопке. По умолча-нию она окажется под текстом. Положение картинки определяет свойство TextImageRelation (отношение текста и картинки). Тут можно выбрать одно из сле-дующих значений:

  • Overlay — текст будет поверх картинки;
  • ImageAboveText — текст будет под картинкой;
  • TextAboveImage — текст будет над картинкой;
  • ImageBeforeText — картинка будет слева от текста;
  • TextBeforeImage — текст будет слева от картинки.

Еще одно свойство, на котором я хочу остановиться: TextAlign. Если выделить его в окне свойств, то появляется кнопка вызова выпадающего списка, а если щелкнуть по ней, то появится всплывающее окно (рис. 5.14). Здесь 9 кнопок в виде прямоугольников. Щелкая по кнопкам, вы можете выбрать, по какой кромке кноп-ки будет выравниваться текст. По умолчанию выбрана самая центральная кнопка, т. е. текст будет выровнен по центру. Если выбрать правую нижнюю кнопку, то текст будет выровнен к правому нижнему углу кнопки.

Наверно, это все самое интересное при использовании кнопок.

Рис. 5.14. Окно выбора выравнивания текста относительно кноп-ки

5.7.2. CheckBox

Компонент CheckBox позволяет выбирать одно из двух состояний, не считая не-определенного состояния, которое нельзя установить программно. Этот компонент используется, когда нужно, чтобы пользователь выбрал один из двух вариантов: да или нет. Например, в окнах настройки шрифтов флажком CheckBox выбирают, должен быть шрифт полужирным или нет, должен ли он быть подчеркнутым или нет, и всегда вопрос будет: да или нет.

У компонента всего два интересных свойства, не считая общих для всех компо-нентов свойств:

  • Checked — свойство равно истине (true), если пользователь поставил фла-жок, иначе будет ложь (false);
  • CheckState — состояние в виде перечисления, в котором можно указать одно из трех значений:
    • Indeterminate — неопределенно;
    • Unchecked — идентично значению свойства Checked, равному false;
    • Checked — идентично значению свойства Checked, равному true.

Следующий пример показывает, как можно программно изменять значения этих свойств:

   runAtStartupCheckBox.CheckState = CheckState.Indeterminate;
   deleteOnExitCheckBox.Checked = false;

В первой строке состояние компонента сбрасывается на неопределенное, а во второй строке другому компоненту свойство Checked меняется на false.

У этого компонента есть три интересных события, которые относятся именно к нему и могут пригодиться вам:

  • CheckedChanged — событие генерируется каждый раз, когда изменяется свой-ство Checked;
  • CheckedStateChanged — событие генерируется при изменении значения свой-ства CheckedState;
  • Click — свойство генерируется при щелчке по компоненту.

5.7.3. CheckedListBox

Допустим, вам нужно создать список, в котором напротив элементов пользова-тель должен будет ставить галочки. Если список маленький и не очень однород-ный, то можно обойтись несколькими элементами управления типа CheckBox. А ес-ли список состоит из 20 элементов? А если список динамический и может изме-няться? Вот тут на помощь приходит CheckedListBox. Пример такого компонента можно увидеть на рис. 5.15.

На рис. 5.15 показано окно, в котором в виде списка с флажками CheckBox при-веден список покупок. Подразумевается, что пользователь идет с ноутбуком или другим устройством по гипермаркету и отмечает по своему списку, что он купил, а что нет.

Рис. 5.15. Пример компонента CheckedListBox

Поскольку список на каждую неделю отличается (по крайней мере, у меня), то реализовывать эту динамику намного проще через компонент CheckedListBox.

Создайте новое приложение и поместите на его форму экземпляр компонента CheckedListBox. Выделите компонент и в панели Properties найдите свойство Items. Выделите его, а затем щелкните по появившейся кнопке. Появится окно небольшо-го текстового редактора, в котором можно вводить элементы списка. Каждый от-дельный элемент нужно писать в новой строке.

По умолчанию все элементы списка расположены в одну колонку. Если вы хо-тите расположить их в несколько колонок, как это сделано у меня, то нужно уста-новить свойство MultiColumn в true. Чтобы элементы в списке были отсортированы, свойство Sorted нужно установить в true.

Как работать с компонентом? Нам может понадобиться узнать, какой элемент выбран сейчас, т. е. на каком элементе сейчас стоит курсор. Это можно узнать через свойство SelectedItem следующим образом:

   if (checkedListBox1.SelectedItem != null)
     checkedListBox1.SelectedItem.ToString();

В первой строке я проверяю, не является ли выделенный элемент нулевым. Ес-ли он нулевой, то ничего не выделено, и мы не можем работать со свойством SelectedItem, которое имеет тип Object, т. е. само по себе является объектом класса. В таком случае свойство нулевое, и для него память не распределена, а, значит, обращение к свойствам или методам SelectedItem приведет к ошибке.

Если свойство не нулевое, то мы можем привести его к строке и увидеть имя элемента, который выделен. Именно это и происходит во второй строке.

Теперь посмотрим, как можно узнать все элементы, на которых стоят галочки, т. е. все отмеченные элементы. Выделенный и отмеченные элементы — это разные вещи.

Отмеченные элементы хранятся в виде коллекции (списка) в свойстве CheckedItems. Мы можем перебрать это свойство с помощью цикла — например, foreach:

   foreach (String str in checkedListBox1.CheckedItems)
     MessageBox.Show(str);

В этом случае в цикле просто выводятся все отмеченные элементы в окне со-общений, поэтому, если будете тестировать пример, не отмечайте слишком много элементов, чтобы много раз не нажимать кнопку OK для закрытия окна сообщений.

Вы можете управлять списком элементов в списке CheckedListBox. Список эле-ментов хранится в свойстве Items. Это свойство имеет тип класса коллекции Collection. Коллекции требуют отдельного разговора, а пока я хочу только сказать, что у этого свойства есть такие методы, как Add() (добавить новый элемент), Remove() (удалить), Clear() (очистить) и т. д. Коллекции встречаются очень часто, и в большинстве случаев работа с ними идентична, поэтому не буду торопиться и показывать вам все возможности коллекций, — мы станем знакомиться с ними постепенно.

Следующий пример показывает, как можно добавить в список новый элемент:

   checkedListBox1.Items.Add("Это что-то", true);

В качестве первого параметра методу Add() коллекции Items передается строка, содержащая текст, который будет назначен в качестве заголовка новому элементу. Второй параметр определяет, должен ли новый пункт быть помеченным или нет. Чаще всего коллекции компонентов будут принимать только один параметр — тек-стовое название. Но тут не совсем обычный компонент, потому что у элементов есть свойство в виде флажка, который можно установить при создании.

Следующий интересный пример показывает один из вариантов удаления отме-ченных элементов:

   foreach (int index in checkedListBox1.CheckedIndices)
     checkedListBox1.Items.RemoveAt(index);

В этом случае запускается цикл, перебирающий все элементы массива, который содержится в свойстве CheckedIndices. А содержится в этом свойстве массив чисел, которые являются индексами помеченных элементов. Если ничего не помечено, то это свойство содержит пустой массив.

Внутри цикла вызывается метод RemoveAt() свойства Items. Этот метод удаляет из коллекции Items элемент под индексом, указанным в качестве параметра. А это именно то, что нам нужно.

Такое удаление таит в себе опасность. Попробуйте запустить пример, выделите последние два элемента и нажмите кнопку удаления. Программа завершит работу аварийно. Допустим, что в списке 10 элементов, и вы выделили 9-й и 10-й для уда-ления. После удаления 9-го количество элементов сокращается до 9, и на следую-щем шаге удаление 10-го станет невозможным, потому что он уже сместился на 9-ю позицию.

Проблему можно решить банальным запуском удаления, начиная с максималь-ного индекса, к минимальному:

  for (int i = listBox1.CheckedIndices.Count - 1; i >= 0;  i--)
       listBox1.Items.RemoveAt(listBox1.CheckedIndices[i]);

В этом примере я запускаю цикл, начиная с последнего элемента из списка CheckedIndices, до нулевого. Свойство Count возвращает количество элементов в коллекции. Не забываем, что индексы нумеруются с нуля, а количество элементов отображает реальное количество, поэтому при инициализации переменной i я вы-читаю единицу из количества. Цикл идет с самого большого к минимальному, поэтому на этот раз, если выделить последние два элемента из 10, сначала будет удален 10-й, а потом 9-й, что абсолютно не нарушает пределы коллекции. После удаления 10-го в коллекции остается 9 элементов, а нам как раз нужно удалить 9-й.

Среди событий компонента вас могут заинтересовать следующие:

  • SelectedIndexChanged — срабатывает при изменении индекса выделенного элемента (свойство SelectedIndex);
  • SelectedValueChanged — срабатывает при изменении выделенного элемента (свойство SelectedValue).

Следующий пример показывает обработчик события SelectedValueChanged. При изменении выделенного элемента его заголовок будет отображаться на поверх- ности метки selectedLabel:

   private void checkedListBox1_SelectedValueChanged(
      object sender, EventArgs e)
   {
     selectedLabel.Text = checkedListBox1.SelectedItem.ToString();
   }

5.7.4. ComboBox

Выпадающий список ComboBox удобен тогда, когда нужно выбрать одно значе-ние из целого списка. Создайте новое приложение и поместите на форму экземпляр компонента ComboBox.

Элементы выпадающего списка задаются в свойстве Items, который имеет тип коллекции, схожий с тем, что мы рассматривали в разд. 5.7.3 у компонента CheckedListBox.

Давайте посмотрим, какие самые интересные свойства добавляет класс ComboBox к базовому классу компонентов:

  • MaxDropDownItems — количество видимых элементов в выпадающем списке, когда вы раскроете его. По умолчанию список раскрывается на 8 видимых эле-ментов, а для просмотра большего количества будет появляться полоса про-крутки. Если у вас всего 9 элементов, то иногда выгоднее расширить количество видимых элементов, чтобы не было полосы прокрутки;
  • DropDownStyle — стиль выпадающего списка. Стили в действии вы можете увидеть на рис. 5.16, а значения у свойства могут быть следующими:
    • Simple — простой стиль, при котором поле ввода элемента и список для выбора видны одновременно;
    • DropDown — вы можете отображать выпадающий список, щелкая по кноп-ке со стрелкой вниз, а также вводить любое значение в поле ввода выпадаю-щего списка с клавиатуры;
    • DropDownList — отличается от предыдущего тем, что нельзя вводить зна-чение с клавиатуры, а только выбирать из списка.

Рис. 5.16. Стили выпадающего списка ComboBox

Продолжим знакомиться с методами коллекции и посмотрим, как можно доба-вить сразу множество элементов. Для этого у коллекции есть метод AddRange(). Этому методу передается массив объектов класса Object, а так как это предок для любого класса, то, значит, можно передать массив любых значений. Чтобы отобра-зить в списке элементы в виде строк, компонент преобразует значения объектов в строку с помощью метода ToString(), наследуемого от Object.

Следующий пример показывает, как можно создать массив из элементов и тут же передать его методу AddRange() без сохранения в какой-либо переменной:

   comboBox1.Items.AddRange(new String[] { "Отлично",  "Хорошо"});

Если необходимо очистить сразу все элементы списка, то можно воспользо-ваться методом Clear() коллекции:

   comboBox1.Items.Clear();

Теперь посмотрим, как можно узнать, какой элемент выделен в выпадающем списке. Чтобы узнать выделенный элемент в виде текста, можно использовать свойство SelectedItem:

   if (comboBox1.SelectedItem != null)
   {
     String str = comboBox1.SelectedItem.ToString();
     MessageBox.Show(str);
   }
   else
     MessageBox.Show("Ничего не выделено");

Прежде чем работать со свойством SelectedItem, желательно проверить его на нулевое значение. Если в выпадающем списке ничего не выделено, то SelectedItem будет равен null. При попытке привести свойство к строке с помощью метода ToString(), когда свойство равно нулю, программа сгенерирует ошибку.

Если вы хотите узнать индекс выделенного элемента из списка, то можно вос-пользоваться свойством SelectedIndex. Это свойство — число, и его проверять на null не нужно. Если ничего не выделено, то свойство вернет 1. Это же значение будет, если вы введете в поле свое значение, а не выберете что-то из списка.

Среди событий компонента вас могут заинтересовать SelectedIndexChanged и SelectedValueChanged, которые мы уже видели в разд. 5.7.3.

5.7.5. DateTimePicker

Этот компонент удобен в тех случаях, когда пользователю нужно вводить в программу дату или время. И то, и другое имеет отдельный тип данных, о котором мы будем говорить в разд. 6.5, а сейчас познакомимся с компонентом DateTimePicker.

Он выглядит как выпадающий список, только в правом углу вместо стрелки для открытия списка находится значок, по нажатию на который появляется всплываю-щее окно для выбора даты. Это очень удобное окно, позволяющее избежать ошибок при вводе компонентов даты.

У компонента множество интересных свойств, дающих возможность настроить внешний вид календаря на любой цвет и вкус. Но самые интересные свойства — это функциональные:

  • Format — формат отображения значения. Здесь можно указать одно из сле-дующих значений:
    • Long — длинный формат, в котором месяц указывается названием, а не числом;
    • Short — короткий формат даты;
    • Time — отображать время для редактирования;
    • Custom — настраиваемый формат. В этом случае формат отображения за-дается через свойство CustomFormat;
  • MaxDate и MinDate — значения максимальной и минимальной даты, за преде-лы которых нельзя выходить;
  • ShowUpDown — показывать в компоненте справа две кнопочки со стрелками вверх и вниз, с помощью которых можно увеличивать и уменьшать текущую часть времени.

Чтобы получить в коде выбранную пользователем дату, можно использовать следующий код:

   DateTime dt = dateTimePicker1.Value;

Выбранное пользователем значение сохраняется в переменную класса DateTime. Да, время в .NET представляется классом DateTime. Это не просто переменная, а класс с большим количеством свойств и методов.

Самым интересным событием компонента можно назвать ValueChanged, которое генерируется каждый раз, когда вы изменяете дату или время внутри компонента. Но пользователь далеко не всегда может выбрать новое значение даты за один раз. Он может выбрать значение, подумать, а потом снова изменить его. Если вы хотите после изменения даты или времени тут же перерисовывать что-то, то это событие будет как раз кстати.

Если вам не нужно отслеживать изменения, которые делает пользователь, и вы хотите получить только окончательный выбор, то в этом случае может быть вы-годнее обрабатывать событие Leave, которое генерируется, когда компонент теряет фокус ввода, т. е. пользователь переключился на работу с другим элементом управ-ления.

5.7.6. Label и LinkLabel

Основная цель обоих компонентов — создание подписей, а у LinkLabel есть до-полнительные возможности для задания части текста на компоненте в виде под-черкнутой ссылки, при наведении на которую курсор будет меняться на значок с рукой.

У компонента Label основными свойствами являются:

  • Text — через это свойство мы можем задавать текст, который будет отобра-жаться на поверхности компонента, т. е. текст компонента;
  • AutoSize — по умолчанию равно true, чтобы компонент автоматически при-нимал минимально необходимые размеры для отображения текста. Если изме-нить текст, то компонент автоматически изменит свои размеры;
  • TextAlign — позволяет указать, по какой стороне компонента должен вы-равниваться текст. Это свойство бессмысленно при AutoSize равном true.

Компонент LinkLabel добавляет следующие свойства:

  • LinkColor и VisitedLinkColor — определяют цвет ссылки и цвет ссылки, если пользователь уже щелкал по ней;
  • LinkVisited — свойство с булевым значением, которое определяет, щелкал ли пользователь по ссылке. Вы должны сами отслеживать щелчки;
  • LinkArea — область ссылки внутри текста. По умолчанию весь текст являет-ся ссылкой, но вы можете ограничить ссылку, указав номер начального символа в свойстве Start и количество символов в ссылке в свойстве Length, которые по-являются, если раскрыть свойство LinkArea в редакторе свойств.

Несмотря на то, что компонент LinkLabel похож на интернет-ссылку всеми сво-ими фибрами, он только похож, а не работает как интернет-ссылка. Если вы введе-те в текстовое поле интернет-адрес (URL) и щелкнете по ссылке в запущенном приложении, ничего не произойдет. Вы должны сами написать код, который дол-жен выполняться по нажатию ссылки, и делать это нужно в обработчике события LinkClicked.

Хотя я убегаю сейчас в сторону, я покажу вам, как можно в обработчике собы-тия запустить браузер и загрузить страницу:

   System.Diagnostics.Process.Start("http://www.flenov.info");

В качестве параметра этого метода передается строка, которую нужно запу-стить в системе. В данном случае это URL, а для обработки URL ОС Windows запу-стит браузер по умолчанию и загрузит указанную страницу. Этой же командой можно запустить файл на выполнение. Для этого нужно просто указать имя файла, который вы хотите открыть или запустить.

5.7.7. ListBox

По своим свойствам компонент очень похож на выпадающий список CheckedListBox, потому что они очень похожи и по смыслу. Но список ListBox не имеет около каждого элемента списка компонента CheckBox, позволяющего ставить флажки.

С другой стороны, у ListBox работает свойство SelectionMode. Вы можете уста-новить его в MultiSimple или MultiExtended, чтобы дать возможность пользователю выбирать несколько элементов из списка. В CheckedListBox попытка установить любое из этих значений приведет к сообщению об ошибке.

Если нужно узнать, какой элемент сейчас выбран, можно воспользоваться свой-ством SelectedItem. Если вы разрешили возможность множественного выбора, то список выделенных элементов можно получить из свойства SelectedItems. Следу-ющий пример перебирает список выделенных компонентов и выводит каждую строку в диалоговом окне:

   foreach (string str in listBox1.SelectedItems)
     MessageBox.Show(str);

Среди событий компонента вас могут заинтересовать SelectedIndexChanged и SelectedValueChanged, которые мы уже видели в разд. 5.7.3.

5.7.8. ListView

Представление списка ListView — достаточно сложный на данном этапе изуче-ния компонент и очень мощный, потому что обладает громадными возможностями. С подобным компонентом мы работаем практически каждый день, потому что в окне Мой компьютер значки и имена файлов отображаются с помощью компо-нента этого класса.

Итак, пробежимся по свойствам компонента:

  • View — режим отображения. Если расставлять свойства по алфавиту, то мы должны были бы рассматривать его в конце, но я его поставил первым, потому что оно сильно влияет на работу некоторых других свойств. Это свойство явля-ется перечислением и может принимать одно из следующих значений:
    • LargeIcon — большие значки;
    • SmallIcon — маленькие значки;
    • List — маленькие значки списком;
    • Details — подробный режим;
    • Tile — позволяет отображать большие снимки;
  • Activation — реакция на ввод. Здесь можно указать одно из значений:
    • Standard — реакция определяется системой по умолчанию;
    • OneClick — реагировать по одинарному щелчку мыши. Это значит, что событие Click будет генерироваться после одинарного щелчка по элементу в списке;
    • TwoClick — реагировать после двойного щелчка мыши;
  • AllowColumnReorder — позволяет переопределять последовательность коло-нок, когда компонент отображается в режиме списка (Details);
  • AutoArrange — определяет, нужно ли автоматически сортировать элементы списка. Опция работает в режиме маленьких или больших значков (SmallIcon или LargeIcon);
  • CheckBoxes — позволяет отображать флажки CheckBox напротив каждого эле-мента списка;
  • Columns — позволяет задать колонки, которые будут отображаться в подроб-ном режиме. Выделите это свойство в панели Properties и щелкните по кнопке вызова окна редактора, который вы можете увидеть на рис. 5.17. Слева внизу окна находятся кнопки Add и Remove для добавления и удаления колонки. Со-зданные колонки появляются в списке Members слева, а справа можно увидеть редактор свойств выделенной колонки. Заголовок колонки можно задать в свой-стве Text;
  • FullRowSelect — если свойство равно true, то в подробном режиме будет выделяться не только заголовок элемента, но и его дополнительная информация в колонках (SubItems), которые мы задавали в свойстве Columns, т. е. полностью вся строка элемента, а не только заголовок;
  • Groups — это свойство позволяет создавать группы для элементов списка. При вызове редактора свойства появляется окно, схожее с редактором колонок. В окне также имеются кнопки создания и удаления групп, а свойства группы можно увидеть в правом списке. Заголовок группы можно задать в свойстве Header;
  • HeaderStyle — стиль заголовков колонок. Здесь можно указать одно из сле-дующих значений:
    • Clickable — по заголовку колонок можно щелкать как по кнопке. Чаще всего на щелчок мыши по заголовку вешают сортировку. Если вы не обраба-тываете щелчки, и они не нужны, то лучше использовать другое значение;
    • Nonclickable — по заголовку нельзя будет щелкать как по кнопке;
    • None — заголовок не нужен;
  • HideSelection — прятать выделение, когда элемент управления теряет фо-кус;
  • HotTracking — менять курсор на значок руки при наведении на элемент;
  • Items — здесь можно создавать элементы списка. Для этого свойства появ-ляется редактор, изображенный на рис. 5.18. Каждый элемент списка — это до-статочно сложный объект со множеством свойств, и он может потребовать от-дельного разговора. Текст, который будет назначен элементу, можно увидеть в свойстве Text, а в свойстве Group можно выбрать группу, в которую нужно доба-вить элемент. Свойство SubItems — это встроенная коллекция внутри элемента коллекции. О как! Здесь можно задавать дополнительные элементы, которые будут отображаться в колонках, которые вы могли задать в свойстве Columns представления списка ListView;
  • LabelEdit — булево свойство, определяющее, можно ли редактировать заго-ловки элементов;
  • LabelWrap — можно ли разбивать заголовок элементов на несколько строк, если его название слишком длинное;
  • LargeImageList — позволяет задать список со значками для больших карти-нок в виде компонента ImageList, который мы пока не рассматривали. Указав в этом свойстве список картинок, вы можете назначать элементам картинки из этого списка по индексу;
  • MultiSelect — разрешить множественное выделение;
  • ShowGroups — нужно ли отображать группы;
  • SmallImageList — позволяет задать список со значками маленьких картинок.

Рис. 5.17. Редактор колонок

Рис. 5.17. Редактор колонок

Рис. 5.18. Редактор элементов списка

Почему для свойств колонок, групп и элементов списка появляется окно редак-тора? Такое окно появляется для любых свойств нестроковых коллекций. До этого у выпадающего списка и у списка выбора мы видели строковые коллекции, и для их редактирования достаточно было простого текстового редактора. Тут у нас кол-лекции более сложных объектов, и для редактирования их свойств нужен специа-лизированный редактор, который и отображается.

Как вы уже поняли, элементы списка хранятся в свойстве Items. У этого свой-ства есть несколько перегруженных вариантов метода Add(), чтобы добавлять эле-менты в список. Самый простейший способ добавить новый элемент — передать методу текстовую строку, которая будет установлена в качестве заголовка нового элемента:

   listView1.Items.Add("Заголовок элемента");

Каждый элемент списка имеет класс ListViewItem, и в данном случае элемент такого класса создается автоматически, и ему присваивается указанный заголовок.

Если нужно настроить несколько свойств нового элемента, то удобнее и эффек-тивнее создать объект класса ListViewItem и передать этот объект методу Add(). Например, как в следующем примере:

   ListViewItem newItem = new ListViewItem("Test");
   newItem.Group = listView1.Groups[0];
   listView1.Items.Add(newItem);

Здесь явно создается экземпляр класса ListViewItem. Конструктору этого класса передается строка, которая станет заголовком элемента. Во второй строке у этого же элемента изменяется свойство Group. Группа имеет тип ListViewGroup, и уже су-ществующие группы находятся в списке свойства Groups. Чтобы не придумывать ничего сложного, я просто использую нулевую группу из этого списка (надеясь, что она там уже есть). Теперь остается только вызвать метод Add() у коллекции и передать ему созданный и настроенный объект класса ListViewItem.

А как добавить группу программно? Посмотрите на следующий пример:

   // создать группу
   ListViewGroup newGroup = new ListViewGroup("Группа");
   listView1.Groups.Add(newGroup);

   // создать элемент
   ListViewItem newItem = new ListViewItem("Test", newGroup);
   listView1.Items.Add(newItem);

Создание группы похоже на создание элементов списка, потому что группы — это тоже коллекции. Обратите внимание на то, как создается на этот раз элемент списка. В этом примере я передаю конструктору не только заголовок, но и объект группы, которая была создана только что.

В реальных приложениях может понадобиться также и определение выделен-ных элементов. У представления списка есть возможность выбирать несколько элементов сразу. Список этих элементов находится в свойстве SelectedItems. Это, опять же, коллекция из элементов ListViewItem, и ее можно просмотреть с помощью цикла foreach, например:

   foreach (ListViewItem item in listView1.SelectedItems)
     MessageBox.Show(item.Text);

В этом примере в цикле перебираются все выделенные в списке элементы и с помощью диалогового окна MessageBox отображаются названия элементов.

Теперь посмотрим, как можно удалять элементы из списка:

   if (listView1.Items.Count > 0)
     listView1.Items.Remove(listView1.Items[0]);

Прежде чем удалять элемент, нужно убедиться, что он есть. Я удаляю в этом примере нулевой элемент, поэтому достаточно удостовериться, что в списке есть хотя бы один элемент, т. е. Items.Count больше нуля. После этого вызывается метод Remove() свойства Items, которому нужно передать удаляемый элемент. Я выбираю нулевой элемент списка Items[0].

Далеко не всегда удобно удалять элемент по объекту, иногда удобнее исполь-зовать индексы. Следующий пример удаляет 5-й элемент, предварительно прове-рив, что в списке есть не менее пяти элементов:

   if (listView1.Items.Count > 5)
     listView1.Items.RemoveAt(5);

Для удаления использовался метод RemoveAt(), которому передается индекс удаляемого элемента.

Среди событий, которые вас могут заинтересовать при работе с компонентом, можно выделить следующие:

  • SelectedIndexChanged — событие генерируется при изменении индекса вы-деленного элемента в представлении;
  • SearchForVirtualItem — используется для поиска элементов в списке, когда он находится в виртуальном режиме. Этот поиск задействуется, когда вы наби-раете название элемента на клавиатуре;
  • VirtualItemsSelectionRangeChanged — генерируется каждый раз, когда изме-няется набор выделенных элементов. Событие генерируется, только когда ком-понент находится в виртуальном режиме;
  • ItemActivate — возникает при активации элемента. Метод активации задает-ся в свойстве Activation;
  • ItemCheck — событие возникает перед тем, как свойство Checked изменилось. В него еще не было записано нового значения, но сразу после обработки собы-тия это произойдет;
  • ItemChecked — генерируется, когда какой-то элемент отмечается флажком;
  • ItemDrag — событие возникает, когда пользователь начинает тянуть элемент списка;
  • ItemMouseHover — событие генерируется, когда курсор мыши движется над поверхностью элемента списка;
  • ItemSelectionChanged — генерируется при изменении выделенного элемента в списке;
  • DrawItem — событие возникает, когда нужно перерисовать элемент. Это со-бытие генерируется, только когда компонент находится в режиме OwnerDraw (од-ноименное свойство должно быть равно true). Вы можете использовать произ-вольный метод рисования элементов представления;
  • DrawSubItem — событие генерируется при рисовании подчиненного элемента (SubItem);
  • DrawColumnHeader — при рисовании заголовка списка;
  • ColumnClick — произошел щелчок по заголовку представления списка. Чаще всего в обработчике этого события реализуют возможность сортировки элемен-тов представления;
  • ColumnWidthChanged — пользователь изменил ширину колонок.

У компонента есть очень мощная возможность — программист сам может реа-лизовать функцию рисования элементов представления списка. Таким образом, элементы списка могут быть окрашены в любой цвет, все зависит от того, какой код вы напишете и как вы захотите нарисовать элементы списка.

Предыдущая глава

5.6. Компоненты .NET

О блоге

Программист, автор нескольких книг серии глазами хакера и просто блогер. Интересуюсь безопасностью, хотя хакером себя не считаю

Обратная связь

Без проблем вступаю в неразборчивые разговоры по e-mail. Стараюсь отвечать на письма всех читателей вне зависимости от страны проживания, вероисповедания, на русском или английском языке.

Пишите мне