понедельник, 26 декабря 2011 г.

Урок 26. Обнуление базы данных


Предыдущий урок был посвящен вопросам подключения к базе данных, расположенной на сменном носителе.
Сегодня я хочу показать, как с использованием компонента TADOCommand вносить изменения в базу данных. Мои не случайные читатели, которые следят за ходом событий, спросят: "Что это ты, Александр Львович, все вокруг да около ходишь? Когда же, наконец, мы двинемся дальше?" А скоро, очень скоро мы продолжим разработку основного алгоритма программы (кстати, Вы не забыли, как она называется и для чего, собственно, она предназначена? :-) ). Я стараюсь сохранить интригу. Да и не интересно будет рассказывать о разных мелочах настройки интерфейса и т.п., когда основной материал останется позади. Так что: терпение, мой друг, терпение...
А сейчас нужно добавить новый Action, создав новую категорию Service. Его свойства показаны на рисунке.
Затем положить на форму компонент TADOCommand с вкладки dbGo панели компонентов. Этот компонент как раз предназначен для того, чтобы выполнять запросы к базе данных, не требующие ответов, т.е. такие, которые видоизменяют информацию в базе данных, но не возвращают пользователю никакого набора строк. В нашем случае, например, по окончании календарного года старые записи становятся не очень-то актуальны. Хранить их нужно (мало ли что?!), а вот таскать везде с собой - вовсе не обязательно. Поэтому и существует необходимость почистить наше хранилище.
И последнее, что нужно сделать - написать обработчик события onExecute, не забыв при этом дать объявление процедуре и "подложить" этот Action под один из пунктов меню "Настройки".
Итак, текст обработчика события onExecute:
procedure TMainFrm.Service_ClearExecute(Sender: TObject);
begin
// Предупреждающий диалог с оператором
MyMessenger.MessageString:='Будет произведена полная очистка базы данных'+CR+'Выполнить?';
MyMessenger.TitleString:='Обслуживание...';
MyMessenger.MessageType:=mtConfirmation;
MyMessenger.Buttons:=[mbYes]+[mbNo];
If MyMessenger.ShowMessage=6
Then
Begin
// Выбор БД и подключение (случае, если необходимые данные не вводились и работа программы только началась)
SetDisk;
MainConnecting;
TableActive(True);
// Подготовка
With ADOCommand1 do
begin
CommandTimeout := 45;
Connection := ADOConnection1;
Prepared := True;
MyMessenger.MessageString:='Можно вычислить остатки на начало или обнулить их'
+CR
+CR
+'Вычислить и запомнить остатки на начало?';
If MyMessenger.ShowMessage=6
Then
Begin                              // Вычисление остатков (заготовка на будущее)
// Подсчет оборотной ведомости и перенос вычисленных остатков - зона нашего ближайшего развития
End
Else                               // Обнуление остатков
begin
// Очистка остатков (значения полей Ost_D и Ost_K таблицы  Accounts будут установлены в ноль)
CommandText:= 'UPDATE Accounts SET Accounts.Ost_D = 0, Accounts.Ost_K = 0';
Execute;
end;
// Очистка главной таблицы (удаление всех записей из таблицы)
CommandText:= 'DELETE FROM Main';
Execute;
// Очистка списка МО (удаление всех записей из таблицы)
CommandText:= 'DELETE FROM MOs';
Execute;// Очистка оборотки (разберем на одном из следующих уроков)
//CommandText:= 'DELETE FROM Oborot';
//Execute;


// Сообщение оператору
MyMessenger.MessageString:='Операция завершена успешно';
MyMessenger.MessageType:=mtInformation;
MyMessenger.Buttons:=[mbOK];
MyMessenger.ShowMessage;
end;
End;
end;

Как видно из текста этой несложной процедуры: один компонент TADOCommand выполняет последовательно несколько запросов: один - на изменение данных в таблице Accouts, остальные - на удаление записей из таблиц. Почему записи не следует удалять из таблицы Accounts? Очень просто: эта таблица содержит план счетов, говоря бухгалтерским языком. Или попросту - условно постоянный набор признаков, который можно использовать из года в год.


В следующем уроке я расскажу о подготовке главной формы к полноценному отображению данных с помощью DBGridEh, а так же поговорим о полях LookUp.


воскресенье, 27 ноября 2011 г.

Урок 25. База данных потерялась?


