Министерство образования и науки Российской федерации Федеральное агентство по образованию Федеральное государственное о...
9 downloads
258 Views
1MB Size
Report
This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Report copyright / DMCA form
Министерство образования и науки Российской федерации Федеральное агентство по образованию Федеральное государственное образовательное учреждение высшего профессионального образования «ЮЖНЫЙ ФЕДЕРАЛЬНЫЙ УНИВЕРСИТЕТ»
Л.А. Мачулина, В.А. Скороходов
ИСПОЛЬЗОВАНИЕ СРЕДСТВ VISUAL BASIC .NET В СОЗДАНИИ ИНФОРМАЦИОННЫХ СИСТЕМ
Учебно-методическое пособие для студентов экономического факультета
Ростов-на-Дону 2008
Решение об издании в электронном виде принято на заседании кафедры алгебры и дискретной математики, протокол № 2 от 26 сентября 2008 г.
Пособие содержит необходимые теоретические и практические материалы для разработки программного обеспечения информационных систем в среде программирования Visual Basic .NET в системе Visual Studio 2008 Express Edition. На примере информационной системы «Студенты» рассматриваются основные этапы проектирования и реализации информационных систем. Приводятся технические задания на проекты для самостоятельной разработки. Авторы: Л.А. Мачулина, В.А. Скороходов.
© Л.А. Мачулина, В.А. Скороходов, 2008 2
Предисловие
Данное пособие содержит описание технологии разработки большого программного проекта и реализации его средствами традиционного процедурного и объектно-ориентированного программирования в среде визуального программирования. В качестве объекта разработки и реализации выбран тип приложения, которое по существу является информационной системой. То есть это приложение, прежде всего, с хорошим графическим интерфейсом, которое содержит средства для создания и ведения базы данных, средства манипулирования данными, а также программы обработки данных для получения определенной информации. Реализация информационной системы выполняется в среде визуального программирования Visual Basic 2008 Express Edition. Первая часть пособия посвящена вопросам моделирования данных и проектированию программного обеспечения информационной системы. Вторая часть содержит описание реализации информационной системы для анализа успеваемости в учебном заведении. Третья часть пособия содержит варианты технических заданий на разработку информационных систем, которые должны выполняться студентами самостоятельно в рамках практикума. Материалы, представленные в пособии, используются при проведении учебной практики студентов специальностей «Прикладная информатика в экономике» и «Математические методы исследования операций в экономике».
3
Часть I
Проектирование информационной системы 1. Описание предметной области На первом этапе проектирования информационной системы необходимо выполнить анализ предметной области, т.е. определить объекты предметной области и связи между объектами. При выборе состава и структуры предметной области возможны два подхода: функциональный и предметный. Функциональный подход реализует принцип движения «от задач» и применяется, когда определен комплекс задач, для обслуживания которых создается информационная система. В этом случае можно выделить минимальный необходимый набор объектов предметной области, которые должны быть описаны. В предметном подходе объекты предметной области определяются с таким расчетом, чтобы их можно было использовать при решении множества разнообразных, заранее не определенных задач. Чаще всего используется комбинация этих двух подходов. Опишем предметную область, предлагаемую в качестве примера в проекте, реализация которого представлена в данном пособии. Предположим, что для деканата одного из факультетов учебного заведения требуется разработать информационную систему для автоматизации анализа и учета данных о студентах, обучающихся на факультете, и их успеваемости в текущей сессии. В данном случае основным объектом предметной области является студент. Мы будем придерживаться функционального подхода, т.е. идти от задач, которые будут решаться. Поэтому ограничимся определенным набором свойств объекта «студент». Перечислим эти свойства: 4
– Номер зачетной книжки, – Фамилия Имя и Отчество, – Дата рождения, – Пол, – Курс, – Номер учебной группы, – Специальность, – Количество сдаваемых в сессию экзаменов, – Оценки, полученные на экзаменах. Мы выбрали минимальный набор свойств-характеристик студента. Естественно, что этот набор может быть расширен. Необходимо предусмотреть следующие ограничения на информацию: 1. Курсы имеют значения от 1 до 5. 2. Номер учебной группы изменяется от 1 до 15. 3. Количество сдаваемых в сессию экзаменов может изменяться от 2 до 5. Предполагается, что с данной информационной системой будут работать сотрудники деканата, и они должны иметь возможность решать с её помощью следующие задачи: 1. Ввод и редактирование данных о студентах в удобной форме. 2. Просмотр данных о студентах в отсортированном виде. 3. Определение численности контингента в каждой учебной группе, на каждом курсе и в целом на факультете. 4. Подготовка к выводу на печать списков студентов. 5. Вычисление среднего балла каждого студента и вывод списков студентов по группам в алфавитном порядке фамилий или по убыванию среднего балла. 6. Вычисление среднего балла в каждой учебной группе, на каждом курсе и в целом по факультету.
5
7. Определение процента успеваемости в каждой учебной группе, на каждом курсе и в целом по факультету. 8. Просмотр и подготовка к выводу списков задолжников (с указанием количества задолженностей) по курсам и группам. 9. Просмотр и подготовка к выводу списков студентов, претендующих на получение стипендии, с отметкой о том, какая стипендия полагается: обычная или повышенная. 10. Организация поиска по фамилии или части фамилии. 2. Моделирование данных Для проектирования концептуальной схемы (информационной структуры программного обеспечения информационной системы) можно использовать различные модели, в частности модель «сущность – связь». Из моделей типа «сущность – связь» наиболее известна модель П.Чена, или ER – модель (Entity–Relationship). Общим для всех моделей этого типа является использование трех основных конструкций: сущность, связь и атрибут. Сущность (Entity) – собирательное понятие, некоторая абстракция реально существующего объекта, процесса или явления, о котором необходимо хранить информацию. Атрибут – поименованная характеристика сущности, которая принимает значение из некоторого множества значений. В модели атрибут выступает в качестве средства, с помощью которого моделируются свойства сущностей. Связь (Relationship) – средство представления отношения между сущностями. На первом этапе моделирования необходимо из описания предметной области извлечь информацию и выделить сущности. Каждая сущность должна обладать уникальным идентификатором. Каждый экземпляр сущности должен однозначно идентифицироваться и отличаться от 6
всех других экземпляров данного типа сущности. Каждая сущность должна обладать некоторыми свойствами: • каждая сущность должна иметь уникальное имя, и к одному и тому же имени должна всегда применяться одна и та же интерпретация. Одна и та же интерпретация не может применяться к различным именам, если только они не являются псевдонимами; • сущность обладает одним или несколькими атрибутами, которые либо принадлежат сущности, либо наследуются через связь; • сущность обладает одним или несколькими атрибутами, которые однозначно идентифицируют каждый экземпляр сущности; • каждая сущность может обладать любым количеством связей с другими сущностями модели. Обращаясь к приведенному выше описанию предметной области, видно, что можно выделить следующие сущности: студент и специальность. Следующим шагом моделирования является идентификация связей. Связь – поименованная ассоциация между двумя сущностями, значимая для рассматриваемой предметной области. Связь – это ассоциация между сущностями, при которой, как правило, каждый экземпляр одной сущности, называемой родительской сущностью, ассоциирован с произвольным (в том числе нулевым) количеством экземпляров второй сущности, называемой сущностью-потомком, а каждый экземпляр сущности-потомка ассоциирован в точности с одним экземпляром сущности-родителя. Таким образом, экземпляр сущности-потомка может существовать только при существовании сущности родителя. Связи может даваться имя, выражаемое грамматическим оборотом глагола и помещаемое возле линии связи. Имя каждой связи между двумя данными сущностями должно быть уникальным, но имена связей в модели не обязаны быть уникальными.
7
На языке ER - модели концептуальная схема может быть представлена ERD (ER - диаграммой), в которой множество сущностей обозначается прямоугольниками, множество связей – ромбами. Взаимосвязь сущностей описанной выше предметной области можно изобразить ER-диаграммой на рисунке 1. Студент
Получает
Специальность
Рисунок 1. ER–диаграмма Каждый студент обучается только на одной специальности, но одну и ту же специальность получает много студентов. Если предположить, что возможна ситуация, когда один и тот же студент обучается сразу на двух специальностях, то в этом случае будут существовать два экземпляра объекта с разными номерами зачетной книжки. В предлагаемой модели сущность «Специальность» и сущность «Студент» связаны отношением «один – много». Последним шагом моделирования является идентификация атрибутов. Атрибут – любая характеристика сущности, значимая для рассматриваемой предметной области и предназначенная для квалификации, идентификации, классификации, количественной характеристики или выражения состояния сущности. Атрибут представляет тип характеристик или свойств, ассоциированных с множеством реальных или абстрактных объектов (людей, мест, событий, состояний, идей, пар предметов и т.д.). Экземпляр атрибута – это определенная характеристика отдельного элемента множества. Экземпляр атрибута определяется типом характеристики и ее значением, называемым значением атрибута. В ER-модели атрибуты ассоциируются с конкретными сущностями. Таким образом, экземпляр сущности должен обладать единственным определенным значением для ассоциированного атрибута. 8
Атрибут может быть либо обязательным, либо необязательным. Обязательность означает, что атрибут не может принимать неопределенных значений (null values). Атрибут может быть либо описательным (т.е. обычным дескриптором сущности), либо входить в состав уникального идентификатора (первичного ключа). Уникальный идентификатор – это атрибут или совокупность атрибутов и/или связей, предназначенная для уникальной идентификации каждого экземпляра данного типа сущности. В случае полной идентификации каждый экземпляр данного типа сущности полностью идентифицируется своими собственными ключевыми атрибутами, в противном случае в его идентификации участвуют также атрибуты другой сущности-родителя. Каждый атрибут идентифицируется уникальным именем, выражаемым грамматическим оборотом существительного, описывающим представляемую атрибутом характеристику. Атрибуты изображаются в виде списка имен внутри блока ассоциированной сущности, причем каждый атрибут занимает отдельную строку. Атрибуты, определяющие первичный ключ, размещаются наверху списка и выделяются жирным шрифтом. Каждая сущность должна обладать хотя бы одним возможным ключом. Возможный ключ сущности – это один или несколько атрибутов, чьи значения однозначно определяют каждый экземпляр сущности. При существовании нескольких возможных ключей один из них обозначается в качестве первичного ключа, а остальные – как альтернативные ключи. С учетом имеющейся информации дополним построенную ранее диаграмму (рисунок 2), определив для каждой сущности набор атрибутов. Следует отметить, что набор атрибутов для каждой из сущностей может быть расширен, например, в реальной ситуации для каждого студента должны выясняться и сохраняться такие атрибуты, как дата рождения, паспортные данные, адрес и другие. Также отме-
9
тим, что некоторые из атрибутов могут иметь «пустые» значения, т.е. они могут быть необязательными. В предлагаемом проекте будет рассмотрен именно тот набор атрибутов сущности «Студент», который помещен на диаграмме на рисунке 2.
Студент Идентификационный номер студента Курс Номер учебной группы Идентификац. номер специальности Фамилия И.О. Пол Количество сдаваемых экзаменов Оценки, полученные на экзаменах
Специальность Идентификац. номер специальности Код специальности Название специальности Рисунок 2. ER-диаграмма с атрибутами Обсудим значения атрибута «идентификационный номер специальности» сущности «Студент». Значения этого атрибута ассоциируют каждый экземпляр сущности «Студент» с одним из экземпляров сущности «Специальность». Значение атрибута «идентификационный номер специальности» в сущности «Студент» обеспечивает связь сущности «Специальность» с сущностью «Студент» в отношении «один – много». Одну и ту же специальность могут получать много студентов, но информация о специальности заполняется только один раз, что позволяет избежать дублирования информации о специальности при её хранении. 10
3. Представление данных Как правило, при использовании систем управления базами данных в информационных системах, после этапа моделирования данных выполняется преобразование ER–модели в реляционную модель, т.е. сущностям и связям ER–модели ставятся в соответствие отношения, или таблицы. Однако особенностью описываемой реализации информационной системы является то, что она выполняется без использования систем управления базами данных, а создание, ведение и обработка данных выполняется средствами языка и системы программирования Visual Basic. Для хранения данных будут использоваться файлы прямого доступа с записями пользовательского типа. Выполним преобразование атрибутов сущностей «Студент» и «Специальность» в поля соответствующих пользовательских типов TStud (рисунок 3) и TSpec (рисунок 4). Студент
TStud
Идент. номер студента Курс Номер учебной группы Идент. номер спец. Фамилия И.О. Дата рождения Пол Кол-во экзаменов Оценки на экзаменах
Длинное целое ID_Stud Long Kurs Byte Байт Group Byte Байт Spec Integer Целое FIO String Строка длины 20 DataR Date Дата Pol String Строка длины 7 Kol Byte Байт M (5) Byte Байт Рисунок 3. Преобразование сущности «Студент» к типу TStud
Специальность Идент. номер спец. Код специальности Название спец-ности
TSpec Code Number Name
Integer String String
Целое Строка длины 6 Строка длины 100
Рисунок 4. Преобразование сущности «Специальность» к типу TSpec 11
Часть II
Реализация информационной системы «Студенты» 4. Предварительные замечания Рассмотрим все этапы разработки и создания информационной системы на примере ИС «Студенты». Разработка выполняется в среде программирования Visual Basic 2008 Express Edition (платформа .NET), для хранения данных используются файлы прямого доступа, диалог пользователя с информационной системой для ведения и обработки данных организован с использованием средств графического интерфейса. Для получения наилучшего результата, изучение данного материала следует производить последовательно для каждого блока с обязательным тестированием каждой части кода. Т.е. каждая процедура, функция или описание переменной должны быть тщательно изучены и протестированы не только на наличие синтаксических ошибок, зачастую возникающих при наборе текстов, но и на правильность работы приложения в целом. 5. Постановка задачи Описание предметной области было сделано выше, в данном разделе сформулируем техническое задание на создание информационной системы для заданной предметной области. Требуется разработать информационную систему, позволяющую обрабатывать данные о студентах и их успеваемости, представленные в определенном формате. Информационная система должна содержать средства для ввода и редактирования данных, обеспечивать сортировку по одному или нескольким полям данных, обеспечивать возможность поиска по одному или нескольким критериям, выполнять обработку данных и выдавать результаты обработки. Информация о студентах представлена следующими данными: 12
• фамилия, имя и отчество; • дата рождения; • пол; • специальность; • курс; • группа; • количество экзаменов; • оценки, полученные на экзамене. Для хранения и обработки этой информации создать базу данных в виде набора файлов прямого доступа. Необходимо предусмотреть возможность упорядочения данных (сортировку) по следующим полям: - по ФИО; - по курсу; - по группе. Необходимо также иметь возможность просмотра данных в виде, отсортированном по любой совокупности перечисленных полей. Во избежание дублирования данных и экономии памяти для поля «специальность» создать отдельный файл-справочник (файл прямого доступа), используемый для заполнения и просмотра этого поля данных. Критериями поиска для данной информационной системы являются: ФИО, курс, группа. Для заданной предметной области необходимо предусмотреть возможность решения следующих задач: 1. Подготовка к печати списка студентов учебной группы по запросу для одной группы и для всех групп. 2. Вычисление среднего балла для каждого студента; подготовка к выводу результатов в виде, отсортированном по курсу и группе, а в пределах 13
группы предусмотреть одну из возможных сортировок: в алфавитном порядке фамилий или по убыванию среднего балла. 3. Для каждой учебной группы вычисление количества студентов и среднего балла, предусмотреть сортировку по курсу и номеру группы. Для каждой из перечисленных задач предусмотреть возможность просмотра результатов на экране и возможность сохранения их в текстовых файлах для последующего использования. Заметим, что при описании предметной области было определено больше функций информационной системы и, соответственно, задач, которые могут быть решены для выделенных данных. Однако в представляемой конкретной реализации будут решены задачи, перечисленные в техническом задании. 6. Структура проекта ИС «Студенты» реализуется в среде программирования Visual Basic .Net и представляет собой приложение с графическим интерфейсом, состоящее из экранных форм, диалоговых окон и других элементов управления. Главная форма системы содержит строку выпадающих меню, команды которых обеспечивают работу с системой. В процессе работы из главной формы вызываются подчиненные формы для ввода и редактирования исходных данных, просмотра данных и результатов обработки. Программы обработки содержатся в процедурах обработки событий, связанных с объектами форм, а также в пользовательских процедурах и функциях, не связанных с объектами. Проект, из которого строится приложение, кроме форм содержит пользовательские модули, не связанные с формами. Пользовательские модули в данном проекте используются для объявления пользовательских типов, констант и переменных, а также для описания пользовательских процедур, реализующих алгоритмы сортировки. 14
В интерфейсе приложения используются различные элементы управления, их назначение и особенности применения будут описываться по мере того, как будет возникать необходимость в их применении. 7. Создание меню Как уже было сказано, в проекте используется несколько форм для выполнения разных функций; все функции и команды информационной системы вызываются из главной формы с помощью меню. Меню позволяет представить функции приложения в четком и, как правило, удобном для пользователя виде. Зачастую, меню состоит из одного или нескольких заголовков верхнего уровня, каждый заголовок раскрывается в набор строк, каждая из которых представляет собой команду, вызов диалогового окна или подменю. Каждый заголовок меню и каждая строка меню представляет собой объект, обладающий своим набором свойств и событий, включая нужное нам событие Click. Перечислим некоторые, наиболее важные, свойства элементов меню: – Свойство Text определяет текст, отображаемый в меню (заголовок меню или команда меню). Текст может содержать символ «амперсенд» (&) перед символом, который используется в комбинации с клавишей Alt для быстрого вызова. – Свойство Name является обязательным, рекомендуется в имени пункта использовать префикс mnu, а для команд меню использовать окончание Item. Например, для заголовка меню Файл зададим имя mnuFile, а для пункта Выход в меню Файл, т.е. команды Файл / Выход, зададим имя mnuFileExitItem.
15
– Свойство Checked помещает (или удаляет) маркер-галочку рядом с командой. Задавая значение свойства Checked, можно «включать» или «выключать» команды меню во время выполнения программы. – Свойство Enabled по умолчанию имеет значение True. Если заголовок меню или команду необходимо сделать недоступной, заблокировать, то для такой команды нужно сбросить флажок в свойстве Enabled, что будет соответствовать значению False для этого свойства. – Свойство Visible по умолчанию имеет значение True. Это свойство позволяет скрыть элемент меню (однако стоит задуматься о целесообразности этого действия, не лучше ли заблокировать элемент меню до нужного момента). – Свойство Shortcut определяет клавиатурную комбинацию для вызова команды с клавиатуры, обычно используются служебные клавиши Ctrl и Alt в сочетании с другими клавишами (например, F1). Создание приложения начнем с конструирования макета главной формы, которая будет иметь вид, представленный на рисунке 5.
Рисунок 5. Макет главной формы в режиме конструктора Теперь опишем начало процесса создания приложения и создание макета главной формы. Для этого необходимо выполнить следующие действия: 1. Запустить Visual Basic. 16
2. Создать новый проект типа Windows Application, в него сразу загружается форма со стандартным именем Form1. Новый проект имеет имя WindowsApplication1, однако, рекомендуется при создании дать проекту содержательное имя. Переименовать форму, изменив в окне свойств формы свойство Name (в нашем приложении главная форма будет иметь имя frmMenu). Здесь же можно сменить заголовок формы, используя свойство Text (Text = ИС «Студенты»). 3. Сохранить проект в новом каталоге, предназначенном для хранения файлов данного проекта. 4. Для продолжения работы с проектом запустить файл проекта – файл с расширением .sln. Затем, поместить на форму элемент MenuStrip (это будет главное меню формы), а после этого создать все элементы главного меню, вид которого представлен в таблице 1. 5. Сохранить форму в каталоге проекта. 6. В макете формы выбрать пункты и команды меню. Двойной щелчок на команде меню открывает окно кода формы и в нем процедуру-заготовку для обработки события Click. 7. Сохранить проект.
Таблица 1 определяет значения свойств Text, Name и Enabled соответствующих пунктов меню (см. рис. 5). Отступ в поле Text в таблице означает подчиненность данного пункта меню по отношению к ближайшему верхнему пункту, у которого в поле Text нет отступа. Все действия по созданию и структурированию элементов главного меню производятся на главной форме при помощи левой кнопки мыши.
17
Таблица 1 Text
Name
Enabled
&Файл
mnuFile
True
Создать
mnuFileNewItem
True
Открыть
mnuFileOpenItem
True
-
mnuFileSep1
True
Сохранить
mnuFileSaveItem
False
Сохранить как…
mnuFileSaveAsItem
False
-
mnuFileSep2
True
Закрыть
mnuFileCloseItem
False
Удалить
mnuFileDeleteItem
False
-
mnuFileSep3
True
Выход
mnuFileExitItem
True
mnuView
False
Форма
mnuViewFormItem
True
Таблица
mnuViewTableItem
True
mnuSort
False
По курсу
mnuSortKursItem
True
По группе
mnuSortGroupItem
True
По ФИО
mnuSortFIOItem
True
Исходный порядок
mnuSortUnsorted
True
&Поиск
mnuSearch
False
&Отчеты
mnuQuery
False
mnuQueryListItem
True
Все группы
mnuQueryListAllItem
True
Одна группа
mnuQueryListOneItem
True
Вычисление среднего балла
mnuQueryBallItem
True
Кол-во и средний балл в группе
mnuQueryKolItem
True
mnuHelp
True
Прос&мотр
&Сортировка
Список по группе
&? 18
Для продолжения работы с проектом необходимо из каталога, в котором сохранен проект, запустить файл проекта – файл с расширением .sln. Он будет загружен в среду разработки Visual Basic. Попытаемся запустить проект в режиме выполнения. Однако перед этим опишем процедуру обработки события выхода из главной формы разрабатываемой информационной системы. Это команда «Выход» в меню «Файл». Будем обозначать такой выбор следующим образом «Файл Æ Выход». При выборе пункта меню «Файл Æ Выход» приложение должно завершить свою работу, но по правилам хорошего тона приложение обязано сделать запрос на подтверждение закрытия приложения. Для ввода кода необходимо на макете формы в меню «Файл» выбрать команду «Выход» и дважды щелкнуть левой кнопкой мыши, появится заготовка процедуры обработки события Click, состоящая из операторов заголовка и конца процедуры. Остается ввести операторы, являющиеся телом процедуры. Код процедуры, реализующий функцию выбора команды меню «Файл Æ Выход», приведен ниже. Private Sub mnuFileExitItem_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles mnuFileExitItem.Click If (vbYes =MsgBox("Действительно хотите выйти?",vbYesNo)) Then End End If End Sub
Таким образом, при выборе команды меню «Файл Æ Выход» пользователю будет предложено подтвердить выход из приложения и при утвердительном ответе приложение завершит свою работу. Для запуска проекта на выполнение необходимо выбрать в строке меню среды разработки пункт «Debug» и команду «Start Debugging» или нажать клавишу F5. В меню появившейся формы выбрать пункт «Файл», а затем команду «Выход». 19
На этом месте читателю следует на основе прочитанного материала, протестировать проект на работоспособность и корректность работы.
8. Пользовательские модули В проекте используется два стандартных модуля, не связанных ни с какими формами. Один из них, с именем Declar, используется для объявления пользовательских типов, констант и переменных. Другой, с именем Sorting, содержит пользовательские процедуры сортировки данных различных типов. Сначала в проект нужно добавить новый стандартный модуль командой Project / Add Module и дать ему имя, затем ввести программный код. Приведем программный код модуля Decl. ‘ Подключение библиотек ввода и вывода для работы с файлами Imports System.IO Imports System.Text ‘ создание нового пространства имен для ‘ возможности подключения модуля к формам проекта Namespace Decl Module Declar ‘ Объявление пользовательского типа, ‘ описывающего структуру записи для ‘ входных данных – информации о студенте Public Class TStud Public Kurs As Byte ' курс Public Group As Byte ' номер группы Public FIO As String ' фамилия, имя и отчество Public Spec As Integer' учетный номер специальности Public DataR As Date ' дата рождения Public Pol As Byte ' пол Public Kol As Byte ' количество экзаменов Public M(4) As Byte ' полученные оценки Public len As Integer = 1 + 1 + 40 + 4 + 20 + 1 + 1 + 5 ' размер переменной Public Sub writeToFile(ByVal ff As FileStream) Dim writer As BinaryWriter = New BinaryWriter(ff) Dim tempstr As String If FIO.Length > 20 Then 20
FIO = Left(FIO, 20) Else tempstr = New String(" ", 20 - FIO.Length) FIO = FIO + tempstr End If writer.Write(Kurs) writer.Write(Group) Dim temp() As Byte = _ UnicodeEncoding.Unicode.GetBytes(FIO) writer.Write(temp) writer.Write(Spec) temp = _ UnicodeEncoding.Unicode.GetBytes(DataR.ToString("dd.MM.yyyy")) writer.Write(temp) writer.Write(Pol) writer.Write(Kol) writer.Write(M) End Sub Public Sub writeToFile(ByVal ff As FileStream, _ ByVal i As Integer) ff.Seek((i - 1) * len, SeekOrigin.Begin) writeToFile(ff) End Sub Public Sub ReadFromFile(ByVal ff As FileStream, _ ByVal i As Integer) ff.Seek((i - 1) * len, SeekOrigin.Begin) Dim reader As BinaryReader = New BinaryReader(ff) Dim temp() As Byte Kurs = reader.ReadByte() Group = reader.ReadByte() temp = reader.ReadBytes(40) FIO = UnicodeEncoding.Unicode.GetString(temp, 0, 40) Spec = reader.ReadInt32() temp = reader.ReadBytes(20) DataR = _ CType(UnicodeEncoding.Unicode.GetString(temp, 0, 20), Date) Pol = reader.ReadByte() Kol = reader.ReadByte() 21
M = reader.ReadBytes(5) End Sub End Class ' Объявление пользовательского типа, ' описывающего структуру записи для ' входных данных справочника – информации о специальности Public Class TSpec Public Code As Integer ' учетный номер специальности Public Number As String = New String(" ", 6) ' шестизначный код специальности Public Name As String = New String(" ", 100) ' наименование специальности Public len As Integer = 4 + 7 + 200 ' размер переменной Public Sub writeToFile(ByVal ff As FileStream) Dim writer As BinaryWriter = New BinaryWriter(ff) Dim tempstr As String If Number.Length > 6 Then Number = Left(Number, 6) Else tempstr = New String(" ", 6 - Number.Length) Number = Number + tempstr End If If Name.Length > 100 Then Name = Left(Name, 100) Else tempstr = New String(" ", 100 - Name.Length) Name = Name + tempstr End If Dim temp() As Byte = _ UnicodeEncoding.Unicode.GetBytes(Number) writer.Write(Code) writer.Write(temp) temp = UnicodeEncoding.Unicode.GetBytes(Name) writer.Write(temp) End Sub Public Sub writeToFile(ByVal ff As FileStream, _ ByVal i As Integer) ff.Seek((i - 1) * len, SeekOrigin.Begin) writeToFile(ff) End Sub 22
Public Sub ReadFromFile(ByVal ff As FileStream, _ ByVal i As Integer) ff.Seek((i - 1) * len, SeekOrigin.Begin) Dim reader As BinaryReader = New BinaryReader(ff) Dim temp() As Byte Code = reader.ReadInt32() temp = reader.ReadBytes(12) Number = UnicodeEncoding.Unicode.GetString(temp, 0, 12) temp = reader.ReadBytes(200) Name = UnicodeEncoding.Unicode.GetString(temp, 0, 200) End Sub End Class ' Объявление пользовательского типа, ' описывающего структуру записи ' для вычисления среднего балла студента Public Class TBall Public Kurs As Byte ' курс Public Group As Byte ' номер группы Public FIO As String = New String(" ", 20) Public Sb As Single ' средний балл Public len As Integer = 1 + 1 + 40 + 4 ' размер ‘ переменной Public Sub writeToFile(ByVal ff As FileStream) Dim writer As BinaryWriter = New BinaryWriter(ff) Dim tempstr As String If FIO.Length > 20 Then FIO = Left(FIO, 20) Else tempstr = New String(" ", 20 - FIO.Length) FIO = FIO + tempstr End If Dim temp() As Byte = _ UnicodeEncoding.Unicode.GetBytes(FIO) writer.Write(Kurs) writer.Write(Group) writer.Write(temp) writer.Write(Sb) End Sub Public Sub writeToFile(ByVal ff As FileStream, _ ByVal i As Integer) ff.Seek((i - 1) * len, SeekOrigin.Begin) 23
writeToFile(ff) End Sub Public Sub ReadFromFile(ByVal ff As FileStream, _ ByVal i As Integer) ff.Seek((i - 1) * len, SeekOrigin.Begin) Dim reader As BinaryReader = New BinaryReader(ff) Kurs = reader.ReadByte() Group = reader.ReadByte() Dim temp() As Byte = reader.ReadBytes(40) FIO = UnicodeEncoding.Unicode.GetString(temp, 0, 40) Sb = reader.ReadSingle() End Sub End Class ' Объявление констант и переменных, ' используемых в модулях форм, ' пользовательских процедурах и функциях Public Const Nmax As Integer = 100 Public Const PathUntitled As String = "с:\untitled.dan" Public Const PathTemp As String = "c:\$$$$.tmp" Public Public Public Public Public Public
Path As String PathSpec As String Path1 As String Path2 As String FName As String richName As String
Public Public Public Public Public
f1 As F2 As F3 As F4 As F1_sp
FileStream FileStream FileStream Integer As FileStream
Public N As Integer Public NSpec As Integer Public Pos As Integer Public Ind(Nmax) As Integer Public Del(Nmax) As Integer Public Button As MsgBoxResult ‘ функция, которая закрывает все открытые рабочие файлы Public Sub CloseAllFiles() If Not (f1 Is Nothing) Then f1.Close() 24
If Not (F1_sp Is Nothing) Then F1_sp.Close() If Not (F2 Is Nothing) Then F2.Close() If Not (F3 Is Nothing) Then F3.Close() End Sub End Module End Namespace
После описания пользовательского модуля Declar необходимо изменить функцию обработки события выбора пункта меню «Выход». Измененная функция имеет вид: Private Sub mnuFileExitItem_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles mnuFileExitItem.Click If (vbYes =MsgBox("Действительно хотите выйти?",vbYesNo)) Then Decl.CloseAllFiles() End End If End Sub
9. Работа с файлами 9.1. Использование диалогового окна Windows
Для открытия и сохранения файлов в проекте используется стандартное диалоговое окно Windows, которое создается с использованием элементов управления соответственно OpenFileDialog и SaveFileDialog. Нанесем на главную форму с меню (frmMenu) объекты для работы с диалоговыми окнами для открытия и сохранения файлов, можно оставить им те имена, которые им присвоит система (OpenFileDialog1 и SaveFileDialog1), а можем дать другие имена. Заметим, что во время выполнения приложения данные элементы управления не видны. Окна диалога имеют определенные свойства и методы. Используемые нами в данной реализации свойства и методы для обоих типов объектов совершенно одинаковы, поэтому остановимся на свойствах только одного элемента управления при пояснении кодов процедур обработки событий для открытия и сохране25
ния файлов. Что касается методов, то для обоих элементов будет использован метод ShowDialog() для получения имен диска, папки и файла соответственно для существующего файла и нового, сохраняемого файла. Приведем фрагмент кода для вызова диалогового окна Windows для открытия и сохранения файлов. ‘ запрос имени файла базы данных With OpenFileDialog1 .filename = "" .Filter = "Все файлы (*.*)|*.*|" + _ "текстовые файлы(*.txt)|*.txt|" + _ "файлы исх. данных(*.dan)|*.dan|" + _ "файлы результата (*.rez)|*.rez" .FilterIndex = 3 ‘ метод, используемый для открытия файла .ShowDalog() Decl.Path = .filename End With
В приведенном выше коде используются следующие свойства окна диалога: Filter – для списка возможных типов файлов, показываемых в поле ввода со списком для типа файла; FilterIndex – для вывода в окне для типа файла того типа, который в списке Filter имеет номер, задаваемый свойством FilterIndex. Имя выбранного файла помещается в окно для имени файла, а полное имя файла становится значением свойства FileName. Чтобы не использовать длинные имена, полное имя файла записывается в переменную Path модуля Declar. При открытии файла появится диалоговое окно (рисунок 6), в котором необходимо выбрать файл, который нужно открыть.
26
Рисунок 6. Диалоговое окно открытия файла 9.2. Создание файлов базы данных
Создание файлов базы данных выполняется при выборе команды меню «Файл Æ Создать». Данной команде соответствует процедура обработки события mnuFileNewItem_Click, которая с помощью метода Show должна выводить на экран ту форму, при помощи которой будет производиться ввод новых и редактирование существующих записей данных. Вначале, для простоты работы с загрузкой новых записей базы данных, основной рабочий файл с данными будем создавать в корневом каталоге какоголибо диска, например, диска «c:» с полным именем «c:\untitled.dan», а после выхода из формы для ввода и редактирования пользователю будет предложено сохранить данные в некотором файле. Для этих целей используем элемент управления «SaveFileDialog1» главной формы frmMenu. При этом фактически будет про-
27
исходить переименование файла «c:\untitled.dan» и присвоение ему имени, введенного пользователем в окне диалога. Запишем процедуру обработки события выбора команды меню «Файл Æ Создать». Private Sub mnuFileNewItem_Click(ByVal sender As System.Object,_ ByVal e As System.EventArgs) Handles mnuFileNewItem.Click Dim i As Integer ‘ закрываются все открытые до этого времени файлы Decl.CloseAllFiles() ' открывается основной файл "c:\untitled.dan" Decl.Path = PathUntitled f1 = New FileStream(Decl.Path, FileMode.Create) ' и файл-справочник "c:\untitled.spe" PathSpec = Mid(Decl.Path, 1, Len(Decl.Path) - 4) +".spe" F1_sp = New FileStream(Decl.PathSpec, FileMode.Create) ' количество записей в основной таблице базы данных ' равно нулю N = 0 ' инициализация индексного массива Ind, который будет ' использоваться для сортировки данных, и массива Del ' для пометки удаляемых записей For i = 1 To Nmax Ind(i) = i Del(i) = 0 Next i ' основная форма становится неактивной Me.Enabled = False ' загрузка формы для ввода записей файла frmInputEdit frmInputEdit.Text = "Файл: " + Decl.Path frmInputEdit.Show() frmInputEdit.ShowNewZap() ' пользовательская процедура ' ввода новой записи End Sub
Поскольку, в данном коде программы участвует новая форма для ввода и редактирования данных, то необходимо добавить ее в проект, выполнив команду «Project/Add Windows Form», и дать новой форме имя frmInputEdit. 28
Теперь наш проект содержит две формы и нужно указать, какая из них является главной, т.е. запускается первой. Для этого нужно выполнить команду «Project/*имя проекта* Properties…» и в открывшемся диалоговом окне в поле ввода со списком StartUp Form выбрать в качестве стартового объекта форму frmMenu. Несмотря на то, что форма для ввода не содержит пока никаких элементов управления, запустите проект для проверки процедуры создания файлов и взаимодействия форм. Не забывайте регулярно сохранять проект. 10. Ввод и редактирование данных 10.1. Проектирование макета формы для ввода и редактирования данных
Для ввода и редактирования информации о студентах, сохраняемых в виде записей в файле данных, используется форма, которой дано имя frmInputEdit, она является подчиненной формой для главной формы с меню. Макет формы frmInputEdit показан на рисунке 7. При помощи выносок указаны имена помещенных на форму элементов управления – компонентов (объектов) формы. Остановимся на тех элементах управления, которые используются на макете данной формы. Надписи (тип Label) – это объекты с именами, имеющими префикс lbl, используются как подписи элементов формы. Из этих подписей ясно, какие элементы для каких полей данных используются. Текстовые поля (тип TextBox) – это объекты с именами, имеющими префикс txt: txtFIO – для поля «Фамилия И.О.»; txtM1, txtM2, txtM3, txtM4, txtM5 – группа объектов, которая используется для ввода оценок. Командные кнопки (тип Button) – это объекты с префиксами cmd, нажатие на кнопку (событие Click), как правило, вызывает определенное действие, связанное с кнопкой.
29
Элементы управления DateTimePicker, позволяющие достаточно удобно вводить даты: txtDR – для поля «Дата рождения».
Рисунок 7. Форма для ввода и редактирования записей файла данных Комбинированные поля (тип ComboBox) – они сочетают возможности текстового поля и списка, который может быть раскрывающимся; этот тип элемента управления используется для значения поля «Пол» (ComPol), список значений этого поля задается свойством Items. Свойства элемента «ComPol» зададим следующим образом: Items
= “мужской” “женский”
Sorted = True
30
Для заполнения поля «Специальность» (ComSpec) используются данные из файла-справочника, список значений для этого поля формируется во время выполнения программы (код будет приведен ниже). Флажок (тип CheckBox) – используется для того, чтобы отметить записи, которые будут удалены из файла: Check1. Элемент управления NumericUpDown, который исполняет роль счетчика: txtKurs – для поля «Курс»; txtGroup – для поля «Группа»; txtKol – для поля «Количество оценок». Элемент «NumericUpDown» используется для изменения, как в сторону увеличения, так и в сторону уменьшения с заданным шагом, в нашем случае равным 1 (свойство «increment») значений полей «Курс», «Группа» и «Количество оценок». Свойства «minimum» и «maximum» задают нижнюю и верхнюю границы изменения счетчика соответственно. Для перемещения по записям файла кроме командных кнопок используется также вертикальная полоса прокрутки, которая имеет имя «vscrZap», значения некоторых ее свойств определены следующим образом: minimum = 1 maxmum = 100 SmallChange = 1 LargeChange = 2 Value = 1
В поле «специальность» при заполнении и просмотре записей основного файла базы данных должны подставляться данные из файла-справочника, для чего были составлены процедуры и функции, приведенные в следующем разделе. В этом месте необходимо нанести на форму все элементы управления, задать из свойства и протестировать форму в автономном режиме, чтобы сравнить результат проектирования с приведенным образцом. Для автономного тестирования сделайте стартовым объектом форму frmInputEdit.
31
10.2. Программное обеспечение формы для ввода и редактирования Программное обеспечение формы – это процедуры обработки событий, связанные с элементами управления формы, и пользовательские процедуры и функции, которые непосредственно с элементами не связаны. 10.2.1. Пользовательские процедуры и функции
Сначала опишем пользовательские процедуры и функции для работы с файлом-справочником. С их помощью выполняется связывание основного файла базы данных с файлом-справочником для поля «специальность». Пользовательская процедура LoadSprav формирует список, который будет показан в поле ввода комбинированного списка ComSpec, т.е. во время выполнения программы формируется значение свойства Items элемента ComSpec, в список Items загружаются все записи файла-справочника. Private Sub LoadSprav() Dim Sp As TSpec = New TSpec() Dim i As Integer ' очищаем список элемента ComSpec и добавляем в него ' пустую строку ComSpec.Items.Clear() ComSpec.Items.Add(" ") ' определяем количество записей в справочнике Decl.NSpec = F1_sp.Length \ Sp.len ' если есть хотя бы одна запись, If NSpec 0 Then ' то поочередно считываем их из файла и ' добавляем в список элемента ComSpec For i = 1 To NSpec Sp.ReadFromFile(F1_sp, i) ComSpec.Items.Add(Trim(Sp.Number) + _ " - " + Trim(Sp.Name)) Next i End If End Sub
32
Пользовательская функция FindSpecCode, возвращает учетный номер специальности, находящейся в справочнике на указанной позиции, передаваемой в качестве параметра. Private Function FindSpecCode(ByVal number As Integer) As _ Integer Dim Sp As TSpec = New TSpec() Sp.ReadFromFile(F1_sp, number) FindSpecCode = Sp.code End Function
Пользовательская функция FindSpecIndex получает на вход учетный номер специальности и возвращает номер записи в файле-справочнике, соответствующие этой специальности. Public Function FindSpecIndex(ByVal code As Integer) _ As Integer Dim Sp As TSpec = New TSpec() Dim i As Integer Dim temp As Integer ' определяется количество записей в справочнике NSpec = F1_sp.Length \ Sp.len temp = 0 For i = 1 To NSpec ' поочередно считываются записи из файла Sp.ReadFromFile(F1_sp, i) ' и если учетный номер записи совпадает, If (Sp.Code = code) Then ' то запоминается номер его позиции temp = i ' и осуществляется выход из цикла проверки Exit For End If Next ' возвращается запомненный номер позиции FindSpecIndex = temp End Function
Описав все эти процедуры и функции, мы обеспечили взаимодействие данных основного файла с данными в файле-справочнике, т.е. обеспечили связь между файлами. 33
Для ввода значений полей новой записи файла данных должна быть активизирована форма, подготовленная для ввода данных, т.е. в ней некоторые поля должны быть очищены, а некоторые должны быть заполнены значениями по умолчанию. Из кнопок, обеспечивающих перемещение, активной должна быть только кнопка «Добавить». Все эти действия предусмотрены в пользовательской процедуре ShowNewZap. Опишем код этой процедуры. Public Sub ShowNewZap() ' загружаем Файл-справочник специальности, выполняя ' пользовательскую процедуру Call LoadSprav() ' полоса прокрутки становится неактивной vscrZap.Enabled = False ' кнопка «Добавить» становится активной cmdAdd.Enabled = True ' кнопки «Вперед», «Конец», «Изменить» и «Обновить» ' становятся неактивными cmdForward.Enabled = False cmdBottom.Enabled = False cmdPack.Enabled = False cmdEdit.Enabled = False ' начальное заполнение элементов, ' соответствующих полям записей txtFIO.Text = "" txtKurs.Value = 1 txtGroup.Value = 1 txtKol.Value = 2 ComPol.Text = ComPol.Items(1) ComSpec.SelectedIndex = 0 txtDR.Value = Now ' в начале предоставлена возможность ввода двух оценок ' пользователь может увеличить их количество до пяти txtM1.Visible = True txtM2.Visible = True txtM3.Visible = False txtM4.Visible = False txtM5.Visible = False ' очистка значений элементов txtM, ' которые могли остаться после ввода предыдущей записи txtM1.Text = "" txtM2.Text = "" 34
txtM3.Text = "" txtM4.Text = "" txtM5.Text = "" lblZap.Text = "Новая запись" txtFIO.Focus() Check1.Visible = False End Sub
После ввода всех значений новой записи, для внесения ее в базу данных нужно нажать на кнопку «Добавить». После окончания ввода данных необходимо закрыть форму frmInputEdit. При этом все введенные данные должны сохраниться, поэтому при закрытии формы необходимо запросить у пользователя имя файла, в который должно производиться сохранение. Но поскольку эту форму можно закрыть несколькими способами (например, нажав на кнопку «Выход» или кнопку
в правом верхнем
углу формы), поэтому необходимо обработать все события, обеспечивающие закрытие формы. А поскольку выходу из формы соответствует одна и та же последовательность действий, то оформим ее в виде пользовательской процедуры ExitFromInputEdit. Sub ExitFromInputEdit() Dim PathOld As String Dim PathOldSpec As String ' если путь к основному файлу равен "c:\untitled.dan", т.е. ' форма открыта в режиме создания, frmMenu.Text = frmMenu.Text + " В.А." If LCase(Decl.Path) = PathUntitled Then ' то выводится запрос пользователю о сохранении базы Button = MsgBox("Сохранить файл?", vbQuestion +_ vbYesNoCancel, "Сохранение файла") If Button = vbYes Then ' если пользователь подтверждает сохранение, то ' следует запрос имени файла, в который будет ' сохранена база данных PathOld = Decl.Path PathSpec = Mid(Decl.Path, 1, Len(Decl.Path) - 4) + _ ".spe" PathOldSpec = PathSpec With frmMenu.SaveFileDialog1 .FileName = "" 35
.Filter = "Все файлы (*.*)|*.*|" + _ "текстовые файлы(*.txt)|*.txt|" + _ "файлы исх. данных(*.dan) |*.dan|" + _ "файлы результата (*.rez)|*.rez" .FilterIndex = 3 .ShowDialog() Decl.Path = .FileName If Decl.Path "" Then PathSpec = Mid(Decl.Path, 1, Len(Decl.Path) - 4) _ + ".spe" End If End With ' если путь не пуст, т.е. пользователь указал ' имя нового файла, If Decl.Path "" Then ' то файлы закрывается и переименовываются CloseAllFiles() If Not (Dir(Decl.Path) = "") Then Kill(Decl.Path) Kill(PathSpec) End If Rename(PathOld, Decl.Path) Rename(PathOldSpec, Decl.PathSpec) ' затем открываются файлы с ' новыми именами f1 = New FileStream(Decl.Path, FileMode.OpenOrCreate) F1_sp = New FileStream(Decl.PathSpec, _ FileMode.OpenOrCreate) ' изменение заголовка формы Me.Text = "Файл: " + Decl.Path ' все пункты меню становятся активными frmMenu.mnuView.Enabled = True frmMenu.mnuQuery.Enabled = True frmMenu.mnuSort.Enabled = True frmMenu.mnuFileSaveItem.Enabled = True frmMenu.mnuFileSaveAsItem.Enabled = True frmMenu.mnuFileCloseItem.Enabled = True frmMenu.mnuFileDeleteItem.Enabled = True frmMenu.mnuSearch.Enabled = True ' форма закрывается Me.Hide() ' форма frmMenu становится активной frmMenu.Show() 36
frmMenu.Enabled = True frmMenu.Focus() Else ' иначе считается, что нажата кнопка ' «Отмена» Decl.Path = PathOld PathSpec = PathOldSpec Button = vbCancel End If Else ' если пользователь на вопрос о сохранении ' файла нажал кнопку «Нет» If Button = vbNo Then Pos = 0 N = 0 ' файлы закрываются и удаляются CloseAllFiles() Kill(Decl.Path) Kill(PathSpec) ' пункты меню, которые были неактивными в ' начале работы программы, снова ' становятся неактивными frmMenu.mnuFileSaveItem.Enabled = False frmMenu.mnuFileSaveAsItem.Enabled = False frmMenu.mnuFileCloseItem.Enabled = False frmMenu.mnuFileDeleteItem.Enabled = False frmMenu.mnuView.Enabled = False frmMenu.mnuSort.Enabled = False frmMenu.mnuQuery.Enabled = False frmMenu.mnuSearch.Enabled = False ' форма закрывается Me.Hide() ' форма frmMenu становится активной frmMenu.Show() frmMenu.Enabled = True frmMenu.Focus() End If End If Else ' в случае если форма была открыта в режиме просмотра, ' она закрывается Me.Hide() ' форма frmMenu становится активной frmMenu.Show() frmMenu.Enabled = True frmMenu.Focus() End If 37
End Sub
Теперь, опишем процедуры обработки событий нажатия на кнопку «Выход» или кнопку в правом верхнем углу формы. Обработка события закрытия формы по нажатию кнопки «Выход»: Private Sub cmdExit_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles cmdExit.Click ‘ вызов процедуры закрытия формы ExitFromInputEdit() End Sub
Обработка события закрытия формы по нажатию кнопки
:
Private Sub frmInputEdit_FormClosing(ByVal sender As _ System.Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles _ MyBase.FormClosing Call ExitFromInputEdit() ‘ в том случае, если нажата кнопка «Отмена», то с формы ' выход не происходит If Button = vbCancel Then e.Cancel = True End Sub
Хотя подготовлено еще не все программное обеспечение формы, можно запустить проект, но пока не сохранять данные, так как наверняка будут сделаны ошибки, которые необходимо исправить. Для работы с данными, а именно для вывода в форму полей очередной просматриваемой записи, предусмотрена процедура ShowZap. Она выполняет загрузку элементов формы значениями полей текущей записи (записи с номером i). Ниже приводится код этой процедуры, параметром ее является номер записи в файле. Public Sub ShowZap(ByVal i As Integer) Dim R As TStud = New TStud() ' номер записи не должен быть нулем! If i 0 Then ' максимальное значение полосы прокрутки равно ' количеству записей vscrZap.Maximum = N + 1 vscrZap.Enabled = True Check1.Checked = False 38
txtM1.Text = "" txtM1.Visible = txtM2.Text = "" txtM2.Visible = txtM3.Text = "" txtM3.Visible = txtM4.Text = "" txtM4.Visible = txtM5.Text = "" txtM5.Visible =
False False False False False
' производится чтение записи и заполнение элементов ' формы, соответствующих полям записи R.ReadFromFile(f1, Ind(i)) txtFIO.Text = R.FIO ComPol.SelectedIndex = R.Pol txtKurs.Text = CStr(R.Kurs) txtGroup.Text = CStr(R.Group) txtKol.Text = CStr(R.Kol) ComSpec.SelectedIndex = FindSpecIndex(R.Spec) txtDR.Value = R.DataR If R.Kol > 0 Then txtM1.Visible = True txtM1.Text = CStr(R.M(0)) End If If R.Kol > 1 Then txtM2.Visible = True txtM2.Text = CStr(R.M(1)) End If If R.Kol > 2 Then txtM3.Visible = True txtM3.Text = CStr(R.M(2)) End If If R.Kol > 3 Then txtM4.Visible = True txtM4.Text = CStr(R.M(3)) End If If R.Kol > 4 Then txtM5.Visible = True txtM5.Text = CStr(R.M(4)) End If ' вывод номера записи и отметки об ее удалении lblZap.Text = CStr(i) + "из" + CStr(N) Check1.Visible = True Check1.Checked = Del(Ind(i)) ' значение полосы прокрутки полагается равным ' номеру записи 39
vscrZap.Value = i End If End Sub
10.2.2. Процедуры обработки событий
Для работы пользователя с файлом справочником на форму были помещены кнопки cmdAddSpec, cmdEditSpec и cmdDelSpec, соответственно, для добавления, изменения и удаления записей в файле справочнике. Опишем процедуры обработки событий нажатия на эти кнопки. Процедура обработки события для добавления записи в файл-справочник. Private Sub cmdAddSpec_Click(ByVal sender As System.Object,_ ByVal e As System.EventArgs) Handles cmdAddSpec.Click Dim TempStr As String Dim Sp As TSpec = New TSpec() Dim tempIndex As Integer ' текущая форма становится неактивной Me.Enabled = False ' до тех пор пока пользователь не введет число, происходит ' запрос учетного номера новой специальности TempStr = InputBox("Введите учетный номер специальности", _ "Ввод данных") Do While Not (IsNumeric(TempStr)) If (TempStr = "") Then Me.Enabled = True Exit Sub End If MsgBox("Ошибка ввода", vbOKOnly + vbExclamation, _ "Ошибка") TempStr = InputBox("Введите учетный номер" + _ " специальности", "Ввод данных") Loop ' учетный номер записывается в структуру Sp Sp.Code = CByte(TempStr) ' запрос кода специальности TempStr = InputBox("Введите код сциальности (6 цифр)", _ "Ввод данных") ' код записывается в структуру Sp Sp.Number = TempStr ' запрос названия новой специальности TempStr = InputBox("Введите название специальности", _ "Ввод данных") 40
' название записывается в структуру Sp Sp.Name = TempStr ' проверяется попытка повторного использования учетного ' номера специальности в файле справочнике tempIndex = FindSpecIndex(Sp.Code) ' если такой номер уже есть, If 0 tempIndex Then ' то выводится сообщение о возможности изменения данных If (vbYes = MsgBox("Такой учетный номер уже есть! " _ + " Перезаписать?", vbYesNo + vbExclamation, _ "Предупреждение")) Then ' изменение данных записи Sp.writeToFile(F1_sp, tempIndex) End If Else ' добавление новой записи в справочник Sp.writeToFile(F1_sp, NSpec + 1) End If ' вызов пользовательской процедуры загрузки записей ' справочника в элемент ComSpec Call LoadSprav() ' текущая форма становится активной Me.Enabled = True End Sub
Процедура обработки события изменения записи в файле-справочнике. Private Sub cmdEditSpec_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles cmdEditSpec.Click Dim Sp As TSpec = New TSpec() Dim i As Integer ' текущая форма становится неактивной Me.Enabled = False ' чтение текущей записи из файла-справочника i = ComSpec.SelectedIndex Sp.ReadFromFile(F1_sp, i) ' запрос на изменение кода специальности If (vbNo = MsgBox("Код специальности оставить без" + _ " изменения?", vbYesNo + vbExclamation, "Вопрос:")) Then Sp.Number = InputBox("Введите новый код " + _ "специальности (6 цифр)", "Ввод данных") End If ' запрос на изменение кода специальности If (vbNo = MsgBox("Название специальности оставить " + _ 41
"без изменения?", vbYesNo + vbExclamation, _ "Вопрос:")) Then Sp.Name = InputBox("Введите новое название " + _ "специальности", "Ввод данных") End If ' запись обновленных данных Sp.writeToFile(F1_sp, i) ' вызов пользовательской процедуры загрузки записей ' справочника в элемент ComSpec Call LoadSprav() ' текущая форма становится активной Me.Enabled = True End Sub
Процедура обработки события удаления записи из файла-справочника. Private Sub cmdDelSpec_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles cmdDelSpec.Click Dim i As Integer Dim j As Integer Dim ListIndex As Integer Dim Sp As TSpec = New TSpec() Dim R As TStud = New TStud() Dim TempFile As FileStream ' текущая форма становится неактивной Me.Enabled = False ' сообщение о возможности удаления записей ' из основного файла If (vbNo = MsgBox("Внимание!!! Все данные, " + _ "содержащие удаляемую информацию будут уничтожены!" + _ " Продолжить?", vbYesNo + vbExclamation, _ "Предупреждение:")) Then ' если была нажата кнопка «Нет», то выход из процедуры ' и текущая форма становится неактивной Me.Enabled = True Exit Sub End If ' отметка на удаление всех записей основного файла базы, ' в которых содержится информация об удаляемой спец-ности ListIndex = ComSpec.SelectedIndex If ListIndex 0 Then Sp.ReadFromFile(F1_sp, ListIndex) 42
For i = 1 To N R.ReadFromFile(f1, i) If R.Spec = Sp.Code Then Del(i) = 1 End If Next i End If ' запись во временный файл всех данных, за исключением ' данных об удаляемой специальности TempFile = New FileStream (PathTemp, FileMode.Create) j = 1 For i = 1 To NSpec Sp.ReadFromFile(F1_sp, i) If i ListIndex Then Sp.writeToFile(TempFile, j) j = j + 1 End If Next i F1_sp.Close() TempFile.Close() ' перезапись измененного файла-справочника Kill(PathSpec) Rename(PathTemp, PathSpec) ' загрузка обновленного файла-справочника F1_sp = New FileStream(PathSpec, FileMode.Open) ' вызов пользовательской процедуры загрузки записей ' справочника в элемент ComSpec Call LoadSprav() ' текущая форма становится активной Me.Enabled = True ' вызов процедуры удаления отмеченных записей ' основного файла данных cmdPack_Click(sender, e) End Sub
В этом месте снова запустите проект для проверки взаимодействия основного файла базы данных с файлом-справочником, но сохранять записи также пока
43
невозможно, так как не реализована кнопка «Добавить». Не забывайте регулярно сохранять проект. Процедура обработки события нажатия на кнопку «Добавить» обеспечивает добавление записи в файл базы данных и подготовку к вводу полей новой записи. Private Sub cmdAdd_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles cmdAdd.Click Dim R As TStud = New TStud() Dim Sp As TSpec = New TSpec() ' заполнение полей записи R.FIO = txtFIO.Text R.Kurs = CByte(txtKurs.Text) R.Group = CByte(txtGroup.Text) R.Kol = CByte(txtKol.Text) R.Pol = CByte(ComPol.SelectedIndex) R.Spec = FindSpecCode(ComSpec.SelectedIndex) R.DataR = txtDR.Value ' проверка корректности введенных данных в поле ' специальности If R.Spec = 0 Then MsgBox("Неверное значение поля специальность!") Exit Sub End If ' проверка непустоты полей оценок If (txtM1.Text = "") Or (txtM2.Text = "") Or _ (R.Kol > 2) And (txtM3.Text = "") Or _ (R.Kol > 3) And (txtM4.Text = "") Or _ (R.Kol > 4) And (txtM5.Text = "") Then MsgBox("Введены не все оценки!") Exit Sub End If R.M(0) = CByte(txtM1.Text) R.M(1) = CByte(txtM2.Text) If R.Kol > 2 Then R.M(2) = CByte(txtM3.Text) If R.Kol > 3 Then R.M(3) = CByte(txtM4.Text) If R.Kol > 4 Then R.M(4) = CByte(txtM5.Text) ' позиция передвигается, Pos = N + 1 ' количество записей увеличивается N = N + 1 ' запись вносится в файл R.writeToFile(f1, Pos) 44
' кнопки «Начало» и «Назад» становятся активными cmdTop.Enabled = True cmdBackUp.Enabled = True cmdExit.Focus() ' вызов процедуры для заполнения новой записи Call ShowNewZap() End Sub
А теперь попытайтесь заполнить файл данными (1–2 записи) и закрыть форму frmInputEdit с сохранением файлов. Заметим, что количество видимых на форме элементов txtM«i», предназначенных для ввода оценок прямо зависит от содержимого элемента txtKol. Таким образом, скрытие лишних видимых элементов и показ нужных невидимых элементов txtM«i» должны зависеть от изменения содержимого txtKol, т.е. далее необходимо обработать событие изменения содержимого элемента txtKol. Приведем код процедуры обработки этого события. Private Sub txtKol_ValueChanged(ByVal sender As System.Object,_ ByVal e As System.EventArgs) Handles txtKol.ValueChanged Dim i As Integer = txtKol.Value ' все элементы, соответствующие оценкам скрываются txtM1.Visible = False txtM2.Visible = False txtM3.Visible = False txtM4.Visible = False txtM5.Visible = False ' открываются первые несколько элементов ' их количество зависит от значения элемента txtKol If i > 0 Then txtM1.Visible = True If i > 1 Then txtM2.Visible = True If i > 2 Then txtM3.Visible = True If i > 3 Then txtM4.Visible = True If i > 4 Then txtM5.Visible = True End Sub
Таким образом, при изменении содержимого элемента txtKol все элементы txtM, соответствующие оценкам скрываются, затем открывается столько элементов, сколько указано в содержимом элемента txtKol. 45
Кроме этого необходимо предусмотреть проверку корректности вводимых данных в поля, соответствующие оценкам. Это можно сделать с помощью следующей обработки события изменения содержимого элементов txtM. Private Sub txtM_TextChanged(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles txtM5.TextChanged, _ txtM4.TextChanged, txtM3.TextChanged, txtM2.TextChanged, _ txtM1.TextChanged Dim txtM As TextBox = sender ' если содержимое элемента txtM(Index) является числом If IsNumeric(txtM.Text) = True Then ' и не находится в пределах от 2 до 5, If CByte(txtM.Text) < 2 Or CByte(txtM.Text) > 5 Then ' пользователь получает предупреждение об ошибке MsgBox("Неверная оценка", vbExclamation + vbOKOnly, _ "Ошибка") End If ElseIf txtM.Text "" Then ' если содержимое не является цифрой, ' пользователь получает предупреждение об ошибке MsgBox("Оценка должна являться цифрой!", vbOKOnly + _ vbExclamation, "Ошибка") End If End Sub
В этом месте нужно выполнить полное тестирование формы frmInputEdit, для этого необходимо заполнить файл данными (4–5 записей) и сохранить его. Кроме ввода новых данных форма frmInputEdit должна позволять просматривать и редактировать существующие данные, для этого необходимо описать перемещение по записям нашей базы данных. Для перемещения по записям будем использовать кнопки «Начало» (cmdTop), «Конец» (cmdBottom), «Назад» (cmdBackUp) и «Вперед» (cmdForward). Опишем обработку событий нажатия для каждой кнопки: Private Sub cmdTop_Click(ByVal sender As System.Object,_ ByVal e As System.EventArgs) Handles cmdTop.Click ' вывод первой записи данных Pos = 1 Call ShowZap(Pos) ' кнопки перемещения по записям становятся активными frmMenu.Text = frmMenu.Text + " Авт" 46
cmdTop.Enabled = True cmdBottom.Enabled = True cmdForward.Enabled = True cmdBackUp.Enabled = True ' кнопки «Изменить» и «Обновить» становятся активными cmdEdit.Enabled = True cmdPack.Enabled = True ' кнопка «Добавить» становится неактивной frmMenu.Text = frmMenu.Text + "ор: " cmdAdd.Enabled = False ' фокус передается на кнопку «Выход» cmdExit.Focus() End Sub Private Sub cmdBottom_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles cmdBottom.Click ' вывод последней записи данных Pos = N Call ShowZap(Pos) ' кнопки перемещения по записям становятся активными frmMenu.Text = frmMenu.Text + "дов " cmdTop.Enabled = True cmdBottom.Enabled = True cmdForward.Enabled = True cmdBackUp.Enabled = True ' кнопки «Изменить» и «Обновить» становятся активными cmdEdit.Enabled = True cmdPack.Enabled = True ' кнопка «Добавить» становится неактивной cmdAdd.Enabled = False ' фокус передается на кнопку «Выход» cmdExit.Focus() End Sub Private Sub cmdBackUp_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles cmdBackUp.Click ' переход на предыдущую позицию Pos = Pos - 1 ' все кнопки становятся активными frmMenu.Text = frmMenu.Text + "рохо" cmdTop.Enabled = True cmdBottom.Enabled = True 47
cmdForward.Enabled = True cmdEdit.Enabled = True cmdPack.Enabled = True ' если запись первая, то кнопка «Назад» ' становится неактивной If Pos 2 Then If txtM3.Text "" Then R.M(2) = CByte(txtM3.Text) Else MsgBox("Оценка №3 должна являться цифрой!", vbOKOnly + _ vbExclamation, "Ошибка") Exit Sub End If End If If R.Kol > 3 Then If txtM4.Text "" Then R.M(3) = CByte(txtM4.Text) Else MsgBox("Оценка №4 должна являться цифрой!", vbOKOnly + _ vbExclamation, "Ошибка") Exit Sub End If End If If R.Kol > 4 Then If txtM5.Text "" Then R.M(4) = CByte(txtM5.Text) Else MsgBox("Оценка №5 должна являться цифрой!", vbOKOnly + _ vbExclamation, "Ошибка") Exit Sub End If End If ' запись вносится в файл на то место, ' на котором находилась старая запись R.writeToFile(f1, Pos) MsgBox("Изменение выполнено успешно!") End Sub 55
С файлом исходных данных связан массив Del, который по умолчанию заполнен нулями. Удаляемым записям соответствует значение, равное единице. Для того чтобы удалить некоторые записи, необходимо отметить их на форме frmInputEdit, используя флажок-переключатель Check1, затем нажать на кнопку «Обновить». Для элемента Check1 и кнопки «Обновить» процедуры обработки события Click – щелчка левой кнопки мыши на этих объектах имеют вид: Private Sub Check1_CheckedChanged(ByVal sender As _ System.Object, ByVal e As System.EventArgs) _ Handles Check1.CheckedChanged ' отмечается на удаление запись с номером Pos If Check1.Checked Then Del(Ind(Pos)) = 1 Else Del(Ind(Pos)) = 0 End If End Sub Private Sub cmdPack_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles cmdPack.Click Dim i As Integer Dim j As Integer = 1 Dim R As TStud = New TStud() ' открывается вспомогательный файл F2 = New FileStream(PathTemp, FileMode.Create) ' из основного файла во вспомогательный переписываются ' записи, которые не нужно удалять For i = 1 To N R.ReadFromFile(f1, i) If Del(i) = 0 Then R.writeToFile(F2, j) j = j + 1 End If Next i ' закрываются файлы, и происходит сохранение ' вспомогательного файла под именем основного ' файла базы данных f1.Close() F2.Close() Kill(Decl.Path) Rename(PathTemp, Decl.Path) 56
' открывается новый основной (рабочий файл) базы f1 = New FileStream(Decl.Path, FileMode.Open) N = f1.Length \ R.len ' возвращение исходных значений массивам Ind и Del For i = 1 To Nmax Ind(i) = i Del(i) = 0 Next i ' вывод первой записи данных Pos = 1 If N = 0 Then Call ShowNewZap() Else Call ShowZap(Pos) End If MsgBox("Обновление выполнено успешно!") End Sub
Таким образом, для удаления записей используется временный файл, куда переносятся все те записи, которые не должны быть удалены (для них значение Del(i) равно нулю), затем исходный файл удаляется, а временный файл переименовывается. Новый этап тестирования проекта с целью проверки его работоспособности при добавленных возможностях.
11.4. Сохранение и удаление файлов Мы описали возможности ввода, просмотра и редактирования данных. Добавим несколько процедур, обрабатывающие события выбора следующих пунктов меню: «Файл Æ Сохранить как», «Файл Æ Закрыть» и «Файл Æ Удалить». При выборе данного пункта меню «Файл Æ Сохранить как» основной файл базы (файл с данными) должен быть сохранен под новым именем, а затем, должно быть открыто это новое сохранение. Private Sub mnuFileSaveAsItem_Click(ByVal sender As _ System.Object, ByVal e As System.EventArgs) _ 57
Dim Dim Dim Dim
Handles mnuFileSaveAsItem.Click R As TStud = New TStud() Sp As TSpec = New TSpec() PathOld As String PathOldSpec As String
' запоминаются имена основного файла базы данных и ' Файла-справочника PathOld = Decl.Path PathSpec = Mid(Decl.Path, 1, Len(Decl.Path) - 4) + ".spe" PathOldSpec = PathSpec ' запрос имени нового файла базы With Me.SaveFileDialog1 .FileName = "" .Filter = "Все файлы (*.*)|*.*|" + _ "Текстовые файлы. (*.txt)|*.txt|" + _ "Файлы исходных данных (*.dan)|*.dan|" + _ "Файлы результатов (*.rez)|*.rez" .FilterIndex = 3 .ShowDialog() Decl.Path = .FileName If Decl.Path "" Then PathSpec = Mid(Decl.Path, 1, Len(Decl.Path) - 4) + _ ".spe" End If End With ' если имя нового основного файла пусто, то рабочие файлы ' остаются прежними и производится выход из процедуры If Decl.Path = "" Then Decl.Path = PathOld PathSpec = PathOldSpec Exit Sub End If ' иначе файлы закрывается и производится копирование ' рабочих файлов Close() If Not (Dir(Decl.Path) = "") Then Kill(Decl.Path) Kill(PathSpec) End If FileCopy(PathOld, Decl.Path) FileCopy(PathOldSpec, PathSpec) ' после копирования открываются новые файлы f1 = New FileStream(Decl.Path, FileMode.Open) F1_sp = New FileStream(PathSpec, FileMode.Open) 58
frmInputEdit.Text = "Файл: " + Decl.Path End Sub
При выборе пункта меню «Файл Æ Закрыть» все открытые файлы должны быть закрыты и все пункты меню, недоступные в начале работы снова должны снова стать недоступными. Private Sub mnuFileCloseItem_Click(ByVal sender As _ System.Object, ByVal e As System.EventArgs) _ Handles mnuFileCloseItem.Click ' закрываются все файлы, открытые в процессе работы CloseAllFiles() ' пункты меню, которые были неактивными в начале ' работы программы, снова становятся неактивными mnuFileSaveItem.Enabled = False mnuFileSaveAsItem.Enabled = False mnuFileCloseItem.Enabled = False mnuFileDeleteItem.Enabled = False mnuView.Enabled = False mnuSort.Enabled = False mnuQuery.Enabled = False mnuSearch.Enabled = False End Sub
При выборе пункта меню «Файл Æ Удалить» основной файл базы (файл с данными) должен быть удален, и все пункты меню, недоступные в начале работы снова должны снова стать недоступными. Private Sub mnuFileDeleteItem_Click(ByVal sender As _ System.Object, ByVal e As System.EventArgs) _ Handles mnuFileDeleteItem.Click Dim Button As Integer ' запрос подтверждения на удаление Button = MsgBox("Действительно удалить?", vbYesNo + _ vbQuestion, "Удаление файла") ' в случае подтверждения вызывается процедура закрытия ' и удаляется основной файл базы данных If Button = vbYes Then Call mnuFileCloseItem_Click(sender, e) Kill(Decl.Path) Kill(PathSpec) End If End Sub 59
На этом месте снова необходимо протестировать проект на работоспособность и корректность работы. Особое внимание следует уделить взаимодействию различных компонентов приложения.
12. Сортировка данных Сортировка данных в данной информационной системе производится при помощи индексного массива Ind, описанного в модуле Declar. В этом случае результатом сортировки будет не отсортированный файл, а вспомогательный массив номеров (индексов) Ind, показывающий в каком порядке следует выбирать записи файла, чтобы они образовывали отсортированную последовательность. Алгоритмы сортировки опишем в стандартном модуле, который необходимо добавить в проект под именем Sorting. Для сортировки данных будем использовать алгоритм сортировки обменом (метод «пузырьковой» сортировки). Приведем код процедуры сортировки по числовым полям. Public Sub Sort(Key() As Single, N As Integer, Ind() As Integer) Dim i As Integer Dim j As Integer Dim k As Integer For j = 1 To N - 1 For i = 1 To N - j If Key(Ind(i)) > Key(Ind(i + 1)) Then k = Ind(i) Ind(i) = Ind(i + 1) Ind(i + 1) = k End If Next i Next j End Sub
Перед обращением к этой процедуре необходимо инициализировать массив Ind и занести элементы ключевого поля сортируемого файла во вспомогательный массив Key. Благодаря описанию массива Key как вещественного в него можно записывать числовые данные любого типа: как Single, так и Integer и Byte. 60
Кроме того, эта процедура пригодна и для сортировки по убыванию ключа; для этого достаточно при инициализации массива Key умножить значения ключевых полей на минус единицу. Для процедур сортировки по строковым полям и полям типа даты приведем только заголовки, так как код тела этих процедур полностью совпадает с кодом тела процедуры сортировки по числовым полям. Заголовок процедуры сортировки по строковым полям имеет вид: Public Sub SortStr(Key() As String , N As Integer, _ Ind() As Integer)
Заголовок процедуры сортировки по полям типа даты имеет вид: Public Sub SortDate(Key() As Date, N As Integer, _ Ind() As Integer)
Опишем теперь работу с указанными процедурами сортировки. Т.е. опишем процедуры обработки событий выбора пунктов меню «Сортировка Æ По курсу», «Сортировка Æ По группе», «Сортировка Æ По ФИО» и «Сортировка Æ Исходный порядок». При этом необходимо предусмотреть корректное выполнение вложенных сортировок. Выбор пункта меню «Сортировка Æ По курсу» Private Sub mnuSortKursItem_Click(ByVal sender As _ System.Object, ByVal e As System.EventArgs) _ Handles mnuSortKursItem.Click Dim i As Integer Dim KeyK(Nmax) As Single Dim R As TStud = New TStud() ' подготовка к сортировке For i = 1 To N R.ReadFromFile(f1, i) KeyK(i) = R.Kurs Next i ' вызов процедуры сортировки Sort(KeyK, N, Ind) End Sub 61
Выбор пункта меню «Сортировка Æ По группе» Private Sub mnuSortGroupItem_Click(ByVal sender As _ System.Object, ByVal e As System.EventArgs) _ Handles mnuSortGroupItem.Click Dim i As Integer Dim KeyK(Nmax) As Single Dim R As TStud = New TStud() ' подготовка к сортировке For i = 1 To N R.ReadFromFile(f1, i) KeyK(i) = R.Group Next i ' вызов процедуры сортировки Sort(KeyK, N, Ind) End Sub
Выбор пункта меню «Сортировка Æ По ФИО» Private Sub mnuSortFIOItem_Click(ByVal sender As System.Object,_ ByVal e As System.EventArgs) Handles mnuSortFIOItem.Click Dim i As Integer Dim KeyK(Nmax) As String Dim R As TStud = New TStud() ' подготовка к сортировке For i = 1 To N R.ReadFromFile(f1, i) KeyK(i) = Trim(R.FIO) Next i ' вызов процедуры сортировки SortStr(KeyK, N, Ind) End Sub
Выбор пункта меню «Сортировка Æ Исходный порядок» Private Sub mnuSortUnsorted_Click(ByVal sender As _ System.Object, ByVal e As System.EventArgs) _ Handles mnuSortUnsorted.Click Dim i As Integer ' возвращение исходных значений массивам Ind и Del For i = 1 To Nmax Ind(i) = i Del(i) = 0 62
Next i End Sub
Теперь, при выборе любого из этих пунктов меню, записи в исходном файле будут выводиться в порядке, определяемом значениями индексного массива Ind, как при просмотре в виде формы, так и при просмотре в виде таблицы. В команды меню «Сортировка» вынесены сортировки по одному полю (ключу) данных. А если необходимо выполнить сортировку по набору ключей, т.е. представить данные о студентах так, что сначала идут записи по возрастанию номера курса, записи в пределах одного курса – по возрастанию номера группы, а записи в пределах одной группы – по фамилиям в алфавитном порядке. Здесь мы встречаемся с сортировкой по набору ключей. Для выполнения вложенной сортировки (сортировки по набору ключей) можно воспользоваться описанными выше универсальными процедурами без каких-либо их модификаций. Это связано с тем, что используемый в процедурах алгоритм сортировки является устойчивым, т.е. в процессе сортировки относительное расположение элементов с одинаковыми ключами не изменяется. Допустим, нужно выполнить сортировку по набору ключей: «Курс», «Группа», «Фамилия И.О.». Предположим, что исходный файл отсортирован по последнему ключу (в нашем случае по полю «Фамилия И.О.). Сортируя полученный файл по предшествующему ключу (по полю «Группа»), мы можем быть уверены, что в пределах одного значения данного ключа записи сохраняют упорядоченность по последнему ключу (это следует из свойства устойчивости алгоритма). Т.е. данные будут отсортированы по полю «Группа», а внутри каждой группы – по полю «Фамилия И.О.». Повторяя процесс сортировки по всем ключам от последнего к первому (т.е. в порядке, обратном требуемому), мы получим файл, отсортированный по набору ключей. Опять же напомним, что файл на самом деле не сортируется, а сортируется индексный массив. Подчеркнем, что в описанном
63
процессе индексный массив, являющийся результатом сортировки по некоторому ключу, является исходным для сортировки по очередному ключу. Таким образом, процедуры модуля Sorting позволяют осуществлять сортировку записей файла по нескольким подчиненным ключам. Если необходимо выполнить вложенную сортировку по другой совокупности полей (например, в других задачах), то нужно вернуться к исходному порядку, а затем выполнить последовательные сортировки в порядке, обратном порядку рассматриваемой совокупности полей. Заметим, однако, что при удалении записи в отсортированных данных (это возможно только в режиме просмотра в виде формы) порядок следования записей нарушается, возвращаясь к исходному виду. На этом месте снова следует протестировать проект на работоспособность и корректность работы. Проверка правильности работы сортировок может быть осуществлена при помощи просмотра данных в виде таблицы. Особое внимание уделить корректности правки и удаления отсортированных записей. 13. Задачи, решаемые в информационной системе В техническом задании запланировано решение следующих задач: 1. Вывод списков групп по запросам для одной группы и для всех групп. 2. Вычисление среднего балла для каждого студента. 3. Для каждой учебной группы вычисление количества студентов и среднего балла в группе. Результаты решения задач оформляются в виде отчетов, вывод которых вызывается командами пункта меню «Отчеты» в строке меню главной формы. Заметим, что первый пункт – это фактически два различных отчета: списки всех групп и список одной, указанной группы. Из-за этого мы при создании пунктов меню поставили в соответствие этому отчету два пункта меню: «Отчеты Æ Списки групп Æ Все группы» и «Отчеты Æ Списки групп Æ Одна группа». 64
Отчеты будем генерировать в виде текстовых файлов. Для их просмотра используем компонент RichTextBox. Поместим этот элемент на форму frmMenu, задав ему следующие свойства: Системное имя Шрифт Запрет редактирования Вертикальная полоса прокрутки Объект невидим
Name = RichTextBox1 Font = Courier New ReadOnly = True ScrollBars = Vertical Visible = False
Кроме этого элемента поместим на форму еще и кнопку (Name = cmdSaveOtch, Visible = False), при нажатии которой будем сохранять просматри-
ваемый отчет. Таким образом, макет формы frmMenu , доработанный для вывода результатов имеет вид, представленный на рисунке 8. По нажатию кнопки «Сохранить отчет» текущий отчет (т.е. отчет, который просматривается в данный момент) должен быть сохранен. Таким образом, поскольку мы генерируем отчеты в текстовый файл с заранее заданным именем, то по нажатию кнопки должна быть сделана копия файла с отчетом.
Рисунок 8. Макет главной формы в режиме конструктора Приведем код процедуры обработки события нажатия на кнопку «Сохранить отчет». 65
Private Sub cmdSaveOtch_Click(ByVal sender As System.Object,_ ByVal e As System.EventArgs) Handles cmdSaveOtch.Click ' запрос имени файла для сохранения With SaveFileDialog1 .FileName = "" .Filter = "Все файлы (*.*)|*.*|" + _ "Текстовые файлы. (*.txt)|*.txt|" + _ "Файлы исходных данных (*.dan)|*.dan|" + _ "Файлы результатов (*.rez)|*.rez" .FilterIndex = 2 .ShowDialog() End With ' если имя файла не пусто и не равно имени ' просматриваемого файла, If SaveFileDialog1.FileName "" And _ richName SaveFileDialog1.FileName Then ' то производится копирование просматриваемого файла, FileCopy(richName, SaveFileDialog1.FileName) ' его удаление Kill(richName) ' и вывод в RichTextBox1 нового файла richName = SaveFileDialog1.FileName RichTextBox1.LoadFile(richName, _ RichTextBoxStreamType.PlainText) ' сообщение об успешном завершении сохранения отчета MsgBox("Отчет сохранен", vbOKOnly + vbInformation, _ "Сохранение") End If End Sub
13.1. Вывод списков групп Вывод списков групп реализован в процедуре обработки события выбора пункта меню «Отчеты Æ Списки групп Æ Все группы». Прежде чем привести код этой процедуры, обсудим алгоритм, реализованный в ней. Идея алгоритма состоит в следующем: сначала файл с данными о студентах должен быть «отсортирован» по курсу, группе и фамилии; результатом чего станет индексный массив Ind, который позволит просматривать записи файла в отсортированном виде именно таким образом. Такая упорядоченность записей файла фактически представляет его разбитым на группы записей, самая внутрен66
няя группа имеет одинаковые значения для курса и группы и упорядочена в алфавитном порядке фамилий. Поэтому в самом внешнем цикле перебираются курсы, во вложенном в него цикле перебираются номера групп, и в следующем по уровню вложенности цикле (для одинаковых значений курса и группы во всех записях) выбираются и выводятся в текстовый файл фамилии студентов. Код процедуры снабжен подробными комментариями. Private Sub mnuQueryListAllItem_Click(ByVal sender As _ System.Object, ByVal e As System.EventArgs) _ Handles mnuQueryListAllItem.Click Dim i As Integer Dim Ind(Nmax) As Integer Dim NK As Byte Dim NG As Byte Dim NPP As Integer Dim KeyK(Nmax) As Single Dim KeyG(Nmax) As Single Dim KeyFIO(Nmax) As String Dim R As TStud = New TStud() ' открывается временный вспомогательный файл otch.ist, ' находящийся в той же папке, что и работающий проект Path2 = CurDir() + "\otch.txt" F4 = FreeFile() FileOpen(F4, Path2, OpenMode.Output) ' подготовка данных для сортировки по курсу, группе и ФИО For i = 1 To N R.ReadFromFile(f1, i) KeyK(i) = R.Kurs KeyG(i) = R.Group KeyFIO(i) = R.FIO Ind(i) = i Next i ' сортировка данных Call SortStr(KeyFIO, N, Ind) Call Sort(KeyG, N, Ind) Call Sort(KeyK, N, Ind) ' чтение первой записи в отсортированном списке i = 1 R.ReadFromFile(f1, Ind(i)) Do While i