Введение в, программирование, язык программирования, основы, операционная система
учебники, программирование, основы, введение в,

 

Организация интерфейса и рисование в формах

Организация интерфейса
Практически все проекты, построенные в наших лекциях, были консольными приложениями. В реальной жизни консольные проекты - это большая редкость. Причина, по которой из 12 возможных типов проектов мы выбирали наименее используемый, понятна. Нашей целью являлось изучение свойств языка, классов библиотеки FCL, для этих целей консольный проект вполне подходит, позволяя избегать введения не относящихся к сути дела деталей. Теперь цель достигнута - основные средства языка C# рассмотрены, учебный курс завершается. Остались важные темы, требующие более подробного рассмотрения, такие, как, например, работа с атрибутами, создание собственных атрибутов, класс Reflection, работа с файлами и базами данных; но все это предмет будущего курса. Тем не менее, нельзя окончить этот курс, не посвятив две последние лекции Windows-приложениям. Мне бы хотелось, чтобы активные слушатели (читатели) все консольные проекты переделали в Windows-проекты, построив подходящий для них интерфейс.
Первое знакомство с Windows-проектами состоялось в лекции 2, я настоятельно рекомендую перечитать ее, прежде чем продолжить чтение данной лекции. Вкратце напомню, как создается и выполняется Windows-проект. По умолчанию он содержит класс Form1 - наследника класса Form. Этот класс содержит точку входа в проект - процедуру Main, вызывающую статический метод Run класса Application, который создает объект класса Form1 и открывает форму - видимый образ объекта - для интерактивной работы пользователя. Открываемая форма содержит пользовательский интерфейс - окошки, кнопки, списки, другие элементы управления, меню . Все эти элементы способны реагировать на события, возникающие при выполнении пользователем каких-либо действий - нажатии кнопок, ввода текста, выбора пунктов меню.
Форма и элементы управления
Как населить форму элементами управления? Чаще всего, это делается вручную в режиме проектирования. Доступные элементы управления, отображаемые на специальной панели (Toolbox), перетаскиваются на форму. Этот процесс поддерживается особым инструментарием - дизайнером форм (Designer Form). Как только на этапе проектирования вы сажаете на форму элемент управления, немедленно в тексте класса появляются соответствующие строки кода (в лекции 2 об этом подробно рассказано). Конечно, все можно делать и программно - появление соответствующих строк кода приводит к появлению элементов управления на форме. Нужно понимать, что форма - это видимый образ класса Form, а элементы управления, размещенные на форме - это видимые образы клиентских объектов соответствующих классов, наследников класса Control. Так что форма с ее элементами управления есть прямое отражение программного кода.
Каждый вид элементов управления описывается собственным классом. Библиотека FCL содержит большое число классов, задающих различные элементы управления. Одним из типов проектов, доступных на C#, является проект, создающий элемент управления, так что ничто не мешает создавать собственные элементы управления и размещать их на формах наряду со встроенными элементами. Многие фирмы специализируются на создании элементов управления - это один из видов повторно используемых компонентов.
В каких отношениях находятся класс Form, класс Control, классы элементов управления? На рис. 24.1 показана иерархия отношений, связывающих эти классы.
Естественно, все эти классы являются потомками прародителя - класса Object. Заметьте, класс Control в иерархии классов занимает довольно высокое положение, хотя и у него есть два важных родительских класса - класс Component, определяющий возможность элементам управления быть компонентами, и класс MarshalByRefObject, задающий возможность передачи элементов управления по сети. Класс Control задает важные свойства, методы и события, наследуемые всеми его потомками. Все классы элементов управления являются наследниками класса Control. Чаще всего, это прямые наследники, но иногда они имеют и непосредственного родителя, которым может быть абстрактный класс - это верно для кнопок, списков, текстовых элементов управления. Может показаться удивительным, но класс Form является одним из потомков класса Control, так что форма - это элемент управления со специальными свойствами. Будучи наследником классов ScrollableControl и ContainerControl, форма допускает прокрутку и размещение элементов управления.
Взаимодействие форм
Обычное Windows-приложение всегда содержит несколько форм. Одни открываются в процессе работы, другие закрываются. В каждый текущий момент на экране может быть открыта одна или несколько форм, пользователь может работать с одной формой или переключаться по ходу работы с одной на другую.
Следует четко различать процесс создания формы - соответствующего объекта, принадлежащего классу Form или наследнику этого класса, - и процесс показа формы на экране. Для показа формы служит метод Show этого класса, вызываемый соответствующим объектом; для скрытия формы используется метод Hide. Реально методы Show и Hide изменяют свойство Visible объекта, так что вместо вызова этих методов можно менять значение этого свойства, устанавливая его либо в true, либо в false.
Заметьте разницу между сокрытием и закрытием формы - между методами Hide и Close. Первый из них делает форму невидимой, но сам объект остается живым и невредимым. Метод Close отбирает у формы ее ресурсы, делая объект отныне недоступным; вызвать метод Show после вызова метода Close невозможно, если только не создать объект заново. Открытие и показ формы всегда означает одно и то же - вызов метода Show. У формы есть метод Close, но нет метода Open. Формы, как и все объекты, создаются при вызове конструктора формы при выполнении операции new.
Форма, открываемая в процедуре Main при вызове метода Run, называется главной формой проекта. Ее закрытие приводит к закрытию всех остальных форм и завершению Windows-приложения. Завершить приложение можно и программно, вызвав в нужный момент статический метод Exit класса Application. Закрытие других форм не приводит к завершению проекта. Зачастую главная форма проекта всегда открыта, в то время как остальные формы открываются и закрываются (скрываются). Если мы хотим, чтобы в каждый текущий момент была открыта только одна форма, то нужно принять определенные меры, чтобы при закрытии (скрытии) формы открывалась другая. Иначе возможна клинчевая ситуация - все формы закрыты, предпринять ничего нельзя, а приложение не завершено. Конечно, выход всегда есть - всегда можно нажать магическую тройку клавиш CTRL+ALT+DEL и завершить любое приложение.
Можно создавать формы как объекты класса Form. Однако такие объекты довольно редки. Чаще всего создается специальный класс FormX - наследник класса Form. Так, в частности, происходит в Windows-приложении, создаваемом по умолчанию, когда создается класс Form1 - наследник класса Form. Так происходит в режиме проектирования, когда в проект добавляется новая форма с использованием пункта меню Add Windows Form. Как правило, каждая форма в проекте - это объект собственного класса. Возможна ситуация, когда вновь создаваемая форма во многом должна быть похожей на уже существующую, и тогда класс новой формы может быть сделан наследником класса формы существующей. Наследование форм мы рассмотрим подробнее чуть позже.
Модальные и немодальные формы
Первичным является понятие модального и немодального окна. Окно называется модальным, если нельзя закончить работу в открытом окне до тех пор, пока оно не будет закрыто. Модальное окно не позволяет, если оно открыто, временно переключиться на работу с другим окном. Выйти из модального окна можно, только закрыв его. Немодальные окна допускают параллельную работу в окнах. Форма называется модальной или немодальной в зависимости от того, каково ее окно. Метод Show открывает форму как немодальную, а метод ShowDialog - как модальную. Название метода отражает основное назначение модальных форм - они предназначены для организации диалога с пользователем, и пока диалог не завершится, покидать форму не разрешается.
Передача информации между формами
Часто многие формы должны работать с одним и тем же объектом, производя над ним различные операции. Как это реализуется? Обычная схема такова: объект создается в одной из форм, чаще всего, в главной. При создании следующей формы глобальный объект передается конструктору новой формы в качестве аргумента. Естественно, одно из полей новой формы должно представлять ссылку на объект соответствующего класса, так что конструктору останется только связать ссылку с переданным ему объектом. Заметьте, все это эффективно реализуется, поскольку объект создается лишь один раз, а разные формы содержат ссылки на этот единственный объект.
Если такой глобальный объект создается в главной форме, то можно передавать не объект, требуемый другим формам, а содержащий его контейнер - главную форму. Это удобнее, поскольку при этом можно передать несколько объектов, можно не задумываться над тем, какой объект передавать той или иной форме. Иметь ссылку на главную форму часто необходимо, хотя бы для того, чтобы при закрытии любой формы можно было бы открывать главную, если она была предварительно скрыта.
Представим себе, что несколько форм должны работать с объектом класса Books. Пусть в главной форме такой объект объявлен:
public Books myBooks;
В конструкторе главной формы такой объект создается:
myBooks = new Books(max_books);
где max_books - заданная константа. Пусть еще в главной форме объявлена форма - объект класса NewBook:
public NewBook form2;
При создании объекта form2 его конструктору передается ссылка на главную форму:
form2 = new NewBook(this);
Класс newBook содержит поля:
private Form1 mainform;
private Books books;
а его конструктор следующий код:
mainform = form;
books = mainform.myBooks;
Теперь объекту form2 доступны ранее созданные объекты, задающие книги и главную форму, так что в обработчике события Closed, возникающего при закрытии формы, можно задать код:
private void NewBook_Closed(object sender, System.EventArgs e)
{
mainform.Show();
}
открывающий главную форму.
Образцы форм
Создание элегантного, интуитивно ясного интерфейса пользователя - это своего рода искусство, требующее определенного художественного вкуса. Здесь все играет важную роль: размеры и расположение элементов управления, шрифты, важную роль играет цвет. Но тема красивого интерфейса лежит вне нашего рассмотрения. Нас сейчас волнует содержание. Полезно знать некоторые образцы организации интерфейса.
Главная кнопочная форма
Одним из образцов, применимых к главной форме, является главная кнопочная форма. Такая форма состоит из текстового окна, в котором описывается приложение и его возможности, и ряда командных кнопок; обработчик каждой кнопки открывает форму, позволяющую решать одну из задач, которые поддерживаются данным приложением. В качестве примера рассмотрим Windows-приложение, позволяющее работать с различными динамическими структурами данных. Главная кнопочная форма такого приложения показана на рис. 24.2.
Обработчик события Click для каждой командной кнопки открывает форму для работы с соответствующей динамической структурой данных. Вот как выглядит обработчик события кнопки Список:
private void button4_Click(object sender, System.EventArgs e)
{
//Переход к показу формы для работы со списком
FormList fl= new FormList();
fl.Show();
}
Как видите, открывается новая форма для работы со списком, но главная форма не закрывается и остается открытой.
Шаблон формы для работы с классом
Можно предложить следующий образец формы, предназначенной для поддержки работы с объектами некоторого класса. Напомню, каждый класс представляет тип данных. Операции над типом данных можно разделить на три категории: конструкторы, команды и запросы. Конструкторы класса позволяют создать соответствующий объект; команды, реализуемые процедурами, изменяют состояние объекта; запросы, реализуемые функциями без побочных эффектов, возвращают информацию о состоянии объекта, не изменяя самого состояния. Исходя из этого, можно сконструировать интерфейс формы, выделив в нем три секции. В первой секции, разделенной на три раздела, будут представлены команды, запросы и конструкторы. Следующая секция выделяется для окон, в которые можно вводить аргументы исполняемых команд. Последняя секция предназначается для окон, в которых будут отображаться результаты запросов.
На рис. 24.3 показана форма для списка с курсором, построенная в соответствии с описанным шаблоном.
Список с курсором имеет группу команд, позволяющих перемещать курсор влево, вправо, к началу и концу списка, к элементу с заданным номером. Другая группа команд позволяет производить операции по вставке элементов слева или справа от курсора, удалять элемент, отмеченный курсором. Еще одна группа позволяет производить поиск элементов в списке. Запросы позволяют получить данные об активном элементе, отмеченном курсором, определить число элементов в списке и получить другую полезную информацию.
Работа со списками (еще один шаблон)
Для организации интерфейса разработано большое число элементов управления, часть из них показана на рис. 24.1. Все они обладают обширным набором свойств, методов и событий, их описание может занять отдельную книгу. Такие элементы, как, например, ListView, TreeView, DataGrid, несомненно, заслуживают отдельного рассмотрения, но не здесь и не сейчас. Я ограничусь более подробным разбором лишь одного элемента управления - ListBox, - позволяющего отображать данные в виде некоторого списка.
Элемент управления класса ListBox
Во многих задачах пользователю предлагается некоторый список товаров, гостиниц, услуг и прочих прелестей, и он должен выбрать некоторое подмножество элементов из этого списка. Элемент управления ListBox позволяет собрать в виде списка некоторое множество объектов и отобразить для каждого объекта связанную с ним строку. Он дает возможность пользователю выбрать из списка один или несколько элементов.
В списке могут храниться строки, тогда объект совпадает с его отображением. Если же хранятся объекты, то в классе объекта следует переопределить метод ToString, возвращаемый результат которого и будет строкой, отображаемой в списке.
Давайте рассмотрим главный вопрос: как список заполняется элементами? Есть несколько разных способов. Новой и интересной технологией, применимой к самым разным элементам управления, является связывание элемента управления с данными, хранящимися в различных хранилищах, прежде всего, в базах данных. Для этого у списка есть ряд свойств - DataBinding и другие. Эта технология заслуживает отдельного рассмотрения, я о ней только упоминаю, но рассматривать ее не буду. Рассмотрим три других способа.
Заполнить список элементами можно еще на этапе проектирования. Для этого достаточно выбрать свойство Items: появится специальное окно для заполнения списка строками - элементами списка. Добавлять объекты других классов таким способом невозможно.
Но это можно делать при программной работе со свойством Items, возвращающим специальную коллекцию объектов, которая задана классом ObjectCollection. Эта коллекция представляет объекты, хранимые в списке, и является основой для работы со списком. Класс ObjectCollection предоставляет стандартный набор методов для работы с коллекцией - вставки, удаления и поиска элементов. Метод Add позволяет добавить новый объект в конец коллекции, метод Insert позволяет добавить элемент в заданную позицию, указанную индексом. Метод AddRange позволяет добавить сразу множество элементов, заданное обычным массивом, массивом класса ListArray или коллекцией, возвращаемой свойством Items другого списка. Для удаления элементов используются методы Remove, RemoveAt, Clear. Метод Contains позволяет определить, содержится ли заданный объект в коллекции, а метод IndexOf позволяет определить индекс такого элемента. Коллекция может автоматически сортироваться, для этого достаточно задать значение true свойства Sorted, которым обладает список ListBox.
Еще один способ задания элементов списка поддерживается свойством DataSource, значение которого позволяет указать источник данных, ассоциируемый со списком. Понятно, что этот способ является альтернативой коллекции, задаваемой свойством Items. Так что, если источник данных определен свойством DataSource, то нельзя использовать методы класса ObjectCollection - Add и другие для добавления или удаления элементов списка, - необходимо изменять сам источник данных.
Главное назначение элемента ListBox - предоставить пользователю возможность осуществлять выбор из отображаемых списком элементов. Свойство SelectionMode позволяет указать, сколько элементов разрешается выбирать пользователю - один или несколько. Для работы с отобранными элементами имеется ряд свойств. SelectedItem и SelectedIndex возвращают первый отобранный элемент и его индекс. Свойства SelectedItems и SelectedIndices возвращают коллекции, заданные классами SelectedObjectCollection и SelectedIndexCollection, которые дают возможность анализировать все отобранные пользователем объекты. Методы Contains и IndexOf позволяют определить, выбрал ли пользователь некоторый элемент. Добавлять или удалять элементы из этих коллекций нельзя.
Среди других методов и свойств ListBox - упомяну свойство MultiColumn, с помощью которого можно организовать показ элементов списка в нескольких столбцах; свойство HorizonalScrollBar, задающее горизонтальный скроллинг; методы BeginUpdate и EndUpdate, позволяющие повысить эффективность работы со списком. Все методы по добавлению и удалению элементов, стоящие после BeginUpdate, не будут приводить к перерисовке списка, пока не встретится метод EndUpdate.
У элемента управления ListBox большое число событий, с некоторыми из которых мы встретимся при рассмотрении примеров. Перейдем теперь к рассмотрению примеров работы с этим элементом управления и, как обещано, построим некоторый шаблон, демонстрирующий работу с двумя списками, когда пользователь может переносить данные из одного списка в другой и обратно. На рис. 24.4 показано, как выглядит форма, реализующая данный шаблон.
На форме показаны два списка - listBox1 и listBox2, между которыми расположены две командные кнопки. Обработчик события Click первой кнопки переносит выбранную группу элементов одного списка в конец другого списка, (если включено свойство Sorted, то автоматически поддерживается сортировка списка). Переносимые элементы удаляются из первого списка. Вторая кнопка реализует операцию переноса всех элементов списка. Направление переноса - из левого списка в правый и обратно - задается заголовками (">", ">>") или ("<", "<<"), изображенными на кнопках. Заголовки меняются автоматически в обработчиках события Enter, возникающих при входе в левый или правый списки - listBox1 или listBox2. Еще две командные кнопки, как следует из их заголовков, предназначены для закрытия формы с сохранением или без сохранения результатов работы пользователя. Таково общее описание шаблона. А теперь рассмотрим реализацию. Начнем с обработчиков события Enter наших списков:
private void listBox1_Enter(object sender, System.EventArgs e)
{
/*** Событие Enter у списка возникает при входе в список ***/
button1.Text = ">"; button2.Text = ">>";
}
private void listBox2_Enter(object sender,
System.EventArgs e)
{
/*** Событие Enter у списка возникает при входе в список ***/
button1.Text = "<"; button2.Text = "<<";
}
Посмотрим, как устроены обработчики события Click для командных кнопок, осуществляющих перенос данных между списками:
private void button1_Click(object sender, System.EventArgs e)
{
/* Обработчик события Click кнопки "> <"
* Выборочный обмен данными между списками
* ListBox1 <-> ListBox2******************/
if(button1.Text == ">")
MoveSelectedItems(listBox1, listBox2);
else
MoveSelectedItems(listBox2, listBox1);
}
private void button2_Click(object sender, System.EventArgs e)
{
/* Обработчик события Click кнопки ">> <<"
* Перенос всех данных одного списка в конец другого списка
* ListBox1 <-> ListBox2******************/
if(button2.Text == ">>")
MoveAllItems(listBox1, listBox2);
else
MoveAllItems(listBox2, listBox1);
}
Обработчики событий устроены достаточно просто - они вызывают соответствующий метод, передавая ему нужные аргументы в нужном порядке. Рассмотрим метод, переносящий множество отобранных пользователем элементов из одного списка в другой:
private void MoveSelectedItems(ListBox list1, ListBox list2)
{
/*** Выделенные элементы списка list1 ****
*** помещаются в конец списка List2 *****
*** и удаляются из списка list1 ********/
list2.BeginUpdate();
foreach (object item in list1.SelectedItems)
{
list2.Items.Add(item);
}
list2.EndUpdate();
ListBox.SelectedIndexCollection indeces = list1.SelectedIndices;
list1.BeginUpdate();
for (int i = indeces.Count -1; i>=0 ; i--)
{
list1.Items.RemoveAt(indeces[i]);
}
list1.EndUpdate();
}
Некоторые комментарии к этому тексту. Заметьте, для добавления выделенных пользователем элементов к другому списку используется коллекция SelectedItems и метод Add, поочередно добавляющий элементы коллекции. Метод AddRange для добавления всей коллекции здесь не проходит:
list2.Items.AddRange(list1.SelectedItems);
поскольку нет автоматического преобразования между коллекциями ObjectCollection и SelectedObjectCollection.
Для удаления выделенных элементов из списка list1 используется коллекция индексов. Обратите внимание, при удалении элемента с заданным индексом из любой коллекции индексы оставшихся элементов автоматически пересчитываются. Поэтому удаление элементов происходит в обратном порядке, начиная с последнего, что гарантирует корректность оставшихся индексов.
Намного проще устроен метод, переносящий все элементы списка:
private void MoveAllItems(ListBox list1, ListBox list2)
{
/*** Все элементы списка list1 ****
**** переносятся в конец списка list2 ****
**** список list1 очищается *************/
list2.Items.AddRange(list1.Items);
list1.Items.Clear();
}
Добавим еще одну функциональную возможность - разрешим переносить элементы из одного списка в другой двойным щелчком кнопки мыши. Для этого зададим обработчики события DoubleClick наших списков:
private void listBox1_DoubleClick(object sender,
System.EventArgs e)
{
/* Обработчик события DoubleClick левого списка
* Выбранный элемент переносится в правый список
* ListBox1 <-> ListBox2******************/
MoveSelectedItems(listBox1, listBox2);
}
private void listBox2_DoubleClick(object sender,
System.EventArgs e)
{
/* Обработчик события DoubleClick правого списка
* Выбранный элемент переносится в левый список
* ListBox1 <-> ListBox2******************/
MoveSelectedItems(listBox2, listBox1);
}
Обработчики вызывают уже рассмотренные нами методы.
На этом закончим рассмотрение функциональности проектируемого образца формы. Но, скажете вы, остался не заданным целый ряд вопросов: непонятно, как происходит заполнение списков, как сохраняются элементы после завершения переноса, обработчики события Click для двух оставшихся кнопок не определены. Ничего страшного. Сделаем нашу форму родительской, возложив решение оставшихся вопросов на потомков, пусть каждый из них решает эти вопросы по-своему.