В предыдущем уроке я остановился на том, что показал один из способов наведения порядка в отображаемой интерфейсом программы таблице, т.е. показал один из способов сортировки получаемых из базы данных записей.
Сегодня мне хотелось бы остановиться вот на каком вопросе: испытания собственной программы я произвожу сам, поэтому сам нахожу ее удобные и не очень места. И вот одним из таких подводных камней мне видится смена буквы съемного носителя. Да-да, мой уважаемый читатель, программу можно носить с собой везде, разместив ее на USB, скажем, носителе, поскольку программа, как Вы уже знаете, использует для собственных настроек файл ini, что многие могут посчитать старомодным, а не реестр операционной системы. Использование реестра ОС - модно и современно, но требует написания инсталлятора. Оставим этот вопрос на будущее. А сейчас попробуем разобраться с такой бедой: сменный диск, подключенный к одному компьютеру, имеет, скажем, букву “Е”, а на другом он становится “F”. Но в строке подключения к базе данных, если кто обратил внимание:
ConnectionString=Provider=Microsoft.Jet.OLEDB.4.0;Data Source=X:\Lessons\...\Les 25\Расходы\Rashod.mdb;...
четко прописана буква диска в абсолютном пути (Data Source=X:).
Как быть? Править вручную файл ini перед использованием программы - занятие скучное и неблагодарное.
Тогда предлагаю использовать следующий алгоритм:
- прочитать строку подключения из файла ini,
- попробовать подсоединиться к базе данных,
- при неудачном подсоединении дать возможность оператору выбрать расположение базы данных, используя стандартное окно диалога,
- записать в файл ini новую строку подключения.
Последую примеру героя М.М. Жванецкого: “Если вопросов нет, я начну”.
Добавьте на форму MainFrm компонент TOpenDialog с палитры компонентов, с вкладки “Dialogs”.
Создайте новую процедуру следующего содержания:
procedure TMainFrm.SetDisk(); // Установка диска с БД
Var
ini: TIniFile;
ConnectionString: String;
MyDir: String;
begin
MyDir:=GetCurrentDir; // Запомнить в переменную путь к текущему директорию
OpenDialog1.Filter:='Файлы базы данных|*.mdb'; // Фильтр для диалога выбора файла (показывать только файлы баз данных Access
// Выбор файла
If not OpenDialog1.Execute // Открытие окна диалога
then // Отмена выбора
begin
exit; // Выход из подпрограммы
end;
ini:=TiniFile.Create(MyDir + '\Rashod.ini'); // Подключение к файлу настроек
MySelect.MySel_Dir:=ExtractFilePath(OpenDialog1.FileName); // Извлечение абсолютного пути к выбранному файлу
ConnectionString:='Provider=Microsoft.Jet.OLEDB.4.0;Data Source='+MySelect.MySel_Dir+'Rashod.mdb;Persist Security Info=False'; // Конструирование строки подключения
Ini.WriteString('General','ConnectionString',ConnectionString); // Запись строки подключения в файл настроек
// Ini.WriteString('General','TempConnectionString',ConnectionString);
ini.Free; // Освобождение памяти
SetCurrentDir(MyDir); // Возврат программы к текущему каталогу
end;
Один тонкий момент есть в этой процедуре, который мне хотелось бы пояснить. Дело в том, что компонент TOpenDialog, устанавливает текущий директорий по понятиям операционной системы. Поэтому, если Вы выбрали какую-то папку с базой данных, отличную от рабочей папки программы, то работа программы нарушится. Следовательно, необходимо запомнить в строковой переменной положение указателя на текущую папку до начала всего действия:
MyDir:=GetCurrentDir;,
а по окончании действия, вернуть его на место:
SetCurrentDir(MyDir);
Не забудьте дать объявление новой процедуры в секции Private описания интерфейса:
procedure SetDisk();
Где должна сработать новая процедура? Конечно же, при создании формы, при обработке события onCreate:
If Not TestConnection
then
SetDisk();
Теперь для тестирования измените вручную путь к базе данных, прописанный в файле ini.
Удачи.

Что же дальше?
На следующем уроке я планирую рассказать, как производится очистка базы данных, удаление всех записей из таблиц или присвоение отдельным полям стартовых (нулевых) значений.


четверг, 17 ноября 2011 г.

Урок 24. Сортировка записей в таблице



