Часто задаваемые общие вопросы по Borland Delphi Часть 2 Базы данных - компоненты и VCL.
Базы данных - компоненты и VCL. 1. Какие визуальные компоненты для работы с данными входят в Delphi? Различные версии Delphi содержат следующие наборы компонентов:
2. Использование псевдонимов в запросе SQL. Я делаю запрос по двум таблицам разных форматов, находящихся по разным псевдонимам.
На самом деле вы получаете ошибку Unknown Keyword, следовательно всего-лишь нужно заключить псевдоним и имя таблицы в двойные кавычки.
3. Ошибка в SQL запросе. У меня есть TQuery и TDataSource. В свойстве SQL для TQuery я пишу
Если свойство RequestLive=True, то имя таблицы нужно брать в кавычки:
4. Проблемы при работе с MS Access через TQuery. Я безуспешно пытался использовать данные из Microsoft Access иначе, нежели просто с помощью TTable. Используя TQuery я могу только читать результат, но не могу редактировать. После "login screen" возникает сообщение типа 'Passthrough SQL connection must be shared'. Измените в настройке
псевдонима (alias) пункт 'SQLPASSTHRU MODE' на 'SHARED AUTOCOMMIT'.
TQuery.Open возвращает результат в виде курсора, в связи с этим
он работает только для тех выражений, которые возвращают курсор. CREATE
TABLE возвращает только результат операции - поэтому для выполнения этого
выражения необходимо использовать TQuery.ExecSQL. Но и это может
не сработать, если конкретный драйвер БД не поддерживает операцию создания
таблиц - для получения характеристик драйвера используйте функции BDE (DbiOpenDriverList,
DbiGetDriverDesc).
Я хочу выполнить SQL-выражение и получить результат в свою переменную, что-то типа
TQuery.Open; ... VariableC := TQuery1.Fields[0]; 7. Автоматический подсчет сумм при помощи TQuery. Меня интересует возможность подсчета суммы по таблицам, которые уже находятся на форме. Есть очень простой способ - предположим, что у вас есть на форме Query1, DataSource1, DBGrid1. Добавьте на эту-же форму компоненты Query2, DataSource2, DBText1. Установите property Query2.DataSource=DataSource1. В Query2.SQL напишите
При изменении Query1 (если конечно Query1.RequestLive=True) Query2 будет автоматически перевыполняться. Это решение хоть и простое, но неэкономичное - особенно при большом количестве записей в исходной таблице. Более того, запрос Query2 должен иметь WHERE идентичный Query1. Для подсчета сумм правильнее использовать событие TQuery.OnCalcFields.
Хорошим примером является X:\DELPHI\DEMOS\DB\MASTAPP\MASTAPP.DPR.
Мой запрос получает параметр. Проблема в том, что строка параметра содержит " (двойную кавычку), которая приводит к Runtime Error. Вам необходимо использовать динамический SQL-запрос, иначе при указании например
9. Как создать отдельный компонент TTable? Легко и просто - точно также как и обычный компонент. При этом в качестве
параметра конструктору можно передавать значение nil.
10. Как узнать, какая ячейка при просмотре TDBGrid текущая? Здесь процедура для сохранения текущего номера строки и колонки. Следующий
код в методе MyDBGridDrawDataCell обновляет переменные Col
и Row (которые не должны быть локальными для этого метода) каждый
раз, когда таблица перерисовывается. Используя этот код, вы можете считать,
что Col и Row указывают на текущую колонку и строку соответственно.
11. Как выделить цветом текущую строку в TDBGrid? Для TDBGrid в свойстве Options установите dgRowSelect
в True.
Введите следующий код в обработчике события OnDrawDataCell:
13. Как узнать, что пользователь перешел на другую запись, например, в TDBGrid? Переход на новую запись - это событие, которое относится не к визуальному
компоненту, а к источнику данных. Соответствующее событие называется OnDataChange
и имеется у компонента TDataSource.
Выключите property
DefaultDrawing, и обрабатывайте событие OnDrawDataCell:
В Delphi 2.0 вы
можете использовать редактор столбцов для той же самой цели.
Дело в том, что TDBGrid предполагает многопользовательский доступ к таблице. В этом случае другие пользователи этой-же таблицы могут добавлять или удалять записи, в результате информация о количестве записей на текущий момент становится неопределенной. Конечно, в однопользовательском варианте количество записей всегда известно, но поскольку TDBGrid работает через промежуточный источник данных DataSource, ему неизвестен конкретный способ доступа к данным - навигационный или SQL. Например, для SQL существует только один способ узнать количество записей - выполнить специальный запрос с их подсчетом, а на это может потребоваться значительное время. По всем этим причинам TDBGrid является универсальным средством
для просмотра таблиц, которое работает во всех случаях и с любыми источниками
данных.
Используйте код:
DBGrid1.SetFocus; 17. Как создать обработчик события OnClick для TDBGrid? Как и всякий TControl (иерархия наследования TControl-> TWinControl->
TCustomControl-> TCustomGrid-> TCustomDBGrid-> TDBGrid) у TDBGrid
есть событие OnClick, но оно protected. Так что можно либо
создать новый класс, производный от TDBGrid, в котором объявить
это свойство как published, либо использовать другой вариант. Например,
вы можете использовать событие OnColEnter.
Маска относится к полю в таблице (компонент TField) а не к самому
TDBEdit. Дважды щелкните мышкой на TTable и в FieldEditor'е
добавьте все нужные вам поля. Когда поле выбрано в списке, его свойства
показаны в Object Inspector, включая маску ввода. Связывание TDBEdit
и любых других компонентов с этим TTable будет вызывать наложение
маски на соответствующее поле.
В стандартном наборе такого компонента действительно нет. Возможно, кто-нибудь скоро напишет что-нибудь в этом роде. В принципе, можно обойтись и без данного компонента. Например, есть табличка .db с BLOB полем для OLE объекта. При движении по записям можно OLE сохранять в базе, уничтожать, создавать новый, считывать из базы. Чтение/запись OLE:
20. Что нужно сделать, чтобы при открытии запароленной таблицы не появлялся диалог запроса пароля? Просто дайте этот пароль объекту Session перед открытием таблицы:
Если ваш компонент
доступа к данным (TTable или TQuery) связан с сессий, отличной
от той, которая выставляется по умолчанию, то добавлять пароль нужно именно
у этого компонента TSession.
Ниже приведена функция GetBlobSize, которая возвращает размер
данного BLOB или MEMO поля.
22. Как осуществить поиск по неиндексированному полю в таблице? Вы можете добавить следующую функцию в ваш модуль и вызвать, например:
23. Как узнать, что изменилась текущая запись? Событие TDataSource.OnDataChange когда State=dsBrowse.
При вычислении дат важно удостовериться в том, что все используемые значения подходят по типу. В документации не совсем явно отражен тот факт, что на самом деле тип TDataTime эквивалентен типу Double, который можно использовать далее. В примере, D1 и D2 (поля в Table1) могут быть типа
или Date, или TDateTime, а D3 - поле типа Integer.
1. Почему возникает ошибка компиляции при обращении к объекту Sender в обработчике события? Я в обработчике события OnChange для компонента TEdit пытаюсь получить содержимое его текстового буфера. Однако, следующая конструкция вызывает ошибку компиляции 'неизвестный идентификатор':
2. Проблемы с полями класса типа TObject, TTable и т.д. Я объявляю поле класса как TTable, но при обращении к нему происходит ошибка. Дело в том, что в
Delphi все экземпляры объектов, объявленых как class, являются динамическими.
Соответственно поле MyTable, объявленное как
Подробнее см. Changes in Object Pascal Language в документации или on-line
help.
Вообще говоря, нужно вызывать метод Close для формы. Close вызывает событие OnClose (обработчик которого может решить, что форму нельзя закрывать, например, если имеются несохраненные данные). Close не освобождает память, связанную с формой, если вы, конечно, не поместите в обработчик события вызов метода Release. Если вы хотите уничтожить форму без вызова события OnClose, используйте метод Release. Этот метод работает подобно Free, но позволяет всем обработчикам событий данной формы закончить работу перед тем, как память будет освобождена. Модальные формы "прекращают свой модальный статус", когда вы устанавливаете свойство ModalResult формы в любое значение, отличное от нуля. Если вы поместите кнопку на модальную форму и установите свойство ModalResult для кнопки в некоторое значение, то, когда пользователь нажмет на эту кнопку, форма закроется с результатом, который вы определили. Этот результат можно узнать вызывая ShowModal как функцию. То есть:
4. Перемещение существующих компонентов на TPanel, TGroup и т.п. Я поместил кнопку (или что-то другое) на форму, затем поместил панель, и решил переместить кнопку на панель, но ничего не получилось. Действительно, чтобы поместить кнопку на панель, необходимо на форму сначала поместить панель, выбрать ее, а затем уже помещать кнопку. Но и в вашей ситуации есть решение. Скопируйте (Copy) или вырежьте (Cut) нужный компонент, выберите панель, и сделайте вставку (Paste). Рекомендуется предварительно "подогнать" копируемый компонент в левый верхний угол формы, иначе компонент на панели может выпасть из "пределов видимости" панели (или любого другого группового компонента). Если компонент все-таки "выпал" из пределов видимости - найдите этот компонент в Инспекторе Объектов, и установите нужные значения его свойств Left и Top. Используя группы компонент можно огранизовать форму-шаблон, на которой можно складывать (например в Notebook) компоненты с предварительно заданными свойствами, отличными от стандартных. Это решение проще чем добавлять такие компоненты в палитру компонент - не увеличивается размер библиотеки компонентов DCL (Delphi 3.0 не считается), не загромождается палитра компонент. Учтите, что при таком копировании компонент их имена меняются на новые
(Button1, Button2 и т.д.).
Для того, чтобы добавить компонент на страницу TabbedNotebook,
свойству Parent нового компонента нужно присвоить указатель на требуемую
страницу. Способ для доступа к любой странице TTabbedNotebook во
время выполнения - массив свойств Objects у свойства Pages
компонента TTabbedNotebook. Другими словами, страницы сохранены
в виде объектов в свойстве Pages (тип TStringList). Пример
демонстрирует создание кнопки TButton на второй странице TabbedNotebook1:
То же самое справедливо и для компонента TNotebook.
Попробуйте: &&
Попробуйте использовать следующий код:
8. Почему некоторые компоненты типа TPanel и TEdit не имеют свойства Canvas? Все наследники TCustomControl имеют Canvas, однако, в
большинстве случаев это свойство объявлено protected для предотвращения
рисования 'чужаками' на компоненте. Наследники компонента всегда могут
получить доступ к унаследованным protected свойствам (типа Canvas),
но пользователь компонента-никогда.
Если вы хотите рисовать на компоненте, у которого нет public
свойства Canvas, то используйте, например, компонент TPaintBox:
положите его на панель TPanel, сделайте Align = Client и
рисуйте на TPaintBox.Canvas.
Допустим, вы поместили на форму кнопку, и создали метод OnClick в котором вызываете Button1.Free. Вы видите, что это метод формы - казалось-бы, какие препятствия для правильного уничтожения кнопки? На самом деле Button1.OnClick является свойством и после запуска вашего приложения содержит адрес метода Form1.Button1Click. Именно кнопка вызывает этот метод как свой собственный. А это означает, что кнопка не может удалить себя в своем-же методе. Даже если вы попытаетесь удалить ссылку в OnClick:
Button1.Free; 10. Есть ли у TDBGrid события OnMouseDown, OnMouseUp и OnMouseMove? Они есть, но не объявлены published. Вы можете создать наследника
TDBGrid и сделать их published.
Я хочу делать текущими в форме произвольные компоненты. Как выставить фокус у конкретного компонента ясно - ListBox1.SetFocus. А если я хочу обращаться к некоему компоненту по имени (свойство Name)? Свойство TForm.Components
- массив компонентов формы, который и нужен вам. Вы можете перемещаться
по этому массиву пока не найдете компонент с нужным Name. Например:
12. Как получить горизонтальный ScrollBar на ListBox? Пошлите сообщение LB_SETHORIZONTALEXTENT в ListBox. Например,
сообщение может быть отослано в момент создания формы:
Вы можете использовать сообщения Windows API EM_LINEFROMCHAR
и EM_LINEINDEX для определения положения.
14. Постранична прокрутка TMemo, реализация Undo и определение строки курсора. Как прокрутить содержимое компонента TMemo? Приведенная ниже процедура
предполагает, что фокус находится на Edit1 и осуществляет прокрутку
в соответствии с нажатыми клавишами.
Если встроенного Undo достаточно, то это очень просто:
Весь фокус в сообщении
EM_LINEFROMCHAR. Попробуйте:
Попробуйте так:
16. Как показать содержимое Memo поля в TDBGrid? Используйте следующий код для обработки события OnDrawDataCell
у TDBGrid. (Перед запуском программы создайте объект TMemoField
для memo поля в Fields Editor).
17. Не возникает событие TSpeedButton.OnDblClick. Я создаю событие на SpeedButton1.OnDblClick, но оно, похоже, вообще никогда не возникает. OnClick работает. Что делать? На самом деле работает, только в определенных ситуациях. Если вы помещаете на панель несколько кнопок, то по умолчанию они независимы и соответственно не фиксируются в нажатом состоянии. Поскольку одиночное нажатие мыши на кнопку отрабатывается немедленно, двойной щелчок мыши воспринимается как два нажатия и отпускания. Поэтому OnDblClick и не срабатывает. Если же кнопки связаны в группу (GroupIndex <> 0), то
они могут фиксироваться, и соответственно могут воспринимать двойной щелчок
мыши.
Именно так и происходит в Windows - посылаются оба сообщения. Для того
чтобы обработать только какое-то одно событие необходимо чуть "задержать"
выполнение OnClick. Сделать это можно следующим способом:
19. Как определить из обработчика события OnClick в Popup.MenuItem, для какого объекта это произошло? Используйте свойство PopupComponent компонента TPopupMenu
для определения, где была нажата правая кнопка.
Свойство ActiveControl для формы тоже можно использовать, однако,
ActiveControl не обязательно является тем элементом, для которого
произошло событие.
Используйте свойство Tag. Установите значение Tag свое
у каждого объекта для опознания. (Использование констант, которые описывают
объект - идеально подходит).
Таким образом вы можете обрабатывать события как от однотипных компонент,
так и от компонент разного типа.
На моей форме находится примерно 10 кнопок. Я хочу обрабатывать нажатие на любую из них одним событием, но как их отличить внутри обработчика события? Для этого базовый
класс VCL TComponent имеет поле Tag типа Longint.
В момент разработки вы можете присвоить этому полю любое значение, а в
момент исполнения использовать его (или переопределять). В вашей ситуации
достаточно присвоить полю ButtonX.Tag значение от 1 до 10 (или от
0 до 9, как удобнее), а в обработчике написать примерно следующее:
22. Использование TPanel в качестве "индикатора". Я пытаюсь использовать TPanel как индикатор процесса обновления БД. Однако надпись на панели не обновляется пока не закончится цикл обработки БД. В цикле вызывается Panel.Caption := ... После присвоения Panel.Caption
вызывайте Panel.Refresh или Application.ProcessMessages (второй
вариант предпочтительней, так как позволяет перерисовать себя всем клмплнентам,
которые в этом нуждаются).
Если ваша форма содержит панель подсказки в нижней части формы, то вы можете определить подменю для этой панели, и выставлять Form.ShowHint в True или False в зависимости от состояния Checked элемента меню. Например, в TMenuItem.OnClick напишите:
24. Как в меню поместить bitmap? Можно поступить таким образом:
Параметры:
25. Каким образом можно поместить двумерный массив в TImage? Представим, что данные находятся в массиве:
Image1.Picture.Bitmap.Height := 128;
Image1.Refresh; { для того, чтобы изменения отобразились } 26. Как из программы 'открыть' TComboBox? У TComboBox есть run-time свойство, не упомянутое в on-line help - DroppedDown. Для открытия ComboBox напишите:
27. Как заменить надпись 'Read only' в компонентах TSaveDialog и TOpenDialog? Попробуйте посмотреть в Windows API Help разделы, связанные с lpTemplateName.
Вообще говоря, вы можете заменить стандартный Open Dialog Box своим собственным
шаблоном.
Делаю так:
Проблема в том, что
TCustomGrid имеет метод DrawCell, который является абстрактным.
То, что его безусловно надо переписывать у любого наследника TCustomGrid,
к сожалению, не отражено в документации. Создайте этот метод (пусть даже
пустой) и ваша проблема исчезнет.
В Fields Editor выберите поле для форматирования. Используя свойства
DisplayFormat и EditFormat сделайте то, что нужно. DisplayFormat
работает для поля, на которое не установлен фокус. EditFormat работает
для поля, на которое фокус установлен. Форматирование аналогично первому
параметру в функции FormatFloat, но без скобок.
Используйте данный код для события OnKeyPress компонента TEdit.
Теперь Enter ведет себя как Tab. Затем, выберите все объекты, которые
должны вести себя как Edit1 (за исключением кнопок) и в Object Inspector
установите обработчик OnKeyPress в Edit1KeyPress. Каждый
выбранный вами объект воспринимает Enter как Tab. Если вы хотите обрабатывать
событие на уровне формы (а не в каждом отдельном компоненте), уберите обработчики
события у всех компонент и создайте FormKeyPress - обработчик OnKeyPress
для формы:
[Назад][Содержание][Вперед] |
Delphi32 | Delphi32 - Всё о Дельфи. WWW.delphi32.narod.ru |