№
|
Название
|
Смотреть on-line
|
Ссылка для
скачивания
|
1
|
Hello World с изюминкой
|
||
2
|
Оформление проекта или то, до
чего руки не доходят… (ico, компонент TMainMenu)
|
||
3
|
Техническое задание и алгоритм
|
-
|
-
|
4
|
Список действий и элементы
управления формы (компонент TActionList)
|
||
5
|
Установка компонентов сторонних
производителей
|
||
6
|
Не много ли два таймера
в одной программе ?! (компонент
TTimer)
|
||
7
|
Как запомнить настройки
|
||
8
|
Учим таймер “петь” (компонент
TMediaPlayer)
|
||
9
|
Создание собственного компонента
(компонент TMyLabel)
|
||
10
|
Создание компонента сообщения
(TMyMessenger)
|
||
11
|
Компонент гиперссылка
|
||
12
|
Добавление в проект формы “О
программе”
|
||
13
|
Компонент “О программе”
|
||
14
|
Сохранение настроек программы
|
||
15
|
Финишная обработка исполняемого
файла
|
||
16
|
Самые необходимые знания о
бухгалтерии
|
-
|
-
|
17
|
Что должна уметь программа
«Расходы» или ТЗ на разработку
|
-
|
-
|
18
|
Создание реляционной БД
|
||
19
|
Создание формы-заставки
(отключаемая заставка)
|
||
20
|
Подключение к базе данных
|
||
21
|
Контейнеры и элементы управления
главной формы
|
||
22
|
Компонент «Навигатор»
|
-
|
-
|
23
|
Очень важная последовательность
действий
|
||
24
|
Сортировка записей в таблице
|
||
25
|
…
|
-
|
-
|
Научиться программировать не так сложно, как кажется. Уроки по принципу от простого к сложному. Исходные тексты программ. Видео уроки.
суббота, 31 декабря 2011 г.
Список опубликованных в 2011 году видео материалов
PS
Для просмотра скачанных swf-роликов Вам понадобится flash-проигрыватель
вторник, 27 декабря 2011 г.
Урок 28. Организация ввода и редактирования данных
В предыдущем уроке мы много говорили на тему оформления интерфейса. Теперь настало время заглянуть внутрь - организовать процедуры ввода информации в главную таблицу и редактирования ее.
Для начала, добавьте в раздел частных объявлений декларацию следующей процедуры:
Procedure TMainFrm.MyEdits();
Begin
DataEdit.Value:=ADOTableMain.FieldByName('MyDate').value;
DataEdit.Value:=ADOTableMain.FieldByName('MyDate').value;
DEdit.KeyValue:=ADOTableMain.FieldByName('D').value;
KEdit.KeyValue:=ADOTableMain.FieldByName('K').value;
SEdit.Value:=ADOTableMain.FieldByName('Summa').value;
PEdit.Text:=ADOTableMain.FieldByName('Rem').Text;
Panel3.Visible:=True;
end;
Panel3.Visible:=True;
end;
Panel3 - это верхняя панелька на главной форме, где расположены несколько элементов управления для ввода.
В этой процедуре, элементам управления (контролам) присваиваются значения из соответствующих полей таблицы "Main".
Аналогично объявите и создайте процедуру InsEdit:
Procedure TMainFrm.InsEdit();
Begin
// Процедура подготоавливает ввод новой записи
ADOTableMain.Edit; // Переводит таблицу в режим редактирования
MyEdits; // Вызывает процедуру, переприсваивающую данные из записи в таблице в элементы управления на форме
if DataEdit.CanFocus // Устанавливает фокус на первое слева поле, если это возможно
then
DataEdit.SetFocus;
DBGridEh1.Enabled:=False; // Делает недоступной Gridend;
Begin
// Процедура подготоавливает ввод новой записи
ADOTableMain.Edit; // Переводит таблицу в режим редактирования
MyEdits; // Вызывает процедуру, переприсваивающую данные из записи в таблице в элементы управления на форме
if DataEdit.CanFocus // Устанавливает фокус на первое слева поле, если это возможно
then
DataEdit.SetFocus;
DBGridEh1.Enabled:=False; // Делает недоступной Gridend;
Теперь для компонента ADOTableMain необходимо прописать обработчики четырех событий:
до вставки (BeforeInsert),
до редактирования (BeforeEdit),
после записи (отправки данных в источник - AfterPost),
после вставки (AfterInsert).
Эти процедуры будут выглядеть следующим образом:
procedure TMainFrm.ADOTableMainAfterInsert(DataSet: TDataSet);
begin
// Присвоение некоторых стартовых значений полям таблицы
ADOTableMain.FieldByName('MyDate').Value:=FormatDateTime('dd/mm/yyyy',now());
ADOTableMain.FieldByName('D').Value:=1;
ADOTableMain.FieldByName('K').Value:=1;
ADOTableMain.FieldByName('MO').Value:=MySelect.MySel_IDMO;
ADOTableMain.FieldByName('MO_Name').Text:=MySelect.MySel_MOName;
// Поднять запись на верхнюю панель
InsEdit;
end;
procedure TMainFrm.ADOTableMainAfterPost(DataSet: TDataSet);
begin
DBGridEh1.ReadOnly:=True; // Делает Grid НЕдоступной для редактирования
end;
procedure TMainFrm.ADOTableMainBeforeEdit(DataSet: TDataSet);
begin
MyEdits; // Переписывает значения из источника базы данных в поля формы
end;procedure TMainFrm.ADOTableMainBeforeInsert(DataSet: TDataSet);
begin
DBGridEh1.ReadOnly:=False; // Делает Grid доступной для редактирования
end;
begin
// Присвоение некоторых стартовых значений полям таблицы
ADOTableMain.FieldByName('MyDate').Value:=FormatDateTime('dd/mm/yyyy',now());
ADOTableMain.FieldByName('D').Value:=1;
ADOTableMain.FieldByName('K').Value:=1;
ADOTableMain.FieldByName('MO').Value:=MySelect.MySel_IDMO;
ADOTableMain.FieldByName('MO_Name').Text:=MySelect.MySel_MOName;
// Поднять запись на верхнюю панель
InsEdit;
end;
procedure TMainFrm.ADOTableMainAfterPost(DataSet: TDataSet);
begin
DBGridEh1.ReadOnly:=True; // Делает Grid НЕдоступной для редактирования
end;
procedure TMainFrm.ADOTableMainBeforeEdit(DataSet: TDataSet);
begin
MyEdits; // Переписывает значения из источника базы данных в поля формы
end;procedure TMainFrm.ADOTableMainBeforeInsert(DataSet: TDataSet);
begin
DBGridEh1.ReadOnly:=False; // Делает Grid доступной для редактирования
end;
Пояснения я постарался задать в //комментариях :-)
Теперь настало время вспомнить про разработанный нами несколько уроков назад навигатор. Пора его "приспособить" к главной таблице. Расположите его на нижней панельке и назначьте в качестве источника данных DataSource = DataSourceMain. Не забудьте установить "якоря" (Anchors), привязывающие его справа внизу:
akRight = True;
akBottom = True,
(другие два значения = False).
Если сейчас запустить проект и нажать на навигаторе кнопку "Добавить", то возникнет следующая ошибка:
Вообще-то, о способах и методах отладки я планирую поговорить в будущем. Тема эта неисчерпаема и довольно сложна. А сейчас, просто избавимся от лишнего поля, о котором идет речь в сообщении. Откуда оно взялось? Да, Бог его знает...
удалите строку кода:
ADOTableMain.FieldByName('MO_Name').Text:=MySelect.MySel_MOName;
1. Новая запись со значениями по умолчанию добавлена в таблицу Main. Ура!
2. Остается активным меню. Правильно ли это?
3. Не работают элементы управления с выпадающими списками на верхней панельке.
4. Запись невозможно сохранить.
5. Невозможно вернуть форму к стартовому виду.
procedure TMainFrm.PrDNavigator1nbCancelClick(Sender: TObject);
begin
DBGridEh1.Enabled:=True; // После нажатия кнопки Cancel на навигаторе, сетка должна стать доступной
Panel3.Visible:=False; // Верхняя панелька становится не нужна, скроем ее
end;
procedure TMainFrm.PrDNavigator1nbPostClick(Sender: TObject);
begin
// Переприсвоение значений из контролов формы в поля таблицы
ADOTableMain.FieldByName('MyDate').Value:=DataEdit.Value;
ADOTableMain.FieldByName('D').Value:=DEdit.KeyValue;
ADOTableMain.FieldByName('K').Value:=KEdit.KeyValue;
ADOTableMain.FieldByName('Summa').Value:=SEdit.Value;
ADOTableMain.FieldByName('Rem').Text:=PEdit.Text;DBGridEh1.Enabled:=True; // После нажатия кнопки Post на навигаторе, сетка должна стать доступной
Panel3.Visible:=False; // Верхняя панелька становится не нужна, скроем ее
end;
begin
DBGridEh1.Enabled:=True; // После нажатия кнопки Cancel на навигаторе, сетка должна стать доступной
Panel3.Visible:=False; // Верхняя панелька становится не нужна, скроем ее
end;
procedure TMainFrm.PrDNavigator1nbPostClick(Sender: TObject);
begin
// Переприсвоение значений из контролов формы в поля таблицы
ADOTableMain.FieldByName('MyDate').Value:=DataEdit.Value;
ADOTableMain.FieldByName('D').Value:=DEdit.KeyValue;
ADOTableMain.FieldByName('K').Value:=KEdit.KeyValue;
ADOTableMain.FieldByName('Summa').Value:=SEdit.Value;
ADOTableMain.FieldByName('Rem').Text:=PEdit.Text;DBGridEh1.Enabled:=True; // После нажатия кнопки Post на навигаторе, сетка должна стать доступной
Panel3.Visible:=False; // Верхняя панелька становится не нужна, скроем ее
end;
Теперь по нажатию кнопки Post (сохранить) на навигаторе, форма возвращается к первоначальному виду, а данные из полей формы переписываются в таблицу базы данных.
Разберемся с элементами управления (DEdit, KEdit), которые должны содержать списки.
Элементы управления со списками работают так, что пользователю показывается понятное ему значение из списка (ListField), а программа записывает в элемент управления (и далее - в базу данных) соответствующий ему код (KeyField).
Исправив эту некорректность, Вы получите практически работающую программу.