Не в альтернативу предыдущему уроку, где я довольно долго рассказывал о том, как создать форму для таблицы справочника, данный урок получился очень коротким. Случилось это вот как. Намереваясь составить очередной урок, я открыл проект на том этапе, на котором оставил его в последний раз, запустил программу и увидел очевидное неудобство: при выборе мемориального ордера самый нужный - последний как раз и оказался в конце списка. Это очевидное неудобство: оператор всегда вводит записи в текущий месяц, редко обращаясь к записям предыдущих периодов. Я решил сразу исправить эту ошибку интерфейса, а начинающим программистам - еще раз напомнить: создавая интерфейс всегда ставьте себя на место будущего пользователя, задумывайтесь почаще: что ему будет удобно, а что - нет.
Все инструкции к этому уроку можно свести к следующему:
в свойство IndexFieldNames компонента ADOTableMOs впишите ID DESC.
Вот так все просто?
Нет, не просто. Это - лишь один из нескольких способов задать сортировку.  Другие мы рассмотрим позже.
PS Постфикс DESC задает обратный (от "я" до "а") порядок сортировки.
Наглядный видео урок Вы можете посмотреть или скачать.


Что же дальше?
А в следующем уроке я предлагаю решить вопрос: а как должна вести себя программа, если база данных хранится на съемном носителе, а при подсоединении к компьютеру, съемному диску присвоена неизвестная доселе буква?

среда, 19 октября 2011 г.

Урок 23. Очень важная последовательность действий