http://localhost:3232/img/empty.gifhttp://localhost:3232/img/empty.gifНаследование форм

Для объектного программиста форма - это обычный класс, а населяющие ее элементы управления - это поля класса. Так что создать новую форму - новый класс, наследующий все поля, методы и события уже существующей формы - не представляет никаких проблем. Достаточно написать, как обычно, одну строку:
public class NewForm : InterfacesAndDrawing.TwoLists
Нужно учитывать, что имени класса родителя должно предшествовать имя пространства имен.
Чаще всего, наследуемые формы создаются в режиме проектирования при выборе пункта меню Add Inherited Form. (Добраться до этого пункта можно двояко. Можно выбрать пункт Project/ AddInheritedForm из главного меню либо выбрать имя проекта в окне проекта и выбрать пункт Add/Add Inherited Form из контекстного меню, открывающегося при щелчке правой кнопкой.)
В результате открывается окно Inheritance Picker, в котором можно выбрать родительскую форму. Заметьте, родительская форма может принадлежать как текущему, так и любому другому проекту. Единственное ограничение - проект, содержащий родительскую форму, должен быть скомпилирован как exe или dll. Вот как выглядит окно для задания родительской формы.
При наследовании форм следует обратить внимание на модификаторы доступа элементов управления родительской формы. По умолчанию они имеют статус private, означающий запрет на изменение свойств и обработчиков событий этих элементов. Чаще всего, такая концепция не верна. Мы не можем знать причин, по которым наследникам захочется изменить созданные родителем свойства элементов. Правильным решением является изменение значения модификатора для всех элементов управления родительской формы на protected. У всех элементов родительской формы есть свойство modifiers, в котором можно указать статус элемента управления, что и было сделано для всех элементов нашего шаблона - формы TwoLists.
Наследованную форму можно затем открыть в дизайнере форм, добавить в нее новые элементы и новые обработчики событий или изменить установки наследуемых элементов, если родительская форма предоставила такую возможность. (Хочу предупредить об одном возможном "жучке", связанном с наследованием форм. На одном из моих компьютеров установлена ОС Windows 2000, на другом - Windows XP. Так вот, в Windows 2000 дизайнер отказывается открывать наследуемую форму, хотя она создается и нормально работает. Это происходит как для Visual Studio 2003, так и для beta2 Visual Studio 2005. В Office XP все работает нормально. Не могу утверждать совершенно определенно, что это "жучок", поскольку не проводил тщательного исследования. Но полагаю, что предупредить о такой ситуации полезно.)
Два наследника формы TwoLists
Построим по указанной технологии двух наследников формы TwoLists. Дадим им имена: TwoLists_Strings и TwoLists_Books. Они будут отличаться тем, что первый из них будет заполнять левый список строками, а второй - "настоящими объектами" класса Book. Второй список при открытии форм будет оставаться пустым и служить для хранения выбора, сделанного пользователем. Оба наследника будут также задавать обработчики события Click для командных кнопок, завершающих работу с этими формами. На рис. 24.6 показана наследуемая форма, открытая в дизайнере форм.
Обратите внимание на значки, сопровождающие все наследуемые элементы управления. В классе TwoLists_Strings добавлены поля:
string[] source_items;
string[] selected_items;
const int max_items = 20;
В конструктор класса добавлен код, инициализирующий массивы:
source_items = new string[max_items];
selected_items = new string[max_items];
InitList1();
Вызываемый в конструкторе закрытый метод класса InitList заполняет массив source_items - источник данных - строками, а затем передает эти данные в левый список формы. По-хорошему, следовало бы организовать заполнение списка формы из базы данных, но я здесь выбрал самый примитивный способ:
void InitList1()
{
//задание элементов источника и инициализация списка формы
source_items[0] ="Бертран Мейер:   Методы программирования";
//аналогично заполняются другие элементы массива
//перенос массива в список ListBox1
int i = 0;
while (source_items[i] != null)
{
this.listBox1.Items.Add(source_items[i]);      i++;
}                  
//this.listBox1.DataSource = source_items;
}
Закомментирована альтернативная возможность заполнения списка формы, использующая свойство DataSource. Когда форма откроется, ее левый список будет заполнен, пользователь сможет выбрать из списка понравившиеся ему книги и перенести их в правый список. Зададим теперь обработчики события Click для командных кнопок ("Сохранить выбор" и "Не сохранять"):
private void button3_Click(object sender, System.EventArgs e)
{
int i =0;
foreach(string item in listBox2.Items)
{
selected_items[i] = item;
Debug.WriteLine(selected_items[i]);
i++;
}
this.Hide();
}
private void button4_Click(object sender, System.EventArgs e)
{
foreach(string item in listBox2.Items)
{
Debug.WriteLine(item);
}
this.Hide();
}
Оба они в Debug-версии проекта выводят данные о книгах, выбранных пользователем, и скрывают затем форму. Но первый из них сохраняет результаты выбора в поле selected_items.
Второй наследник TwoLists_Books устроен аналогично, но хранит в списке не строки, а объекты класса Book. Приведу уже без комментариев соответствующие фрагменты кода:
Book[] source_items;
Book[] selected_items;
const int max_items = 20;
Код, добавляемый в конструктор:
source_items = new Book[max_items];
selected_items = new Book[max_items];
InitList1();
Метод InitList1 скорректирован для работы с книгами:
void InitList1()
{
//задание элементов источника и инициализация списка формы
Book newbook;
newbook = new Book("Бертран Мейер",
"Методы программирования",3,1980);
source_items[0] =newbook;
//остальные элементы массива заполняются аналогичным
//образом
//перенос массива в список ListBox1
int i = 0;
while (source_items[i] != null)
{
this.listBox1.Items.Add(source_items[i]);
i++;
}
}
Обработчики событий Click командных кнопок, завершающих работу с формой, имеют вид:
private void button3_Click(object sender, System.EventArgs e)
{
int i =0;
foreach(object item in listBox2.Items)
{
selected_items[i] = (Book)item;
selected_items[i].PrintBook();
i++;
}
this.Hide();
}
private void button4_Click(object sender, System.EventArgs e)
{
Book book;
foreach(object item in listBox2.Items)
{
book = (Book)item;
book.PrintBook();
}
this.Hide();
}
Класс Book определен следующим образом:
public class Book
{
//поля
string author, title;
int price, year;
public Book(string a, string t, int p, int y)
{
author = a; title = t; price = p; year = y;
}
public override string ToString()
{
return( title + " : " + author);
}
public void PrintBook()
{
Debug.WriteLine("автор:" + author + " название: " +
title + " цена: " + price.ToString() +"
год издания: " + year.ToString());
}
}
Обратите внимание, что в классе, как и положено, переопределен метод ToString, который задает строку, отображаемую в списке.
В завершение проекта нам осталось спроектировать главную форму. Сделаем ее в соответствии с описанным ранее шаблоном кнопочной формой (рис. 24.7).
Обработчики событий Click вызывают соответствующую форму для работы либо со списком, хранящим строки, либо со списком, хранящим объекты. На рис. 24.8 показана форма, хранящая строки, в процессе работы с ней.

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г.Яндекс.Метрика