Урок 27. Отображение записей в Grid главной формы
В предыдущем уроке я показал как можно подготовить базу данных к работе, т.е. удалить из таблиц всю хранимую в них информацию, которая к определенному этапу становится не актуальной. В дальнейшем мы еще внесем некоторые изменения в алгоритм очистки, поскольку начинать каждый отчетный период (год) с "чистого листа", наверное, не совсем интересно и даже не правильно. Позже, когда речь пойдет об оборотной ведомости, я покажу, как можно подсчитать остатки, и тогда в алгоритм очистки базы данных нужно будет добавить несложный механизм, позволяющий перенести конечные остатки по счетам в начальные. Но, об этом чуть позже.
Давайте займемся главной формой проекта (MainFrm): пора уже научить ее отображать учетные записи.
Для этого расположите на форме два компонента:
TADOTable и TDataSource с вкладок dbGo и DataAccess соответственно.
Для начала, дайте компонентам имена: ADOTableMain и DataSourceMain.
Затем, для таблицы установите свойства:
Connection = ADOConnection1;
TableName=Main;
Щелкните дважды по компоненту ADOTableMain, и в появившемся окне с помощью правой кнопки добавьте все поля (Add all fields...).
Назначьте источник данных компоненту DataSourceMain:
DataSet = ADOTableMain;
Позаботьтесь о сортировке записей. Будет удобнее, если записи будут отображаться в порядке убывания даты, т.е. более свежие всегда вверху. Для этого в свойстве IndexFieldNames компонента ADOTableMain укажите поле сортировки MyDate с постфиксом DESC.
И последнее, что осталось сделать - указать для сетки (Grid), расположенной на форме источник отображаемых данных:
DataSource = DataSourceMain;
Если сейчас запустить проект, то после выбора МО, отобразится... опять таки пустая таблица... В чем же дело?
Очень просто: таблица Main не активна.
Разыщите процедуру:
procedure TMainFrm.TableActive(p: boolean);
begin
// Активация (деактивация таблиц)
// Активация (деактивация таблиц)
ADOTableMOs.Active:=p;
end;
и добавьте в нее строчку кода:
ADOTableMain.Active:=p;
Аналогичные действия выполняйте при добавлении в проект новой таблицы.
Теперь, если Вы запустите проект, то увидите таблицу (я заранее внес в нее несколько записей для наглядности, ведь, с помощью механизма очистки нам теперь ничего не стоит их удалить!), но зрелище это будет мало утешительным:
Плохо - все:
1. Нужно ли пользователю видеть служебное поле счетчика? Нет.
2. Учет в нашей стране ведется в рублях и копейках, а Summa отображает как будто бы целые.
3. Мы, вроде бы, выбрали мемориальный ордер, а здесь - все записи внавал... (МО=12 и МО=13 и т.д.).
4. Если МО уже выбран, зачем вообще отображать это поле, если значение в нем всегда будет одно и тоже, пока мы не выберем другой МО?
5. Что за чуднЫе заголовки у полей?
6. Хотелось бы, чтобы поля признаков Дебета и Кредита (D и K) отображали бы удобочитаемую информацию, а не порядковые номера счетов, которые пользователь помнить не обязан.
7. Скрытый недостаток - попробуйте ввести что-то в любое поле. Скорее всего, это у Вас получится. Но, задача этой сетки - просмотр записей, а не их редактирование.
8. Хорошо бы по сумме иметь итог.
Именно поэтому я и перечеркнул вид главной формы на иллюстрации. Такой вид никуда не годится.
Давайте постепенно разбираться и исправлять.
Для начала вызовите редактор колонок сетки (дважды щелкнув на DBGridEh1). Нажмите в нем пиктограмму Add all Fields (добавить все поля).
Для полей ID и MO, отображать которые нет необходимости, установите свойство Visible в False.
Остальным полям назначьте заголовки в свойствах Title.Caption ( я записал здесь свойство через точку, это означает, что Title имеет раскрывающийся список свойств, одно из которых Caption), как показано на рисунке:
Недостатки под номерами 1, 4 и 5 устранены.
Побороть пункт 2 можно с помощью свойства DisplayFormat для колонки Summa компонента DBGridEh1, задав в нем следующее значение: "0.,00".
Для решения вопроса отображения записей только одного мемориального ордера, добавьте в процедуру ShowMOs() несколько операторов после комментария "Смена фильтра в главной форме":
procedure TMainFrm.ShowMOs();
Begin
Application.CreateForm(TMOs_Frm, MOs_Frm); MOs_Frm.ShowMoDal; MOs_Frm.Free;
// Смена фильтра в главной форме
Application.CreateForm(TMOs_Frm, MOs_Frm); MOs_Frm.ShowMoDal; MOs_Frm.Free;
// Смена фильтра в главной форме
ADOTableMain.Filtered:=False;
OldFilter:='MO=' + IntToStr(MySelect.MySel_IDMO);
ADOTableMain.Filter:=OldFilter;
ADOTableMain.Filtered:=True;
StatusBarUpdate;
end;
end;
После всех этих манипуляций, главная форма уже лучше показывает записи из базы данных, но все же еще...
Для красоты и удобства (а также для возможности некоторого контроля и анализа в будущем), необходимо организовать самосчетный подвал (футер) в Grid-е. Для этого в свойствах компонента DBGridEh1 укажите
FooterRowCount = 1;
дословно: количество строк в "подвале" - одна и
SumList.Active = True;
Затем, выберите в редакторе колонок поле Summa и укажите следующие его свойства:
Footer.DisplayFormat=0.,00;
Footer.FieldName=Summa;
Footer.ValueType=fvtSum;
Вот, уже форма приближается к нормальному виду:
Чтобы решить вопрос с отображением счетов, необходимо добавить на форму еще пару компонентов TADOTable и TDataSource, связав их, как мы уже это делали выше, с таблицей счетов (Accounts). Назовем их ADOTableAcc и DataSourceAcc соответственно.
Не забудьте добавить ADOTableAcc.Active:=p; в процедуру активации таблиц.
Вызовите конструктор полей для таблицы ADOTableMain.
По правой кнопке вызовите контекстное меню, а нем выберите New Field... (Новое поле). Заполните свойства нового поля, как показано на иллюстрации:
Таким образом Вы создадите так называемое поле Lookup или поле с подстановкой.
Теперь нужно отобразить его в гриде.
Щелкните дважды по сетке, чтобы вызвать конструктор колонок. Воспользуйтесь еще раз кнопкой "Добавить все поля", но ВНИМАНИЕ: на вопрос "перезаписать?" ответьте отрицательно.
Затем удалите из списка повторно добавленные поля и поле "D". Перетащите на его место новое поле "D_Name". Подправьте его свойство:
Caption=Дебет;
Соответственно форма будет выглядеть совсем уже почти...
Надеюсь, что я очень подробно все описал, и Вы самостоятельно сделаете поле с подстановкой "Кредит".
Последний недостаток устраните, установив свойство DBGridEh1.Options.dgEditing = False.
В качестве бонуса конечному пользователю:
установите в свойствах DBGridEh1
OptionsEh.dghRowHighLight=True - удобно, когда строки в таблице подсвечиваются и
Options.dgAlwaysShowSelection=True - для подсветки выделенной курсором ячейки.
На этом сегодня все.
В следующем уроке я покажу как организовать ввод и редактирование данных в главной таблице.