Сначала я назвал этот урок “Форма выбора мемориального ордера”. Но, поскольку, ниже поведу речь о добавлении в проект новой формы, подключении ее к источнику данных, о том, как организовать выбор в форме и передать выбранные данные дальше, как их оценить и обработать, показать оператору и т.п. О многом из того, что будет представлено в этом уроке, я уже говорил ранее. Более того: в будущем действия, о которых пойдет здесь речь, станут привычной рутиной. Поэтому мне хотелось бы очень подробно остановиться на этом вопросе, чтобы потом только ссылаться на него не повторяясь. Всегда интереснее обсуждать что-то новое, чем талдычить об одном и том же, правда? Поэтому, дорогой читатель, наберись терпения. Урок будет длинный, более 20 пунктов...
Цель: добавить в проект новую форму, в которой должна отображаться какая либо справочная таблица, чтобы пользователь мог выбрать одну из записей, предварительно добавив ее, если нужной записи нет.
Озадачимся вопросом: что нужно знать программе, т.е. что должен пользователь уже определить, прежде, чем начнет вводить записи о расходах в наш реестр?
Валюту? Правильно, но пока я принял валюту по умолчанию - рубли.
Пользователь должен определиться, как он будет группировать информацию. Опытный бухгалтер на крупном предприятии обычно не ограничивается распределением информации только по месяцам года, он дробит ее на более мелкие группы. Но, я думаю, что для бытовых целей, достаточно группировать записи о расходах по месяцам. Поэтому в дальнейшем под мемориальным ордером будем понимать часть записей базы данных, относящихся к определенному месяцу. Отсюда и перечень мемориальных ордеров будет состоять из 12 строк по числу месяцев в году.
Нам понадобится очень многое из предыдущих уроков, поэтому, если Вы с ними не ознакомились по какой либо причине, то лучше это сделать сейчас.
Приступим (подробности - см. в соответствующем уроку видео материале).
1. Добавьте на форму Main компоненты TADOTable и TDataSource с постфиксами MOs в названиях. Назначьте ADOTableMOs свойство Connection=ADOConnection1 (ранее выполнено подключение к базе данных). Определите свойство TableName=MOs (имя таблицы базы данных “Расход”, содержащей перечень мемориальных ордеров). Компоненту DataSourceMOs назначьте DataSet=ADOTableMOs (этот компонент будет иметь связь с таблицей, чтобы выполнить визуализацию - вывод на экран - данных, хранящихся в ней). Установите свойство AutoEdit этого компонента в False.
2. Добавьте  в проект новую форму с именем Name=MOs_Frm, назвав модуль формы Unit=MOs. Задайте некоторые свойства формы: заголовок - Caption=’Список мемориальных ордеров’, KeyPreview=True, чтобы форма реагировала на нажатие клавишей (понадобится в дальнейшем для обработки нажатия функциональных кнопок и таких как “Escape” или “Enter”), Position=poScreenCenter, чтобы форма выводилась в центре экрана.
3. Добавьте пару панелей из библиотеки компонентов ProDelphi с соответствующими опциями Align (одна панель должна располагаться внизу, вторая - быть клиентом).
4. С вкладки компонентов библиотеки EhLib, установленной ранее, добавьте компонент сетку - TDBGridEh на панель-клиент.
5. Установите на нижнюю панель разработанный ранее навигатор (вы можете использовать навигатор, поставляемый с средой разработки, работать он будет аналогично, отличия лишь во внешнем виде).
6. Пропишите взаимно формы в Uses в разделе implementation (в модуле формы Main: Uses MOs; в модуле формы MOs: Uses Main). Сие описание необходимо, чтобы можно было в одной форме использовать переменные и процедуры с глобальной областью видимости из другой формы.
7. Аналогично тому, как мы это делали в уроке 4, создайте в ActionList действие, назвав его Name=Data_SelectMO, с подсказкой - Hint=’Выбор мемориального ордера’, отнеся его к категории Category=Data с подписью Caption=’&Начать ввод’. Надеюсь, что Вы понимаете, для чего в Caption использован знак амперсанда. Создайте заготовку (достаточно ввести один комментарий, чтобы среда разработки не удалила пустышку при сохранении) OnExecute.
8. Свяжите пункт меню (Начать ввод, Name=N_Edit) с созданным действием Data_SelectMO, аналогично тому, как это делалось в уроке 4.
9. Добавьте  поля в ADOTableMOs (правой кнопкой по компоненту - Fields Editor, правой кнопкой по окну редактора полей - Add All Fields...)
10. Уберите форму из автодоступных в модуле проекта (как это сделать, см урок 12).
11. Создайте процедуру StatusBarUpdate, дав ее объявление в области Private, т.к. нет необходимости вызывать ее из других форм, отображающую в статус баре выбор оператора с помощью следующих команд:
StatusBar1.Panels[1].Text:='Валюта: '+ MySelect.MySel_ValName;
StatusBar1.Panels[2].Text:='МО: '+ MySelect.MySel_MOName;
StatusBar1.Panels[3].Text:='Счет: '+MySelect.MySel_AccName;
StatusBar1.Panels[4].Text:='Каталог: '+ MySelect.MySel_Dir;
12. Создайте Private процедуру ShowMOs, позволяющую выводить форму MOs_Frm на экран:
Application.CreateForm(TMOs_Frm, MOs_Frm); // Создание формы
MOs_Frm.ShowMoDal;                                          // Вывод формы на экран
MOs_Frm.Free;                                                      // Освобождение памяти
StatusBarUpdate;                                                   // Визуализация выбора
13. Добавьте на форму MOs_Frm кнопку выбора (экземпляр компонента TButton). Для этой кнопки достаточно изменить только подпись Caption=’Выбрать’, поскольку в дальнейшем я планирую заменить ее компонентом собственного производства. Не стоит создавать действие в ActionList, так как действие этой кнопки не будет продублировано в меню или где-то еще.
14. Напишите обработчик нажатия кнопки выбора:
// Выбор сделан
MyPSelect();
где MyPSelect() - Private процедура:
procedure TMOs_Frm.MyPSelect();
begin
// Обработка выбора
case ModalResult of
1:
begin
if MainFrm.ADOTableMOs.FieldByName('ID').Value>0
then
Begin
MySelect.MySel_IDMO:=MainFrm.ADOTableMOs.FieldByName('ID').Value;
MySelect.MySel_MOName:=MainFrm.ADOTableMOs.FieldByName('Name').Text;
MySelect.MySel_Mes:=MainFrm.ADOTableMOs.FieldByName('Mes').Value;
Close;
end;
end;
2:
Begin
Close;
end;
end;
end;
Обратите внимание, что переменная MySelect типа запись объявлена в форме Main!
15. Добавьте в обработчик создания формы операторы присвоения начальных значений переменным:
procedure TMOs_Frm.FormCreate(Sender: TObject);
begin
MySelect.MySel_IDMO:=0;
MySelect.MySel_MOName:='';
MySelect.MySel_Mes:=0;
end;
16. Чтобы сделать таблицу доступной (активировать), создайте в главной форме Private подпрограмму:
procedure TMainFrm.TableActive(p: boolean);
begin
// Активация (деактивация таблиц)
ADOTableMOs.Active:=p;
end;
которая так же может быть использована в целях отключения таблиц (подобных объектов в будущем будет несколько).
17. В конструкторе формы MOs_Frm назначте источник для DBGridEh: DataSource=MainFrm.DataSourceMOs. Обратите внимание, что средства подключения к базе данных и компоненты для работы с ней расположены на главной форме.
18. Теперь пришло время “облагородить” сетку, задав удобоваримые заголовки колонок (столбцов). Для этого кликните два раза по изображению грида на форме MOs_Frm, а затем по кнопке Add All Fields - добавить все поля. Перейдите в инспектор объектов. Выбирая последовательно поля, в свойстве Title-Caption задайте названия. Закройте окно Editing.DBGridEh.Colums.
19. Для сетки, отображающей таблицу из трех полей (колонок), удобно установить свойство AutoFitColWidths в значение True, чтобы они все уместились на экране, чтобы избежать появления горизонтальной полосы прокрутки.
20. Назначьте источник для навигатора аналогично тому, как Вы сделали это для DBGridEh.
21. Создайте обработчик события onKeyDown формы MOs_Frm дважды щелкнув по одноименной строке в инспекторе объектов:
procedure TMOs_Frm.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
// Обработка нажатия клавиши
case Key of            // Start Case
VK_ESCAPE:
begin
if MessageDlg('Закрыть?',mtConfirmation, [mbYes]+[mbNo], 0)=6
then
Close;
end;
else
End;                   // End case
end;
Это нужно для того, чтобы пользователь мог закрыть форму клавишей Esc.
Вывод на экран окна диалога с вопросом “Закрыть?” можно оформить с помощью находящегося на главной форме компонента MyMessenger.
22. Для пользователей, которые не привыкли кликать по кнопкам, а предпочитают делать выбор двойным щелчком, оформите обработку соответствующего события для DBGridEh:
procedure TMOs_Frm.DBGridEh1DblClick(Sender: TObject);
begin
// Реакция на двойной клик
ModalResult:=1;
MyPSelect();
end;
23. И последнее, что необходимо сделать - обработать выбор после закрытия вспомогательной формы (если ничего не выбрано, зачем показывать пустую таблицу пользователю?!) и научить программу показывать необходимые в данную минуту пункты меню:
procedure TMainFrm.Data_SelectMOExecute(Sender: TObject);
begin
// Обработка выбора МО
// Инфо на статусбаре
StatusBarUpdate;
// Вывод формы выбора МО
ShowMOs;
// Обработка выбора
if MySelect.MySel_IDMO=0
then
PrDPanel2.Visible:=False
else
Begin
PrDPanel2.Visible:=True;
end;
// Открыть недоступные пункты меню
N12_Ins.Enabled:=True;
N10_Filter.Enabled:=True;
N_Credit.Enabled:=True;
N_ExportToMDB.Enabled:=True;
N_Kodifikators.Enabled:=True;
end;
Что же дальше?
Короткий разговор о сортировке записей в таблице-источнике.

Урок 22. Компонент навигатор


На предыдущем уроке мы начали наполнять главную форму программы "Расходы" контейнерами и элементами управления. На этом я предлагаю временно оставить наш проект. Я постараюсь объяснить почему. Какое основное действие будет производить оператор, запустив программу, как Вы думаете? Чаще всего он будет вводить новые данные. Что ему нужно для этого? Следуя логике, заложенной в структуре базы данных, следуя фазам луны :-) и правилам бухгалтерского учета, учетные записи лучше группировать. Группировка в журнально-ордерной системе (это я сейчас не ругнулся, поверьте) имеет одни принципы. Эта система была создана до появления вычислительных машин. С появлением же компьютеров, надобность в группировке как таковой отпала вовсе: в базу данных можно валить все навалом, потом разберемся, когда будем из нее данные извлекать. И все же, во многих программах присутствует вид группировки записей в мемориальные ордера (опять не ругнулся). Под этим каждый волен понимать все, что хочет,  а я предлагаю не мучиться (все же не большую систему пишем) и принять один уровень группировки равным одному календарному месяцу. В крупных системах мемориальные ордера группируют по дебетовому или кредитовому принципу, а мы сопоставим мемориальному ордеру месяц. Вряд ли в нашем учете накопится огромное количество записей среди которых потребуется отыскать ошибку - это основная причина настоящего упрощения. Хотя и в этой программе пользователь может применить иные принципы. Наше дело - написать такой софт, который его в его творчестве не будет сдерживать и ограничивать.
Поэтому, первое, что должна наша программа спросить пользователя - это: в какой мемориальный ордер желаете запись добавить?
Как программа может это сделать? Соответствующую форму на экран вывести.
Что в той форме будет видеть пользователь? Пользователь в той форме должен увидеть список всех заведенных им мемориальных ордеров - МО (с учетом нашего упрощения - месяцев).
- А что, если наступил новый месяц, спросят меня любознательные? Как быть пользователю?
Справедливый вопрос. Стало быть, в форме, позволяющей просматривать список МО, и выбирать нужный из этого списка, должна быть предусмотрена возможность добавления новой записи. Ох, чувствуете, куда нас занесло?
А занесло нас в область управления (добавления, редактирования и удаления - AED от Add, Edit, Delete) записями в таблице базы данных.
Вообще-то, для этого существует стандартный компонент, поставляемый с средой разработки. Но...
Однажды шеф сказал: "Хочу, чтобы на кнопках были надписи вместо непонятных пиктограмм!"
И действительно, домашнюю хозяйку, решившую с помощью программы "Расходы" поучитывать слегка свои затраты на губную помаду, лучше не пугать пиктограммами и, не дай Бог, иностранными словами.
Увы, такого компонента в стандартной поставке нет, только TDBNavigator.
Пришлось яндекс погуглить :-)
После долгих мытарств отыскал, наконец, на Delphi World 6 статью NavigationBotton. Отдельное спасибо автору!
Потом были долгие поиски контейнера для набора кнопок. Суть проблемы, с которой сталкивались многие до меня, но никто не поделился результатом ее решения, заключается в том, что при изменении размера контейнера компонента хотелось, чтобы пропорционально (в конструкторе) менялись бы и размеры кнопок.
Для любознательных скажу пару слов: на основе добытой NavigationBotton и стандартного контейнера TPanel, я сделал собственный компонент, поменяв надписи на кириллические, исключив кнопки перехода по записям, добавив свойство источника данных DataSource и написав подпрограмму пересчета размеров. Исходный код, который, признаюсь, далек от идеала, каждый любознательный может скачать вот здесь. Ну, а для тех, кто не захочет заморачиваться и разбираться в чужих писульках - добро пожаловать на страничку с библиотекой компонентов Pro-Delphi (кстати, для успешной дальнейшей работы она должна уже быть установлена в Вашей среде разработки).