понедельник, 26 декабря 2011 г.
Урок 26. Обнуление базы данных
Предыдущий урок был посвящен вопросам подключения к базе данных, расположенной на сменном носителе.
Сегодня я хочу показать, как с использованием компонента TADOCommand вносить изменения в базу данных. Мои не случайные читатели, которые следят за ходом событий, спросят: "Что это ты, Александр Львович, все вокруг да около ходишь? Когда же, наконец, мы двинемся дальше?" А скоро, очень скоро мы продолжим разработку основного алгоритма программы (кстати, Вы не забыли, как она называется и для чего, собственно, она предназначена? :-) ). Я стараюсь сохранить интригу. Да и не интересно будет рассказывать о разных мелочах настройки интерфейса и т.п., когда основной материал останется позади. Так что: терпение, мой друг, терпение...
А сейчас нужно добавить новый Action, создав новую категорию Service. Его свойства показаны на рисунке.
Затем положить на форму компонент TADOCommand с вкладки dbGo панели компонентов. Этот компонент как раз предназначен для того, чтобы выполнять запросы к базе данных, не требующие ответов, т.е. такие, которые видоизменяют информацию в базе данных, но не возвращают пользователю никакого набора строк. В нашем случае, например, по окончании календарного года старые записи становятся не очень-то актуальны. Хранить их нужно (мало ли что?!), а вот таскать везде с собой - вовсе не обязательно. Поэтому и существует необходимость почистить наше хранилище.
И последнее, что нужно сделать - написать обработчик события onExecute, не забыв при этом дать объявление процедуре и "подложить" этот Action под один из пунктов меню "Настройки".
Итак, текст обработчика события onExecute:
В следующем уроке я расскажу о подготовке главной формы к полноценному отображению данных с помощью DBGridEh, а так же поговорим о полях LookUp.
Сегодня я хочу показать, как с использованием компонента 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;
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? Очень просто: эта таблица содержит план счетов, говоря бухгалтерским языком. Или попросту - условно постоянный набор признаков, который можно использовать из года в год.MyMessenger.MessageString:='Операция завершена успешно';
MyMessenger.MessageType:=mtInformation;
MyMessenger.Buttons:=[mbOK];
MyMessenger.ShowMessage;
end;
End;end;
В следующем уроке я расскажу о подготовке главной формы к полноценному отображению данных с помощью 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 новую строку подключения.
- прочитать строку подключения из файла ini,
- попробовать подсоединиться к базе данных,
- при неудачном подсоединении дать возможность оператору выбрать расположение базы данных, используя стандартное окно диалога,
- записать в файл ini новую строку подключения.
Последую примеру героя М.М. Жванецкого: “Если вопросов нет, я начну”.
Добавьте на форму MainFrm компонент TOpenDialog с палитры компонентов, с вкладки “Dialogs”.
Создайте новую процедуру следующего содержания:
procedure TMainFrm.SetDisk(); // Установка диска с БД
Var
ini: TIniFile;
ConnectionString: String;
MyDir: String;
begin
ini: TIniFile;
ConnectionString: String;
MyDir: String;
begin
MyDir:=GetCurrentDir; // Запомнить в переменную путь к текущему директорию
OpenDialog1.Filter:='Файлы базы данных|*.mdb'; // Фильтр для диалога выбора файла (показывать только файлы баз данных Access
OpenDialog1.Filter:='Файлы базы данных|*.mdb'; // Фильтр для диалога выбора файла (показывать только файлы баз данных Access
// Выбор файла
If not OpenDialog1.Execute // Открытие окна диалога
then // Отмена выбора
begin
exit; // Выход из подпрограммы
end;
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);
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); // Возврат программы к текущему каталогу
SetCurrentDir(MyDir); // Возврат программы к текущему каталогу
end;
Один тонкий момент есть в этой процедуре, который мне хотелось бы пояснить. Дело в том, что компонент TOpenDialog, устанавливает текущий директорий по понятиям операционной системы. Поэтому, если Вы выбрали какую-то папку с базой данных, отличную от рабочей папки программы, то работа программы нарушится. Следовательно, необходимо запомнить в строковой переменной положение указателя на текущую папку до начала всего действия:
MyDir:=GetCurrentDir;,
а по окончании действия, вернуть его на место:
SetCurrentDir(MyDir);
Не забудьте дать объявление новой процедуры в секции Private описания интерфейса:
procedure SetDisk();
Где должна сработать новая процедура? Конечно же, при создании формы, при обработке события onCreate:
If Not TestConnection
then
SetDisk();
then
SetDisk();
Теперь для тестирования измените вручную путь к базе данных, прописанный в файле ini.
Удачи.
Удачи.
Что же дальше?
На следующем уроке я планирую рассказать, как производится очистка базы данных, удаление всех записей из таблиц или присвоение отдельным полям стартовых (нулевых) значений.

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

Подписаться на:
Сообщения (Atom)