Что же дальше? Попробуем организовать работу с одной из таблиц базы данных. Пора уже, не правда ли?

вторник, 18 октября 2011 г.

Урок 21. Контейнеры и элементы управления главной формы


Упомянув однажды в новостях о Java , я оставил эту модную среду разработки в стороне. Для работы с базами данных она годится, но не очень. Подключение к БД Access так и не вызвало у меня восторга, но мало того: для таблиц нужно "сочинять" модель поведения. Не то, чтобы лениво... Вряд ли стоит этим заниматься, когда стоит цель - сделать быстро. Но, не влюбившись в Java, не оставлю ее навсегда. Как в мультике про Карлсона: "Он улетел, но он обещал вернуться..." Я обещаю вернуться.
Итак, в предыдущем уроке я рассказал о подключении к базе данных MS Access. Но, давайте сейчас вернемся к оставленному нами Delphi проекту программы "Расходы" и наполним нашу основную форму контейнерами и элементами управления.
Я не располагаю элементы управления непосредственно на форме. Такой способ приемлем и не является ошибкой. Просто для управления интерфейсом (переключения его в различные режимы: добавления данных, редактирования и т.п.) удобнее располагать элементы управления, размещая их в контейнерах.
Стандартным (привычным) набором я считаю три панели и строку состояния.
Вы можете использовать любые панели, а я размещу на форме свое "детище" из библиотеки Pro-Delphi. Строку состояния Вы найдете на панели компонентов, на вкладке "Win 32".
Первой панели установите свойство Align в alTop, чтобы она была всегда только сверху,  а вторую - сделайте клиентом (она будет изменять свой размер пропорционально изменению размера формы) - alClient. Третью расположите на предыдущей (можно использовать окно Structure) - и установите ее свойство Align в alBottom.
Для первых двух панелей отключите свойство Visible, установив его в False. Третью панель можно оставить видимой, так как она будет показываться или отключаться вместе с контейнером, в котором она размещена, т.е. с панелью 2.
Несколько слов о назначении этих панелек.
На верхней панельке я обычно располагаю элементы управления, предназначенные для редактирования выбранной записи. Кому-то может показаться такой способ не вполне удобным... Что ж: вольному - воля, Вы вправе добавить в проект лишнюю форму. Поэтому при старте показывать эту панель не имеет смысла.
Панель-клиент - основное поле действия. Здесь, как правило, располагается большая таблица, позволяющая обозревать "оперативный простор", производить выбор какой либо строки или как-то отфильтровывать лишнее. Совсем не лишнее использовать ее для показа, например, результатов поиска.
На последней панельке я располагаю обычно элементы управления, предназначенные для перехода по записям таблицы (навигатор), средства поиска и отбора записей.
Теперь щелкните два раза по StatusBar, чтобы вызвать на экран редактор. Добавьте в семейство Panels пять элементов (счет ведется от нуля):
0    Text=Записей   Width=130
1    Text=Валюта     Width=150
2    Text=МО             Width=200
3    Text=Счет         Width=200
4    Text=Каталог   Width=255
(ширина подобрана опытным путем :-) ). На этих Panels будут отображаться поясняющие выбор оператора надписи.
Теперь вспомним о том, что когда-то давно, на уроке №5 я рассказывал о замечательной библиотеке компонентов EhLib. Настало время ее полноценного использования. Добавьте на панель-клиент экземпляр компонента TDBGridEh. Я сознательно сейчас не переименовываю добавляемые компоненты, чтобы не усложнять рассказ.
На самую верхнюю нашу панельку (1) добавьте следующие компоненты из библиотеки EhLib, переименовав их (здесь это действие необходимо, так как эти элементы будут использоваться в программном коде, и будет просто удобнее ,если они получат осмысленные имена хотя бы на смеси кириллицы и латиницы :-) ):
TDBDateTimeEditEh           Name=DataEdit,
два элемента TDBLookUpComboBoxEh с именами Name=DEdit, и Name=KEdit,
TDBNumberEditEh с именем Name=SEdit
и один элемент TEdit с вкладки "Стандартные", дав ему имя PEdit.
Как многие уже догадались, эти элементы управления предназначены для редактирования выбранной записи, а точнее полей: даты, счетов дебета и кредита, суммы и примечания.
В результате всех этих действий, должна получиться вот такая картина:
При запуске проекта на выполнение, вы не должны увидеть ничего нового: форма и строка меню. Если Вы видите панели с элементами управления, значит Вы не сделали их невидимыми на момент старта программы. Исправьте эту ошибку.







Что же дальше?
Придется оставить не на долго наш проект, чтобы подготовить один полезный компонент.


вторник, 11 октября 2011 г.

Урок 20. Подключение к базе данных


Продолжаем разговор о создании программы "Расходы". 


На прошлом уроке я позволил себе лирическое отступление, рассказав о создании заставки к программе.
Но сегодня, мой дорогой читатель,  наступил долгожданный момент, когда речь пойдет о подключении к созданной нами ранее в среде MS Access базе данных Rashod.mdb.
Но, для начала, предлагаю ввести одну переменную типа записи и одну полезную константу в разделе interface:
const CR=#13#10;                   // Константа перевода каретки
Type  TMySelect = record
-  объявление типа запись,  на основе которого будет объявлена переменная,  предназначенная для хранения  выбираемых в процессе работы значений
MySel_ID: Integer;             // Идентификатор выбранной строки
MySel_IDAcc: Integer;          // Идентификатор счета
MySel_AccName: String;         // Наименование счета
MySel_IDMO: Integer;           // Идентификатор мемориального ордера
MySel_MOName: String;          // Наименование мемориального ордера
MySel_Mes: Integer;            // Номер месяца
MySel_IDVal: Integer;          // Идентификатор валюты
MySel_ValName: String;         // Название валюты
MySel_Dir: String;             // Выбранная рабочая папка
MySel_Field: String;           // Имя поля
end;

И пусть это пока не смущает... Пригодится.
Не забудьте добавить объявление переменной:
var  MySelect: TMySelect;
Теперь настало время открыть форму в режиме конструктора и разместить на ней компонент TADOConnection с вкладки dbGO панели инструментов. Этот компонент не единственный, который позволяет связать приложение с базой данных. Есть ряд компонентов сторонних производителей, выполняющий аналогичные функции. Корифеи начнут упрекать меня за выбор этого компонента, но я оправдаюсь следующим:
  • данный компонент входит в стандартную поставку среды разработки (т.е. Вам не придется искать и покупать аналоги сторонних производителей),
  • работа с ним проста и удобна,
  • компонент работает надежно и стабильно.
Измените свойство Login Prompt экземпляра ADOConnection1 компонента TADOConnection на False. Это нужно для того, чтобы при попытке подключения к базе данных не запрашивался пароль и логин. В других задачах Вы можете использовать иные настройки.
Затем, как показано на иллюстрации:
  1. щелкните по кнопке с многоточием в поле ConnectionString, чтобы вызвать редактор строки подключения,
  2. нажмите кнопку "Build" - построить,
  3. выберите драйвер MS Jet, позволяющий коннектиться к базам данных Access,
  4. нажмите кнопку "Далее"
  5. выберите базу данных Rashod.mdb,
  6. нажмите "Проверить соединение",
  7. нажмите "ОК".
Полученную с помощью мастера строку подключения скопируйте целиком в соответствующую строку файла Rashod.ini (см. видео):
[General]
ConnectionString=Provider=Microsoft.Jet.OLEDB.4.0;Data Source=X:\Lessons\InternetBusiness\! VideoLessons\Les 20\Расходы\Rashod.mdb;Persist Security Info=False

Правильно сформированная строка не вызовет ошибки, если свойство Connected элемента ADOConnection1 изменить на True. Если у Вас все получилось, верните этому свойству первоначальное значение.
ВНИМАНИЕ! В финале работы над проектом Вы обязательно удалите строку подключения из свойства ConnectionString!
Но, не сейчас. Она нам еще понадобится, когда мы продолжим разработку и будем наполнять проект таблицами и запросами.
Теперь нам необходимо осуществить программное подключение.
В описании класса формы, в разделе Private, добавим одну процедуру и одну функцию:
private    { Private declarations }
Procedure MainConnecting();
Function TestConnection(): Boolean;
Назначение функции - дать ответ на вопрос: возможно ли подключение к базе данных по заданной в ini файле строке подключения.
Процедура же, после тестирования производит необходимые действия по подключению к базе данных.
Текст функции:
Function TMainFrm.TestConnection(): Boolean;
var  ini: TIniFile;
ConnectionString: String;
begin
Result:=True;                     // Если не произойдет "плохого", то функция вернет это значение
// Подключение и считывание из файла настроек
ini  ini:=TiniFile.Create(GetCurrentDir + '\Rashod.ini');
ConnectionString:=Ini.ReadString('General','ConnectionString','');
ini.Free;
// Проверка, имеется ли подключение? Если да, то - сброс
If ADOConnection1.Connected=True  then    ADOConnection1.Connected:=False;
// Назначение компоненту считанной из файла настроек строки подключения
ADOConnection1.ConnectionString:=ConnectionString;
// Подключение
Try
ADOConnection1.Connected:=true;
Except
ADOConnection1.Connected:=False;     // Если неудача
Result:=False;
End;
end;

Процедура:
Procedure TMainFrm.MainConnecting();
var  ini: TIniFile;
ConnectionString: String;
begin
ini:=TiniFile.Create(GetCurrentDir + '\Rashod.ini');
ConnectionString:=Ini.ReadString('General','ConnectionString','');
ini.Free;
If ADOConnection1.Connected=True  then    ADOConnection1.Connected:=False;
ADOConnection1.ConnectionString:=ConnectionString;
ADOConnection1.Connected:=true;
end;

А теперь, задав для события создания формы (onCreate) процедуру его обработки как показано в прилагаемом видео, запишем в нее следующий текст:
procedure TMainFrm.FormCreate(Sender: TObject);
begin
// Стартовые значения переменным
MySelect.MySel_ID:=0;
MySelect.MySel_IDAcc:=0;
MySelect.MySel_IDMO:=0;
MySelect.MySel_AccName:='';
MySelect.MySel_MOName:='';
MySelect.MySel_Mes:=0;
MySelect.MySel_IDVal:=1;
MySelect.MySel_ValName:='Руб';
If Not TestConnection  then    {некие действия с помощью процедуры SetDisk, разговор о которой в будущем};
MainConnecting;
// далее будет произведено подключение таблиц TableActive(True);
end;
Если Вы скомпилировали проект, и при этом не возникло ошибок, на этом на сегодня все.

Что же дальше?
Короткий разговор о контейнерах и элементах управления главной формы.