Как заставить работать DB2 через протокол IPX?
Nomadic отвечает:
Связь Win-клиента c DB2 в сети Netware
Hастройка доступа к DB2
1. Связь с использованием протокола IPX/SPX.
Возможны два варианта доступа: через сервер NETWARE; прямая адресация. 1.1. Конфигурация для доступа через сервер.
Замечание: Проверялся доступ через сервера NW 3.11 и 3.12. Для 4.х нужно еще разобраться.
1.1.1. DB2 Сервер
должна быть установлена OS/2 Warp или OS/2 Warp Connect; включена поддержка NETWARE; в CONFIG.SYS в переменную среды DB2COMM добавить (через запятую) IPXSPX и перезагрузить систему; создать командный файл DBIPXSET.CMD следующего вида:
|------------------------------------------------------------------ |db2 update dbm cfg using fileserver objectname dbserver |------------------------------------------------------------------
где - <NWSERVER> - имя сервера;
выполнить командный файл DBIPXSET.CMD; перестартовать сервер базы данных; создать командный файл DBIPXREG.CMD следующего вида:
|---------------------------------------------------------------- |db2 register nwbindery user
|----------------------------------------------------------------
где - <USERNAME> - имя пользователя, обладающего правами администратора на сервере <NWSERVER> ;
выполнить командный файл DBIPXREG.CMD; ответить на запрос пароля.
1.1.2. WINDOWS - клиент
установить WINDOWS 3.1 или WfWG 3.11; установить клиента NETWARE от версии 4.х; при установке влючить поддержку WINDOWS; установить клиента DB2 для WINDOWS; используя программу Client Setup описать новый узел - сервер базы данных :
Name - <любое имя> Protocol - IPX/SPX File server - <NWSERVER> Object name - dbserver описать базу данных и разрешить доступ к ней через ODBC. 1.2. Конфигурация для доступа через прямую адресацию
1.2.1. DB2 Сервер см. п 1.1.1; найти в директории x:\sqllib\misc программу DB2IPXAD.EXE и выполнить ее; записать полученный адрес; 1.2.2. WINDOWS - клиент см. п. 1.1.2. (первые три шага); используя программу Client Setup описать новый узел - сервер базы данных : Name - <любое имя> Protocol - IPX/SPX File server - * Object name - <адрес полученный от DB2IPXAD.EXE> описать базу данных и разрешить доступ к ней через ODBC. [001332]
Почему DB2 ругается на Create Trigger?
Nomadic отвечает:
Я тут писал по поводу того, что у меня не pаботали тpиггеpы. Все дело оказалось в пpавиле написания команды "create trigger". Если все остальные команды коppектно воспpинимаются на любом pегистpе, то эта - только набpанная одними большими буквами. [001389]
Бинарные-, MEMO-, OLE-поля и .DBT-файлы
MEMO-поля хранят данные в .DBT-файлах, состоящих из перечисляемых последовательных блоков (0, 1, 2 и т.д.). Размер блока равен 512 байт. Первый блок в .DBT-файле (нулевой блок) - заголовок .DBT-файла.
MEMO-поле каждой записи .DBF-файла содержит номер (значение указывается в кодовой странице OEM), указывающий на блок с хранимыми данными. Если поле не содержит никаких данных, .DBF-файл будет заполнен пробелами (20h) (а не числами).
В случае изменения данных какого-либо поля, блоки могут изменить свои порядковые номера для отображения новой позиции данных в .DBT-файле.
Данная информация взята из руководства по использованию dBASE III Plus ("Using dBASE III PLUS", Appendix C).
Бинарные, MEMO и OLE-поля хранят данные в .DBT-файлах, состоящих из перечисляемых последовательных блоков (0, 1, 2 и т.д.). Переменная BLOCKSIZE определяет размер каждого блока. Первый блок в .DBT-файле (нулевой блок) - заголовок .DBT-файла.
Бинарное, OLE- или MEMO-поле каждой записи .DBF-файла содержит номер (значение указывается в кодовой странице OEM), указывающий на блок с хранимыми данными. Если поле не содержит никаких данных, .DBF-файл будет заполнен пробелами (20h) (а не числами).
В случае изменения данных какого-либо поля, блоки могут изменить свои порядковые номера для отображения новой позиции данных в .DBT-файле.
Если вы удаляете текст в бинарном, OLE- или МЕМO-поле, в отличие от dBASE III PLUS и dBASE IV, таблица dBASE 5.0 под Windows для ввода нового текста использует удаленную область. dBASE III PLUS всегда добавляет новый текст в конец .DBT-файла. В dBASE III PLUS размер .DBT-файла растет всякий раз при добавления нового текста, даже если перед этим текст был удален.
Данная информация взята из справочника по dBASE под Windows ("dBASE for Windows Language Reference manual", Appendix C).
Предупреждение: У Вас есть право использовать данную техническую информацию с продуктом фирмы Borland только лишь в случае, когда это не противоречит Лицензионному соглашению, поставляемую с программным продуктом. [000063]
DBase и 'особые случаи' BDE
Dbase является причиной бОльшего количества 'special case' в BDE, чем таблицы SQL и Paradox из-за поддержки "Выражений в Индексах" (Expressions in indexes) и т.д., и т.п.. Создание/пересоздание индекса DbiRegenIndexes( Table1.Handle ); { Регенерация всех индексов } create index (зависит от существования выражения)
if (( Pos('(',cTagExp) + Pos('+',cTagExp) ) > 0 ) then Table1.AddIndex( cTagName, cTagExp, [ixExpression]) (<- ixExpression - _литерал_) else Table1.AddIndex( cTagName, cTagExp, []); |
Мастер/Деталь связан с выражением дочернего индекса вызов BDE процедуры DbiLinkDetailToExp() вместо обычной DbiLinkDetail() Упаковка таблиц
with Table1 do StrPCopy( TName, TableName ); Result := DBIPackTable( DbHandle, Handle, TName, szDBASE, TRUE ); |
Установка видимости удаленных записей, on/off (т.е. dBase SET DELETED ON/OFF)
DbiSetProp( hDBIObj(Table1.Handle), curSOFTDELETEON, LongInt(bValue)); |
Установка символа частичного/точного соответствия, on/off (т.е. dBase SET EXACT ON/OFF)
DbiSetProp( hDBIObj(Table1.Handle), curINEXACTON, LongInt(bValue)); |
[000561]
DBASE - Индексы выражений
dBASE - Индексы выражений (Expression Indexes): Введение
Индексы для таблиц dBASE могут базироваться на значениях из отдельной области, нередактируемых данных, или на основе выражений. Индексы выражений, уникальные для индексов dBASE, могут формироваться на основе нескольких полей, измененных значений полей или их комбинаций. Выражение для dBASE-индекса создаются с использованием dBASE-функций и синтаксиса для конкатенации нескольких полей или изменения значений поля для полей, включенных в выражения индекса.
В конце данного совета включены два небольших раздела, описывающих механику создания индексов выражений dBASE, один относится к утилите Database Desktop, другой - к приложениям Delphi.
Индексные выражения на основе множества полей
Функции dBASE доступны для применения в Delphi или Database Desktop для ускоренного использования в выражениях индекса, и затем только в связи с индексами dBASE. То есть, вы не сможете использовать функции dBASE или синтаксис для создания выражения индекса для таблицы Paradox или Local InterBase Server (LIBS). Функции dBASE не могут использоваться при программировании в Delphi. Они доступны только для выражений индесов dBASE. Синтаксис и функции dBASE, которые могут быть использованы для выражений индексов, "расположены" в библиотечном файле Borland Database Engine (BDE) IDDBAS01.DLL.
При создании индекса dBASE, который должен базироваться на двух или более полях таблицы, для которой он создается, два или более поля конкатенируются (связываются вместе) в величине, которая в некоторой степени похожа на Delphi тип String, с использованием синтакса Delphi: оператор "+". Например, выражению необходимо создать индекс, который должен базироваться в первую очередь на основе поля LastName, а затем на основе поля FirstName:
LastName + FirstName В отличие от самого dBASE, такие индексы, основанные на нескольких полях, ограничены использованием таких же полей в таблице. dBASE допускает создание индексов, основанных на нескольких полях, содержищихся в другой таблице. Это позволяет во время создания индекса иметь открытую только "другую" таблицу или использовать таблицу, содержащую индекс.
У индексов с несколькими полями для других типов таблиц (например, Paradox и InterBase), используемые поля должны быть разделены точкой с запятой (;), как показано ниже:
LastName;FirstName В выражениях индекса dBASE, в которых конкатенируются несколько полей, фактическое выражение должно выглядеть следующим образом:
LastName + FirstName При создании индексных выражений, которые конкатенируют два и более поля, все включенные поля должны иметь одинаковый тип. К тому же, если они должны конкатенироваться, вместо складывания, то все поля должны иметь тип String. Например, для двух целочисленных полей, Value1 и Value2, выражение индекса...
Value1 + Value2 ...не вызовет ошибку. Но в этом случае произойдет конкатенация двух значений полей и они суммируются. Таким образом, если Value1 для данной записи содержало 4, а Value2 - 5, результирующий индексный узел будет целой величиной 9, а не строковой конкантенацией "45".
Если поля, включенные в выражение индекса, имеют не строковый тип, они должны быть преобразованы. Вот некоторые функции dBASE, преобразовывающие различные типы данных к строковому типу для использования в выражениях индекса:
STR( [, [, ]]) Преобразовывает dBASE-тип Float или Numeric в Character (String)
DTOS() Преобразовывает значение Date к Character, формат YYYYMMDD
MLINE(, ) Извлекает отдельную строку из Memo-поля как значение Character
Другое ограничение при создании индексов путем конкантенации нескольких полей - максимально допустимая длина индексной величины. Величина, возвращаемая индексным значением, не может превышать 100 символов. Это предел длины значения, возвращаемого выражением, не длина самого выражения. Например, вы не можете создать индекс путем конкантенации двух полей, если они оба имееют длину 255 символов.
Индексные выражения на основе модификации значений полей
Дополнительно к созданию индексов, основанных на конкатенации значений двух и более полей, также возможно создать индекс, основанный на модификации значений полей. Примером этого может служить создание индекса на основе первых трех символов поля типа String, года и месяца поля с типом Date, индекс с использованием конкантенации полей с типами String и Date, где значение поля типа String отсортировано по возрастающей, а поле, имеющим тип Date - по убывающей, и даже создание индекса на основе поля с логическим типом.
Создание индексов на основе модификации значений полей требует, по крайней мере, практическое знание функций dBASE и синтаксиса, поскольку данная технология использует dBASE, а не функции и синтаксис Delphi. Функция dBASE SUBSTR() извлекает подстроку из поля типа String. Delphi-эквивалент данной dBASE-функции - Copy. Но только dBASE функция SUBSTR() может применяться при создании индексного выражения dBASE.
Использование фунций dBASE в индексных выражениях dBASE заключается в простом включении в индексное выражение функции, использование в функциях dBASE-синтаксиса и имени (имен) поля (полей), использующихся в функциях. Например, индексное выражение на основе трех последних символов значения поля типа String с именем Code, имеющим длину 20 символов выглядит так:
RIGHT(Code, 3) Важно соблюдение следующего правила: конструкции индексных выражений dBASE, модифицирующих значения полей, должны возвращать величину с "последовательной длиной" для каждой записи таблицы, т.е. результат не должен содержать граничных пробелов. Например, функция dBASE TRIM() удаляет граничные пробелы (ASCII код 32) из значения поля типа String. Если это было использовано вместе с конкантенацией двух полей, имеющих тип String, где поле не имеет постоянной длины для разных записей, длина результирующего значения будет различная для всех записей. В свете этого рассмотрим следующий пример: построим индексное выражение на основе конкантенации полей LastName и FirstName field, где функция TRIM() применена к полю LastName:
TRIM(LastName) + FirstName Данное выражение не возратит значения "последовательной длины" для всех записей. Если поля LastName и FirstName содержали значения...
LastName FirstName -------- --------- Smith Jonas Wesson Nancy ...то результат использования индексного выражения может быть таким:
SmithJonas WessonNancy Как вы можете наблюдать, длина значения первого поля равна 10 символов, тогда как второго - 11 символов. Узлы индекса для данного индексного выражения должны базироваться на значении поля первой ненумерованной записи. Следовательно, результат выражения индекса для каждого узла должен быть равен 10 символов. В нашем примере результат вычисления для второй записи округляется до "WessonNanc". Все это приводит к тому, что поиск, основанный на поиске полных значений в полях, окончится неудачей.
Решение это дилеммы кроется в не использовании функции TRIM(), а в использовании полной длины значений поля LastName, включая граничные пробелы. В индексах, которые используют функции IIF() для установления порядка одного поля или другого, основанных на сравнении логических выражений в IIF(), если два поля имеют различную длину, более короткое поле должно быть заполнено пробелами до длины большей области. Для примера, создавая индекс с использованием функции IIF(), и индексируя поля Company или Name, базирующийся на поле Category, и где поле Company длиной 40 символов, а поле Name длиной 25 симловов, поле Name необходимо дополнять 15-ю пробелами; например, с помощью dBASE-функции SPACE(). Выражение индекса в этом случае будет таким:
IIF(Category = "B", Company, Name + SPACE(15)) Поиск и выражения индексов dBASE
Выражения индексов dBASE являются исключениями из правил в том, как они обрабатываются Delphi и BDE, по сравнению с обработкой индексов таблиц другого типа, также основанных на множестве полей.
Это вынуждает вынести dBASE-индексы в отдельный класс. Обработка таких индексов в Delphi и BDE отличается от обработки индексов для других типов таблиц. Одно из самых существенных различий заключается в том, что не все поисковые инструменты, основанные на индексах и использующие синтаксис Delphi, могут использовать выражения индексов dBASE. FindKey, FindNearest и GotoKey методы компонента TTable не годятся для работы с выражениями индексов. При попытке использования FindKey вы получите сообщение об ошибке: "Field index out of range." (Индекс поля за границами диапазона). При попытке использования метода GotoKey может произойти та же ошибка, или табличный курсор может остаться на месте (визуально искомая величина не найдена). С выражениями индексов может использоваться только метод GotoNearest. Но даже GotoNearest может не работать с некоторыми индексными выражениями. Только с помощью эксперимента можно установить - работает ли метод GotoNearest с данным индексным выражением.
Фильтрация индексных выражений dBASE
Как и основанный на индексах поиск, индексные выражения dBASE при использовании фильтров Delphi также имеют некоторые исключения.
С активным индексным выражением метод SetRange компонента TTable приводит к следующей ошибке: "Field index out of range." (Индекс поля за границами диапазона). Тем не менее, с тем же активным индексным выражением методы SetRangeStart и SetRangeEnd успешно фильтруют набор данных.
Например, выражение индекса с конкантенацией поля LastName и активного FirstName, в приведенном ниже коде, использующем метод FindKey (предполагающий фильтрацию тех записей, где первый символ поля LastName содержит "S"), "вылетит" с ошибкой:
begin Table1.SetRange(['S'], ['Szzz']) end; |
Код, приведенный ниже, использует то же активное выражение индекса, но используемый фильтр поля LastName правильно отфильтрует данные, и не вызовет ошибки:
begin with Table1 do begin SetRangeStart; FieldByName('LastName').AsString := 'S'; SetRangeEnd; FieldByName('LastName').AsString := 'Szzz'; ApplyRange; end; end; |
И, так же, как и в случае основанного на индексах поиска, успех применения фильтра целиком и полностью зависит от самого индексного выражения. Использование методов SetRangeStart и SetRangeEnd в приведенном примере работало бы с индексом, построенным на основе простой конкантенации двух полей, имеющих тип String. Но если вместо этого выражение было основано на одном или нескольких полях с использованием функции IIF(), тот же самый процесс фильтрации потерпел бы неудачу (хотя и без ошибки).
Несколько полезных советов при создании индексных выражений dBASE
Вот некоторые "удобные" индексные выражения dBASE. Некоторые интуитивно-понятные в достижении своей цели, другие немного "заумные".
Сортировка поля типа Character символов по-возрастающей, поля Date - по-убывающей
С полем типа Character и именем Name, и полем типа Date и именем OrdDate:
Name + STR(OrdDate - {12/31/3099}, 10, 0) Сортировка поля типа Character по-возрастающей и поля типа Numeric (или Float) по-убывающей
C полем типа Character и именем Company, и полем типа Numeric и именем Amount (поле Amount имеет длину 9 цифр с двумя цифрами после десятичной запятой):
Company + STR(Amount - 999999.99, 9, 2) Сортировка логического поля
Для того, чтобы записи со значением True располагались впереди записей со значением False в логическом поле с именем Paid, выполните следующее:
IIF(Paid, "A", "Z") Два поля с типом Numeric (или Float)
Предположим, у нас имеется два поля типа Numeric с пятью и двумя десятичными разрядами, первое поле с именем Price, второе - Quantity:
STR(Price, 5, 2) + STR(Quantity, 5, 2) Сортировка одного из двух полей в зависимости от выполнения логического условия
Сортировка имен месяцев в поле, имеющим тип Character
Предположим, поле содержит имена месяцев на английском языке ("Jan," "Feb" и т.д.), и его необходимо расположить в соответствующем порядке (имя поля M):
IIF(M="Jan", 1, IIF(M="Feb", 2, IIF(M="Mar", 3, IIF(M="Apr", 4, IIF(M="May", 5, IIF(M="Jun", 6, IIF(M="Jul", 7, IIF(M="Aug", 8, IIF(M="Sep", 9, IIF(M="Oct", 10, IIF(M="Nov", 11, 12))))))))))) (Вышеприведенный код - единственная строка кода, разбирая на несколько из-за ограничений ширины страницы.)
Сортировка по первой строке Memo-поля
Для Memo-поля с именем Notes:
MLINE(Notes, 1) Сортировка по средним трем символам в девятисимвольном поле типа long
Для девятисимвольного поля типа long с именем StockNo:
SUBSTR(StockNo, 4, 3) Создание индексных выражений dBASE в Database Desktop
В утилите Database Desktop, индексы могут создаваться как для новой таблицы (во время ее создания), так и для существующей, путем ее реструктуризации. В обоих случаях используется диалог "Define Index", использующийся для создания одного или более индексов таблицы.
Для вывода диалога создания индекса ("Create Index") во время создания новой таблицы, в диалоге создания dBASE таблицы ("Create dBASE Table") (показ структуры), выберите в списке "Table Properties" (свойства таблицы) пункт "Indexes" (индексы) и нажмите на кнопку "Define".
Чтобы вывести диалог создания индекса ("Create Index") при создании индекса для существующей таблицы, выберите Utilities|Restructure, выберите файл с таблицей в диалоге выбора файла ("Select File"), и в диалоге реструктуризации таблицы dBASE ("Restructure dBASE Table") (показ структуры таблицы) выберите в списке "Table Properties" (свойства таблицы) пункт "Indexes" (индексы) и нажмите на кнопку "Define".
Только в диалоге создания индекса ("Create Index"), выражения индекса могут создаваться щелчком на кнопке "Expression Index" (индеск выражения) и вводом выражения в поле редактирования "Expression Index". Для ассистирования данного процесса, вы можете дважды щелкнуть на имени поля с списке полей, после чего имя поля будет помещено в область редактирования "Index Expression" в текущей точке ввода (позиция курсора).
Как только нужное выражение составлено, нажмите кнопку OK. Введите имя нового индексного тэга в поле редактирования "Index Tag Name" (имя индексного тэга") в диалоге "Save Index As" (сохранить индекс как...) и нажмите "OK". (Помните, имена тэгов индексов dBASE не могут превышать десяти символов и должны соблюдать соглашения об именах dBASE.)
Создание индексных выражений dBASE в приложениях Delphi
dBASE-индексы могут создаваться программным путем в Delphi-приложениях как для новой таблицы (метод CreateTable компонента TTable), так и для существующей.
Для создания индекса как части новой таблицы, необходимо вызваться метод Add свойства IndexDefs компонента TTable. В нашем случае необходимо включить в набор флажков индекса флажок ixExpression. Данный флажок уникален для индексов таблиц dBASE, и может использоваться только с индексными выражениями dBASE. Для примера:
with Table1 do begin Active := False; DatabaseName := 'Delphi_Demos'; TableName := 'CustInfo'; TableType := ttdBASE; with FieldDefs do begin Clear; Add('LastName', ftString, 30, False); Add('FirstName', ftString, 20, False); end; with IndexDefs do begin Clear; Add('FullName', 'LastName + FirstName', [ixExpression]); end; CreateTable; end; |
Добавление индекса к существующей таблицы осуществляется вызовом метода AddIndex компонента TTable. Кроме того, флажки индекса должны включать в себя значение TIndexOptions ixExpression.
Table1.AddIndex('FullName', 'LastName + FirstName', [ixExpression]); |
Изучение функций и синтаксиса dBASE
Для создания индексных выражений dBASE могут использоваться только функции и синтакс, относящиеся к обработке данных. Тем не менее, полный список и описание данных функций выходит за рамки данного совета. Для получения дополнительной информации о dBASE-функциях обработки данных, обратитесь к руководству "dBASE Language Reference" или книгам и справочникам по dBASE третьих фирм.
[000630]
DBFSeek и DBFLocate
Надежней и намного быстрее (если вы ищите отдельные записи) выполнить поиск строки с помощью Seek (если найдена первая запись), или выполнить Locate (индекс не требуется)
например
Table1.UpdateCursorPos; if DBFSeek( Table1, xVal1 ) then {_не_ delphi-функция - смотри ниже} begin if DBFLocate( Table1, 'CUSTNAME', xVal2 ) then {_не_ delphi-функция - модификация из faq) begin ... делаем все, что необходимо end; end; |
Tom
ps. DBFLocate - модифицированная из faq фунция fieldname DBFSeek - функция, найденная методом проб и ошибок! - значительно лучшая (IMHO) чем setkey...fieldbyname1...fieldbyname2...gotokey, используемые для выражений индексов dBase за первым полем. Вы можете использовать FindKey для dBase индексов, состоящих из одного поля, вопреки мнению других участников форума.
{============================================================ { DBFSeek { поиск величины с использованием индекса - простой путь {============================================================} function DBFSeek( const Table1: TTable; const sValue: string): boolean; var sExpValue : DBIKEYEXP; bmPos : TBookMark; nOrder : integer; begin Result := False; with Table1 do begin if (Active) and (Length(IndexName) > 0) then begin bmPos := GetBookMark; DisableControls; StrPCopy( sExpValue, sValue ); if (DbiGetRecordForKey( Handle, True, 0, strlen(sExpValue), @sExpValue, nil ) = DBIERR_NONE ) then Result := True else GotoBookMark( bmPos); FreeBookMark( bmPos ); EnableControls; end; end; end; {================================================================================== { DBFLocate { поиск величины, не связанный с ключевым полем { замена из faq, теперь акцептует fieldname, величина может быть частичной {================================================================================} function DBFLocate( const Table1: TTable; const sFld, sValue: string): boolean; var bmPos : TBookMark; bFound : boolean; len : integer; begin Result := False; if (not StrEmpty(sValue)) and (not StrEmpty(sFld) )then begin with Table1 do begin DisableControls; bFound := False; bmPos := GetBookMark; len := Length(sValue); First; while not EOF do begin if FieldByName(sFld).AsString <> sValue then Next else begin Result := True; bFound := True; Break; end; end; if (not bFound) then GotoBookMark( bmPos); FreeBookMark( bmPos ); EnableControls; end; end; end; |
[000563]
Формат и размер dBase-поля
Во время выполнения программы мне необходимо получать размеры и десятичные порядки полей таблицы DBase.
Следующий код иллюстрирует необходимые вызовы BDE:
procedure GetdBaseFieldTypes( t : TTable; var l : TStringList); var pF: pFLDDesc; cProps: CURProps; p: pFLDDesc; i: Byte; w : Word; s : string; oldmode : LongInt; begin Check(DbiGetCursorProps(t.Handle,cProps)); Check(DbiGetProp(hDBIObj(t.Handle),curXLTMODE,oldmode,SizeOf(LongInt),w)); Check(DbiSetProp(hDBIObj(t.Handle), curXLTMODE, LongInt(xltNONE))); try if MaxAvail < (cProps.iFields*SizeOf(FLDDesc)) then raise EOutofMemory.Create('Недостаточно памяти для процесса'); GetMem(pF,(cProps.iFields*SizeOf(FLDDesc))); Check(DbiGetFieldDescs(t.Handle,pF)); p := pF; for i := 1 to cProps.iFields do begin with p^ do begin s := IntToStr(iFldNum)+' : '+StrPas(szName)+' : '; case iFldType of fldDBCHAR : begin { Char string, строка символов } s := s + 'CHARACTER('+IntToStr(iUnits1)+')'; end; fldDBNUM : begin { Number, число } s := s + 'NUMBER('+IntToStr(iUnits1)+','+InttoStr(iUnits2)+')'; end; fldDBMEMO : begin { Memo (blob), МEMO-BLOB-поле } s := s + 'MEMO'; end; fldDBBOOL : begin { Logical, лочическая величина } s := s + 'LOGICAL'; end; fldDBDATE : begin { Date, поле даты } s := s + 'DATE'; end; fldDBFLOAT : begin { Float, числа с плавающей точкой } s := s + 'FLOAT('+IntToStr(iUnits1)+','+InttoStr(iUnits2)+')'; end; fldDBLOCK : begin { Логический тип LOCKINFO } s := s + 'LOCKINFO'; end; fldDBOLEBLOB : begin { OLE object (blob), OLE-объект, BLOB-поле } s := s + 'OLE'; end; fldDBBINARY : begin { Binary data (blob), двоичные данные, BLOB-поле } s := s + 'BINARY'; end; else s := s + 'НЕИЗВЕСТНО'; end; end; l.Add(s); Inc(p); end; finally Check(DbiSetProp(hDBIObj(t.Handle),curXLTMODE,oldmode)); FreeMem(pF,(cProps.iFields*SizeOf(FLDDesc))); end; end; |
- Eryk Bottomley [000830]
Как открыть индексированную таблицу dBase, если отсутствует файл индекса?
Nomadic советует:
Для dBase-таблицы встроенными средствами ты не перестроишь индекс, если его нет. Для этой цели мне пришлось написать процедуру для физического удаления признака индексации в самом dbf-файле и после её применения добавлять индексы заново.
Для этого в заголовок файла dbf по смещению 28(dec) записываешь 0.
По другому никак не выходит(я долго бился)- вот для Paradox таблиц все Ok.
С помощью BDE Callbacks. Пpимеp для Delphi 2.0, на пеpвом не пpовеpял:
=== Callback.pas ===
unit Callback; interface uses BDE, Classes, Forms, DB, DBTables; type TForm1 = class(TForm) Table1: TTable; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private CBack: TBDECallback; // опpеделение BDE CallBack CBBuf: CBInputDesc; // пpосто буфеp function CBFunc(CBInfo: Pointer): CBRType; // Callback-функция public end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.FormCreate(Sender: TObject); begin Session.Open; // В это вpемя сессия ещё не откpыта CBack := TBDECallback.Create( Session {Hапpимеp}, nil, cbINPUTREQ, @CBRegBuf, SizeOf(CBBuf), CBFunc, False); // Опpеделили Callback Table1.Open; //^^^^^^^^^^^ - здесь возможна ошибка с индексом, etc. end; procedure TForm1.FormDestroy(Sender: TObject); begin CBack.Free; // Освобождаем CallBack end; function TForm1.CBFunc(CBInfo: Pointer): CBRType; begin with PCBInputDesc(CBInfo)^ do case eCbInputId of cbiMDXMissing {, cbiDBTMissing - можно ещё и очищать BLOB-поля}: begin iSelection := 3; // Hомеp ваpианта ответа (1-й - откpыть только // для чтения, 2-й - не откpывать, 3-й - отсоединить индекс). // Возможный источник непpиятностей: а вдpуг в последующих веpсиях // BDE номеpа будут дpугими? Result := cbrCHKINPUT; // Обpабатывать введённый ответ end; end; end; end. |
=== Callback.pas ===
PS: конечно, это лишь пpимеp, делающий минимум необходимого. В pамках данного письма невозможно дать какое-то описание BDE Callbacks. Инфоpмацию я взял из BDE32.HLP, BDE.INT и DB.PAS. В VCL.HLP совсем ничего нет по этому поводу.
Вообще, pуки бы отоpвал тем, кто писал спpавку по Дельфям: я неделю мучался с сабжем, пока случайно не набpёл на Callbacks. [001251]
Memo-поля и .DBT-файлы
MEMO-поля хранят данные в .DBT-файлах, состоящих из перечисляемых последовательных блоков (0, 1, 2 и т.д.). Переменная BLOCKSIZE определяет размер каждого блока. Первый блок в .DBT-файле (нулевой блок) - заголовок .DBT-файла.
MEMO-поле каждой записи .DBF-файла содержит номер (значение указывается в кодовой странице OEM), указывающий на блок с хранимыми данными. Если поле не содержит никаких данных, .DBF-файл будет заполнен пробелами (20h) (а не числами).
В случае изменения данных какого-либо поля, блоки могут изменить свои порядковые номера для отображения новой позиции данных в .DBT-файле.
Данная информация взята из справочника по dBASE IV ("dBASE IV Language Reference", Appendix D).
MEMO-поля хранят данные в .DBT-файлах, состоящих из перечисляемых последовательных блоков (0, 1, 2 и т.д.). Переменная BLOCKSIZE определяет размер каждого блока. Первый блок в .DBT-файле (нулевой блок) - заголовок .DBT-файла.
MEMO-поле каждой записи .DBF-файла содержит номер (значение указывается в кодовой странице OEM), указывающий на блок с хранимыми данными. Если поле не содержит никаких данных, .DBF-файл будет заполнен пробелами (20h) (а не числами).
В случае изменения данных какого-либо поля, блоки могут изменить свои порядковые номера для отображения новой позиции данных в .DBT-файле.
Если вы удаляете текст в МЕМO-поле, в отличие от dBASE III PLUS, таблица dBASE 5.0 под DOS для ввода нового текста использует удаленную область. dBASE III PLUS всегда добавляет новый текст в конец .DBT-файла. В dBASE III PLUS размер .DBT-файла растет всякий раз при добавления нового текста, даже если перед этим текст был удален.
Данная информация взята из справочника по dBASE под DOS ("dBASE for DOS Language Reference manual", Appendix C).
Обработка исключения `index not found`
Как мне открыть таблицу dBASE без требуемого MDX-файла? Я постоянно получаю исключение "Index not found..." (индекс не найден).
Во время создания таблицы dBASE с production-индексом (MDX) в заголовке DBF-файла устанавливается специальный байт. При последующем открытии таблицы, dBASE-драйвер читает этот специальный байт и, если он установлен, он также пытается открыть файл MDX. Если попытка открыть файл MDX заканчивается неудачей, возникает исключительная ситуация.
Для решения этой проблемы вам необходимо обнулить этот байт (28-й десятичный байт) в файле DBF, избавляющий таблицу от зависимости MDX-файла.
Нижеприведенным модуль является простым примером того, как можно обработать исключение при открытии таблицы, обнулив этот байт в DBF-файле и вновь открыв таблицу.
unit Fixit; interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, DB, DBTables, Grids, DBGrids; type TForm1 = class(TForm) Table1: TTable;; Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} const TheTableDir = 'c:\temp\'; TheTableName = 'animals.dbf'; procedure RemoveMDXByte(dbFile: String); { Данная процедура использует в качестве параметра имя файла DBF } { и исправляет его заголовок для того, чтобы не требовать MDX-файл } const Value: Byte = 0; var F: File of byte; begin AssignFile(F, dbFile); Reset(F); Seek(F, 28); Write(F, Value); CloseFile(F); end; procedure TForm1.Button1Click(Sender: TObject); { Данная процедура вызывается в ответ на нажатие кнопки. Она } { пытается открыть таблицу и, если файл .MDX не найден, } { DBF-файл исправляется и управление вновь передается данной } { процедуре для повторного открытия таблицы, но уже без MDX } begin try { устанавливаем каталог таблицы } Table1.DatabaseName := ThheTableDir; { устанавливаем имя таблицы } Table1.TableName := TheTableName; { пытаемся открыть таблицу } Table1.Open; except on E:EDBEngineError do { Нижеследующее сообщение указывает на то, что файл MDX не найден: } if Pos('Index does not exist. File', E.Message)>0 then begin { Сообщаем пользователю о наличии проблемы. } MessageDlg('Файл MDX не найден. Попытка открытия без индекса.', mtWarning, [mbOk], 0); { удаляем байт MDX из заголовка таблицы } RemoveMDXByte(TheTableDir + TheTableName); { Посылаем кнопке сообщение для эмуляции ее нажатия. } { Этот трюк заставит данную процедуру выполниться } { повторно, и таблица будет открыта без файла MDX } PostMessage(Button1.Handle, cn_Command, bn_Clicked, 0); end; end; end; end. |
[000531]
Определение номера записи в таблице dBASE
Таблицы dBASE применяют довольно статическую систему нумерации записей. Номер записи для данной записи (извините за тавтологию) отражает физическую позицию в табличном файле. Эти номера записей не изменяются вследствие фильтрации, упорядочивания данных или сортировки. К примеру, первая запись, хранящаяся в .DBF файле, будет иметь номер записи 1. Возможно, после некоторого упорядочивания индекса, запись будет последней из 100 записей. В этом случае запись должна оставаться с тем же номером, а не номером 100, отражающим новую позицию в сортированном наборе данных. Это противоречит таблицам Paradox, где соблюдается последовательная нумерация. Последовательная нумерация Paradox похожа на нумерацию записей dBASE, за исключением большей гибкости и отражению в номере записи ее текущей позиции в наборе данных. То есть, запись может не всегда иметь номер, установленный для нее фильтром набора данных, уменьшившим общее число записей, или при активном индексе, из-за чего может измениться отображаемый порядок записи.
В приложениях для работы с базами данных, созданных с помощью Delphi и Borland Database Engine (BDE), DB-компонентами не предусмотрено извлечение и определение записи таблицы dBASE. Такая операция, тем не менее, возможна с помощью вызова из вашего приложения функций BDE.
Существует несколько функций BDE, возвращающих информацию о текущей записи dBASE, например, ее номер. На самом деле, любая функция, заполняющая структуру BDE pRECProps, вполне достаточна. Например, функции BDE DbiGetRecord, DbiGetNextRecord и DbiGetPriorRecord. Естественно, только первая из них реально позволяет получить информацию о текущей записи. Две других перемещают при вводе указатель на запись, подобно методам Next и Prior компонентов TTable и TQuery.
Структура pRECProps состоит из следующих полей:
iSeqNum: тип LongInt; определяет текущий номер записи (относительно набора данных, включая фильтрацию и сортировку индекса); используется, если тип таблицы поддерживает последовательную нумерацию (только Paradox).
iPhyRecNum: тип LongInt; определяет номер записи; используется, если тип таблицы поддерживает физические номера записи (только dBASE).
bRecChanged: тип Boolean; в настоящее время не используется.
bSeqNumChanged: тип Boolean; в настоящее время не используется.
bDeleteFlag: тип Boolean; указывает на удаленную запись; используется, если тип таблицы поддерживает "мягкое" удаление (только dBASE).
Одна из этих BDE-функций может быть вызвана из вашего приложения для заполнения данной структуры, из которой затем может быть извлечен физический номер записи. Ниже - пример использования для этой цели функции DbiGetRecord.
function RecNo(ATable: TTable): LongInt; var R: RECProps; rslt: DbiResult; Error: array [0..255] of Char; begin ATable.UpdateCursorPos; rslt := DbiGetRecord(ATable.Handle, dbiNoLock, nil, @R); if rslt = DBIERR_NONE then Result := R.iPhyRecNum else begin DbiGetErrorString(rslt, Error); ShowMessage(StrPas(Error)); Result := -1; end; end; |
Для вызова любой BDE-функции из приложения Delphi, модули-обертки BDE DbiTypes, DbiErrs и DbiProcs должны быть включены в секцию Uses модуля, из которого они будут вызываться (секция Uses здесь не показана). Для того, чтобы сделать функции более транспортабельными, они не имеют прямой ссылки на компонент TTable, но указатель на TTable передается как параметр. Если эта функция используется в модуле, который не ссылается на модули Delphi DB и DBTables, они должны быть добавлены, иначе ссылки на компонент TTable будут недействительными.
Метод TTable UpdateCursorPos вызывается для гарантии синхронизации номера текущей записи в компоненте TTable и связанной с ним таблицы.
В случае ошибок BDE функций, исключительная ситуация ими не генерируется. Вместо этого они возвращают значение BDE-типа DbiResult, указывающее на успешное завершение или ошибку операции. Возвращаемое значение должно быть получено и обработано внешним приложением, с выполнением соответствующих действий. Любой результат, кроме DBIERR_NONE, указывает на неудачное выполнение функции. В этом случае может быть осуществлено дополнительное действие (как в примере выше), где с помощью BDE функции DbiGetErrorString код ошибки переводится в удобночитаемое сообщение. В этом примере возвращаемое в DbiGetRecord значение сохраняется в переменной rslt, а затем для определения успешности вызова функции сравнивается с DBIERR_NONE.
Если вызов DbiGetRecord был успешным, физический номер записи из поля iPhyRecNum структуры pRECProps сохраняется в переменной Result, которая является возвращаемой функцией величиной. Чтобы указать на то, что функция потерпела неудачу (т.е., вызов фунции DbiGetRecord окончился неудачно), вместо номера записи возвращается отрицательная величина. Значение ее может быть произвольным (отрицательная величина совместимого типа) и отдается на усмотрение программисту. [001262]
Определение удаления записей в .DBF
Взято из "Dtopics Database 1.10 from 3K computer Consultancy":
Dbase в BDE имеет большее количество ситуаций 'особого случая', чем таблицы SQL и Paradox, поскольку данный формат поддерживает выражения в индексах и прочие характеристики, например: Создание и пересоздание индекса
- DbiRegenIndexes( Table1.Handle ); { Регенерация всех индексов } |
- создание индекса (зависит от того, существует ли выражение или нет)
if (( Pos('(',cTagExp) + Pos('+',cTagExp) ) > 0 ) then Table1.AddIndex( cTagName, cTagExp, [ixExpression]) ( else Table1.AddIndex( cTagName, cTagExp, []); |
Связки Master/Detail в выражениях дочерних индексов
- вызов процедуры BDE DbiLinkDetailToExp() вместо обычной DbiLinkDetail()
Пакование таблиц
with Table1 do StrPCopy( TName, TableName ); Result := DBIPackTable( DbHandle, Handle, TName, szDBASE, TRUE ); |
Задание видимости удаленных записей - вкл/выкл (например, dBase SET DELETED ON/OFF)
DbiSetProp( hDBIObj(Table1.Handle), curSOFTDELETEON, LongInt(bValue)); |
Задание частичного/полного соответствия символов - вкл/выкл (например, dBase SET EXACT ON/OFF)
DbiSetProp( hDBIObj(Table1.Handle), curINEXACTON, LongInt(bValue)); |
<------- Конец ---------->
Ну и теперь сами вопросы:
<------Содержание-------->
"Как мне увидеть записи dBASE, помеченные для удаления?"
В обработчике события AfterOpen вызовите приведенную ниже функцию. Включите DBITYPES, DBIERRS, DBIPROCS в список используемых модулей. Для вызова функции передайте ей в качестве аргумента имя TTable и TRUE/FALSE в зависимости от необходимости показа/скрытия удаленных записей. Пример:
procedure TForm1.Table1AfterOpen(DataSet: TDataset);
begin
SetDelete(Table1, TRUE);
end;
procedure SetDelete(oTable:TTable; Value: Boolean); var rslt: DBIResult; szErrMsg: DBIMSG; begin try Table.DisableControls; try rslt := DbiSetProp(hDBIObj(oTable.Handle), curSOFTDELETEON, LongInt(Value)); if rslt <> DBIERR_NONE then begin DbiGetErrorString(rslt, szErrMsg); raise Exception.Create(StrPas(szErrMsg)); end; except on E: EDBEngineError do ShowMessage(E.Message); on E: Exception do ShowMessage(E.Message); end; finally Table.Refresh; Table.EnableControls; end; end; |
"Могу ли я создать в табличной сетке колонку, в которой будут показываться записи, помеченные для удаления из таблицы dBASE?"
Создайте вычисляемое поле, затем в обработчике события таблицы OnCalcField замените его таким образом:
procedure TForm1.Table1CalcFields(DataSet: TDataset); var RCProps : RecProps; Result : DBIResult; begin Result := DbiGetRecord(Table1.Handle, dbiNo |
Рукописи не горят... [001258]
Пакование таблиц dBASE
Для упаковки таблицы dBASE убедитель в том, что таблица открыта в монопольном (exclusive) режиме и вызывайте DbiPackTable. Пример:
Table1.Close ; Table1.Exclusive := TRUE ; Table1.Open ; DbiPackTable( Table1.DBHandle, Table1.Handle, nil, nil, TRUE ) ; |
Убедитесь, что DBITYPES, DBIPROCS, DBIERRS включены в секцию USES вашего модуля и, в случае запуска из-под IDE, в режиме проектирования таблица не активна.
OAmiry/Borland [000372]
Пакование таблиц dBASE II
Упаковка таблиц dBASE требует вызова BDE функции DbiPackTable. Пример ее использования показан ниже, включая проверку на ошибки. Чтобы воспользоваться функцией DbiPackTable, вызывающий модуль должен в своей секции uses иметь модули-обертки BDE DbiTypes, DbiErrs и DbiProcs.
При неудачном вызове DbiPackTable, сообщение об ошибке не генерится. Для того, чтобы понять как функция сработала, вам необходимо проверить возвращаемое ею значение. В случае успешного выполнения возвращаемое значение равно DBIERR_NONE. Любое другое значение указывает на ошибку, а с помощью него можно определить саму ошибку, ее причину, и наметить действия, необходимые для ее устранения.
Вот сам пример:
procedure TForm1.Button1Click(Sender: TObject); var Error: DbiResult; ErrorMsg: String; Special: DBIMSG; begin table1.Active := False; try Table1.Exclusive := True; Table1.Active := True; Error := DbiPackTable(Table1.DBHandle, Table1.Handle, nil, szdBASE, True); Table1.Active := False; Table1.Exclusive := False; finally Table1.Active := True; end; case Error of DBIERR_NONE: ErrorMsg := 'Успешно'; DBIERR_INVALIDPARAM: ErrorMsg := 'Указанное имя таблицы или указатель на имя таблицы ' + 'равен NULL'; DBIERR_INVALIDHNDL: ErrorMsg := 'Указанный дескриптор базы данных или курсора ' + 'неверен или равен NULL'; DBIERR_NOSUCHTABLE: ErrorMsg := 'Таблица с таким именем не существует'; DBIERR_UNKNOWNTBLTYPE: ErrorMsg := 'Неизвестный тип таблицы'; DBIERR_NEEDEXCLACCESS: ErrorMsg := 'Таблица открыта не в эксклюзивном режиме'; else DbiGetErrorString(Error, Special); ErrorMsg := '[' + IntToStr(Error) + ']: ' + Special; end; MessageDlg(ErrorMsg, mtWarning, [mbOk], 0); end; |
[001308]
Пакование таблиц dBASE III
Для упаковки таблицы dBASE, открытой с помощью TTable, воспользуйтесь функцией BDE DbiPackTable. Для этого достаточно сделать две операции: Добавьте в секцию uses следующие модули:
{ Для Delphi 1.0: } DBITYPES, DBIPROCS и DBIERRS;
{ Для Delphi 2.0: } BDE;
Затем вызовите BDE функцию DbiPackTable следующим образом:
Check(DbiPackTable(Table1.DbHandle, Table1.Handle, Nil, szDBASE, TRUE)); |
Примечания: Таблица должна быть открыта в эксклюзивном режиме. При вызове функций API BDE используйте процедуру Check. Check в случае ошибки при вызове BDE генерирует исключительную ситуацию. [001310]
Пакование таблиц dBASE IV
Nomadic советует:
Для dBase:
uses DbiProcs; with Table do begin OldState := Active; Close; Exclusive := True; Open; DbiPackTable( DBHandle, Handle, nil, nil, True ); {^ здесь можно добавить check()} Close; Exclusive := False; Active := OldState; { при желании можно сохранить закладку } end; |
Pavel Kulchenko
(2:465/66)
Пpимеp для Paradox:
Uses BDE; // for D3, для D2 не помню (что-то типа DbiProcs и еще что-то) // для пpимеpа tLog : TTable; // таблица, юзающая d:\db\log.db var TblDesc: CRTblDesc; rslt: DBIResult; Dir: String; //имеется в виду huge string т.е. {$H+} hDb: hDbiDb; begin tLog.Active := False; //деактивиpуем TTable SetLength( Dir, dbiMaxNameLen + 1 ); DbiGetDirectory( tLog.DBHandle, False, PChar( Dir ) ); SetLength( Dir, StrLen( PChar( Dir ) ) ); DbiOpenDatabase( nil, nil, dbiReadWrite, dbiOpenExcl, nil, 0, nil, nil, hDb ); DbiSetDirectory( hDb, PChar( Dir ) ); FillChar( TblDesc, sizeof( CRTblDesc ), 0 ); StrPCopy( TblDesc.szTblName, 'd:\db\log.db' ); // здесь должно быть полное имя файла //котоpое можно: а) ввести pуками; //б) вытащить из пpопеpтей таблицы; //в) вытащить из алиаса; //г) см. FAQ StrCopy( TblDesc.szTblType, szParadox ); //BTW тут может и szDBase стоять TblDesc.bPack := TRUE; DbiDoRestructure( hDb, 1, @TblDesc, nil, nil, nil, False ); DbiCloseDatabase(hDb); end; // можно еще чеки ввести, но облом :-) |
[001355]
Пароль на dBASE-файлы
dBase-файлы не поддерживают пароли. Естественно, вы можете создать свои собственные методы поддержки паролей. Но это будет работать только с вашими приложениями. Боюсь, что при наличии тысяч читателей/конверторов dBase, этот способ не годится. [001314]
Показ меток 'удаленных' записей в dBASE-файлах
Для начала вы должны включить SoftDeletes, после чего вы сможете просматривать записи, помеченные к удалению. В противном случае, вы их не увидите. По умолчанию, для файлов DBF, SoftDeletes установлен в False. Вот логика работы:
procedure TForm1.Button1Click(Sender: TObject); var B: BOOL; W: Word; begin Check(DbiSetProp(hDBIObj(Table1.Handle), curSOFTDELETEON, longint(True))); { Проверяем, что это работает } Check(DbiGetProp(hDBIObj(Table1.Handle), curSOFTDELETEON, @B, sizeof(B), W)); if B = False then Label2.Caption := 'Не помечена' else Label2.Caption := 'Помечена'; end; |
Когда указатель на запись указывает на запись, которую вы хотите удалить, используйте следующую логику:
Table1.UpdateCursorPos; Check(DbiUndeleteRecord(Table1.Handle)); |
Метод UpdateCursorPos устанавливает основной курсор BDE на позицию курсора текущей записи, который существуют только для того, чтобы все работало правильно. Вам нужно только вызвать этот метод прямым вызовом одной из BDE API функций (такой как, например, DbiUndeleteRecord).
Ну и, наконец, чтобы все работало, поместите модули DBIPROCS и DBITYPES с список USES. [001333]
Показ удаленных записей в таблице dBASE
В таблицах dBASE записи не удаляются до тех пор, пока таблица не будет упакована. Пока же это не произойдет, удаленные записи остаются в таблице, только имеют при этом флажок "к удалению". Для того, чтобы показать эти существующие, но не отображаемые записи, существует функция ShowDeleted(), которая использует функцию BDE API DbiSetProp(), показывающая записи, помеченные к удалению. При использовании этой функции нет необходимости закрывать и вновь открывать таблицу. ShowDeleted() в качестве параметров передается TTable и логическое значение. Логический параметр указывает на необходимость показа удаленных записей.
Демонстрационный проект:
unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, DBCtrls, Grids, DBGrids, DB, DBTables; type TForm1 = class(TForm) Table1: TTable; DataSource1: TDataSource; DBGrid1: TDBGrid; DBNavigator1: TDBNavigator; CheckBox1: TCheckBox; procedure CheckBox1Click(Sender: TObject); public procedure ShowDeleted(Table: TTable; ShowDeleted: Boolean); end; var Form1: TForm1; implementation uses DBITYPES, DBIERRS, DBIPROCS; {$R *.DFM} procedure TForm1.ShowDeleted(Table: TTable; ShowDeleted: Boolean); var rslt: DBIResult; szErrMsg: DBIMSG; begin Table.DisableControls; try Check(DbiSetProp(hDBIObj(Table.Handle), curSOFTDELETEON, LongInt(ShowDeleted))); finally Table.EnableControls; end; Table.Refresh; end; procedure TForm1.CheckBox1Click(Sender: TObject); begin ShowDeleted(Table1, CheckBox1.Checked); end; end. |
[001312]
Структура заголовка файла данных для таблицы dBASE III PLUS.
Заголовок табличного файла | ||
Байт | Содержание | Описание |
0 | 1-й байт | Определение наличия MEMO-файла в таблице dBASE III PLUS (03h без MEMO-файла (.DBT-файл;) 83h с MEMO-файлом). |
1-3 | 3 байта | Дата последнего обновления в формате YYMMDD |
4-7 | 32-битное число | Количество записей в таблице |
8-9 | 16-битное число | Количество байтов, занимаемых заголовком |
10-11 | 16-битное число | Количество байтов, занимаемых записью |
12-14 | 3 байта | Зарезервированная область |
15-27 | 13 байт | Зарезервировано для сетевой версии dBASE III PLUS |
28-31 | 4 байта | Зарезервированная область |
32-n | 32 байта | Массив с описаниями полей (структура каждого такого описания показана ниже) |
n+1 | 1 байт | Хранится значение 0Dh, выполняющее роль терминатора описаний полей |
n - последний байт массива с описаниями полей. Размер массива зависит от количества полей в табличном файле.
Описание поля таблицы | ||
Байт | Содержание | Описание |
0-10 | 11 байт | Имя поля в ASCII (заполнено нулями). |
11 | 1 байт | Тип поля в ASCII (C, D, L, M или N) |
12-15 | 4 байта | Адрес данных поля (ссылка на память, а не на диск) |
16 | 1 байт | Размер поля в бинарном формате |
17 | 1 байт | Порядковый номер поля в бинарном формате |
18-19 | 2 байта | Зарезервировано для сетевой версии dBASE III PLUS |
20 | 1 байт | ID рабочей области |
21-22 | 2 байта | Зарезервировано для сетевой версии dBASE III PLUS |
23 | 1 байт | Флаг установки поля |
24-31 | 1 байт | Зарезервированная область |
Структура файла | ||
Байт | Содержание | Описание |
0 | 1-й байт | Контроль файла dBASE IV: биты 0-2 указывают номер версии, бит 3 - наличие MEMO-файла dBASE IV, биты 4-6 - наличие SQL-таблицы, бит 7 - наличие любого MEMO-файла (или dBASE III Plus, или dBASE IV) |
1-3 | 3 байта | Дата последнего обновления в формате YYMMDD |
4-7 | 32-битное число | Количество записей в таблице |
8-9 | 16-битное число | Количество байтов, занимаемых заголовком |
10-11 | 16-битное число | Количество байтов, занимаемых записью |
12-13 | 2 байта | Зарезервированная область, заполнена нулями |
14 | 1 байт | Флаг, указывающий на наличие незавершенной транзакции |
15 | 1 байт | Флаг кодировки |
16-27 | 12 байт | Зарезервированная область для многопользовательского использования dBASE IV |
28 | 1 байт | Флаг наличия MDX-файла: 01H - файл присутствует, 00H - файл отсутствует |
29 | 1 байт | ID драйвера языка |
30-31 | 2 байта | Зарезервированная область, заполнена нулями |
32-n* | по 32 байта | Массив с описаниями полей (структура каждого такого описания показана ниже) |
n+1 | 1 байт | 0DH в качестве терминатора |
n - последний байт массива с описаниями полей. Размер массива зависит от количества полей в табличном файле.
Описание поля таблицы | ||
Байт | Содержание | Описание |
0-10 | 11 байт | Имя поля в ASCII (заполнено нулями). |
11 | 1 байт | Тип поля в ASCII (C, D, F, L, M или N) |
12-15 | 4 байта | Зарезервированная область |
16 | 1 байт | Размер поля в бинарном формате |
17 | 1 байт | Порядковый номер поля в бинарном формате |
18-19 | 2 байта | Зарезервированная область |
20 | 1 байт | ID рабочей области |
21-30 | 10 байт | Зарезервированная область |
31 | 1 байт | Флаг MDX-поля: 01H если поле имеет метку индекса в MDX-файле, 00H - нет. |
Заголовок табличного файла | ||
Байт | Содержание | Описание |
0 | 1-й байт | Контроль файла dBASE под Windows: биты 0-2 указывают номер версии, бит 3 - наличие MEMO-файла dBASE IV или dBASE под Windows, биты 4-6 - наличие dBASE IV SQL-таблицы, бит 7 - наличие любого .DBT MEMO-файла (MEMO-файл таблицы dBASE III Plus, dBASE IV или dBASE под Windows) |
1-3 | 3 байта | Дата последнего обновления в формате YYMMDD |
4-7 | 32-битное число | Количество записей в таблице |
8-9 | 16-битное число | Количество байтов, занимаемых заголовком |
10-11 | 16-битное число | Количество байтов, занимаемых записью |
12-13 | 2 байта | Зарезервированная область, заполнена нулями |
14 | 1 байт | Флаг, указывающий на наличие незавершенной транзакции |
15 | 1 байт | Флаг кодировки |
16-27 | 12 байт | Зарезервированная область для многопользовательского использования |
28 | 1 байт | Флаг наличия MDX-файла: 01H - файл присутствует, 00H - файл отсутствует |
29 | 1 байт | ID драйвера языка |
30-31 | 2 байта | Зарезервированная область, заполнена нулями |
32-n* | по 32 байта | Массив с описаниями полей (структура каждого такого описания показана ниже) |
n+1 | 1 байт | 0DH в качестве терминатора |
n - последний байт массива с описаниями полей. Размер массива зависит от количества полей в табличном файле.
Описание поля таблицы | ||
Байт | Содержание | Описание |
0-10 | 11 байт | Имя поля в ASCII (заполнено нулями). |
11 | 1 байт | Тип поля в ASCII (B, C, D, F, G, L, M или N) |
12-15 | 4 байта | Зарезервированная область |
16 | 1 байт | Размер поля в бинарном формате |
17 | 1 байт | Порядковый номер поля в бинарном формате |
18-19 | 2 байта | Зарезервированная область |
20 | 1 байт | ID рабочей области |
21-30 | 10 байт | Зарезервированная область |
31 | 1 байт | Флаг MDX-поля: 01H если поле имеет метку индекса в MDX-файле, 00H - нет. |
Заголовок табличного файла | ||
Байт | Содержание | Описание |
0 | 1-й байт | Контроль файла dBASE под Windows: биты 0-2 указывают номер версии, бит 3 - наличие MEMO-файла dBASE IV или dBASE под Windows, биты 4-6 - наличие dBASE IV SQL-таблицы, бит 7 - наличие любого .DBT MEMO-файла (MEMO-файл таблицы dBASE III Plus, dBASE IV или dBASE под Windows) |
1-3 | 3 байта | Дата последнего обновления в формате YYMMDD |
4-7 | 32-битное число | Количество записей в таблице |
8-9 | 16-битное число | Количество байтов, занимаемых заголовком |
10-11 | 16-битное число | Количество байтов, занимаемых записью |
12-13 | 2 байта | Зарезервированная область, заполнена нулями |
14 | 1 байт | Флаг, указывающий на наличие незавершенной транзакции dBASE IV |
15 | 1 байт | Флаг кодировки таблицы dBASE IV |
16-27 | 12 байт | Зарезервированная область для многопользовательского использования |
28 | 1 байт | Флаг наличия MDX-файла: 01H - файл для данной таблицы присутствует, 00H - файл отсутствует |
29 | 1 байт | ID драйвера языка |
30-31 | 2 байта | Зарезервированная область, заполнена нулями |
32-n | по 32 байта | Массив с описаниями полей (структура данного массива показана ниже) |
n+1 | 1 байт | 0DH в качестве терминатора |
n - последний байт массива с описаниями полей. Размер массива зависит от количества полей в табличном файле.
Описание поля таблицы | ||
Байт | Содержание | Описание |
0-10 | 11 байт | Имя поля в ASCII (заполнено нулями). |
11 | 1 байт | Тип поля в ASCII (B, C, D, F, G, L, M или N) |
12-15 | 4 байта | Зарезервированная область |
16 | 1 байт | Размер поля в бинарном формате |
17 | 1 байт | Порядковый номер поля в бинарном формате |
18-19 | 2 байта | Зарезервированная область |
20 | 1 байт | ID рабочей области |
21-30 | 10 байт | Зарезервированная область |
31 | 1 байт | Флаг MDX-поля: 01H если поле имеет метку индекса в MDX-файле, 00H - нет. |
Таблицы dBASE: Структура .DBF-файла
Иногда возникает необходимость поработать с таблицей dBASE напрямую, без Borland Database Engine (BDE). К примеру, если .DBT-файл (содержащий MEMO-данные) для данной таблицы безвозвратно потерян, .DBF-файл становится абсолютно непригодным, поскольку байт в заголовке .DBF-файла указывает, что таблица должна содержать соответствующий MEMO-файл. Решение этой проблемы потребует обнуление этого байта, для того чтобы таблица не указывала на сопутствующий MEMO-файл. Или, если Вам захотелось написать собственную программу для работы с данными.
Ниже приводяся структуры .DBF-файлов для таблиц dBASE. Представлены структуры файлов для различных версий dBASE: dBASE III PLUS 1.1, dBASE IV 2.0, dBASE 5.0 под DOS и dBASE 5.0 для Windows.
Текущий номер записи набора данных
{Извлекает физический номер записи xBase. Требует наличие модулей DBITYPES, DBIPROCS, и DBIERRS в списке используемых модулей. Функция требует на входе один аргумент типа TTable (например, Table1).} function Form1.Recno( oTable: TTable ): Longint; var rError: DBIResult; rRecProp: RECprops; szErrMsg: DBIMSG; begin Result := 0; try oTable.UpdateCursorPos; rError := DbiGetRecord( oTable.Handle, dbiNOLOCK, nil, @rRecProp ); if rError = DBIERR_NONE then Result := rRecProp.iPhyRecNum else case rError of DBIERR_BOF: Result := 1; DBIERR_EOF: Result := oTable.RecordCount + 1; else begin DbiGetErrorString( rError, szErrMsg ); ShowMessage( StrPas( szErrMsg )); end; end; except on E: EDBEngineError do ShowMessage( E.Message ); end; end; |
[001239]
Текущий RecNo (dBase/Pdox)
...какой метод вовращает номер текущей записи? (т.е.: Я хочу использовать это вместе с функцией RecordCount и выводить для пользователя в строке состояния нечто вроде: "Запись #n из x")
Вот функция, возвращающая номер текущей записи в наборе данных DataSet. В основном я все скопировал и расставил комментарии, которые теперь лишают меня возможности поговорить на эту тему. Могли бы вы получить эти цифирьки из DBIPROCS.INT?
function RecordNumber(Dataset: TDataset): Longint;
var
CursorProps: CurProps;
RecordProps: RECProps;
begin { Возвращаем 0, если набор данных не Paradox или dBASE } Result := 0; with Dataset do begin { Набор данных активен? } if State = dsInactive then DBError(SDataSetClosed); { Нам необходимо сделать этот вызов, чтобы "захватить" курсор iSeqNums } Check(DbiGetCursorProps(Handle, CursorProps)); { Синхронизируем курсор BDE с курсором набора данных } UpdateCursorPos; { Заполняем RecordProps текущими свойствами записи } Check(DbiGetRecord(Handle, dbiNOLOCK, nil, @RecordProps)); { С каким типом набора данных мы работаем? } case CursorProps.iSeqNums of 0: Result := RecordProps.iPhyRecNum; { dBASE } 1: Result := RecordProps.iSeqNum; { Paradox } end; { case } end; { with } end; { function } |
Затем, в обработчике события OnDataChange DataSet, я использовал команду:
MyTextVariable := 'Запись ' + IntToStr( RecordNumber( tImport ) ) + ' из ' + IntToStr( tImport.RecordCount ) ; |
Mike Downey [000738]
Восстановление записи dBase
После удаления записи из таблицы (dBase), возможно ли ее восстановить? Какие идеи?
function GetTableCursor( oTable : TTable ) : hDBICur;
var
szTable : Array [0..78] of Char;
begin
StrPCopy( szTable, oTable.TableName );
DbiGetCursorForTable( oTable.DBHandle, szTable, nil, Result );
end;
function dbRecall( oTable : TTable ) : DBIResult; begin Result := DbiUndeleteRecord( GetTableCursor( oTable ) ) ); end; |
Могли бы вы дать пример использования функции?
Предположим, у вас на форме имеется кнопка (с именем 'butRecall'), восстанавливающая текущую отображаемую (или позиционируемую курсором) запись, данный код, будучи расположенный в обработчике события кнопки OnClick (вместе с опубликованным выше кодом), это демонстрирует (продвигаясь в наших предположених дальше, имя вашего объекта TTable - Table1 и имя текущей формы - Form1):
procedure TForm1.butRecallClick(Sender : TObject); begin if dbRecall( Table1 ) <> DBIERR_NONE then ShowMessage( 'Не могу восстановить запись!' ); end; |
- Loren Scott [000815]
Записи таблицы
Записи в табличном файле располагаются непосредственно за заголовком таблицы. Данным записи предшествует байт, указывающий на удаленность записи: значение 20h (пробел) указывает что запись не удалена, значение 2Ah (звездочка) - запись была удалена. Поля упаковываются записями без разделителей полей или терминаторов записи. Конец файла помечается единственным байтом (с EOF-маркером), OEM-код которого соответствует значению 26 (1Ah). Вы можете ввести данные в кодовой странице OEM как показано ниже.
Допустимый тип данных таблиц dBASE | |
Тип данных | Возможные значения |
C (Символы) | Все символы кодовой страницы OEM |
D (Дата) | Числа и символ-разделитель для месяца, дня и года (внутренний формат записи - 8 цифр в формате YYYYMMDD) |
N (Числовой) | - . 0 1 2 3 4 5 6 7 8 9 |
L (Логический) | ? Y y N n T t F f (? - не инициализировано) |
M (Мемо) | Все символы кодовой страницы OEM (внутренний формат записи - 10 цифр, содержащих номер .DBT-блока) |
Записи в табличном файле располагаются непосредственно за заголовком таблицы. Данным записи предшествует байт, указывающий на удаленность записи: значение 20h (пробел) указывает что запись не удалена, значение 2Ah (звездочка) - запись была удалена. Поля упаковываются записями без разделителей полей или терминаторов записи. Конец файла помечается единственным байтом (с EOF-маркером), OEM-код которого соответствует значению 26 (1Ah).
Допустимый тип данных таблиц dBASE | ||
Обозначение типа | Тип | Диапазон значений |
C | Символы | Все символы кодовой страницы OEM |
D | Дата | Числа и символ-разделитель для месяца, дня и года (внутренний формат записи - 8 цифр в формате YYYYMMDD) |
F | Бинарные числа с плавающей точкой | - . 0 1 2 3 4 5 6 7 8 9 |
N | Десятичное преобразование бинарных чисел | - . 0 1 2 3 4 5 6 7 8 9 |
L | Логический | ? Y y N n T t F f (? - не инициализировано) |
M | Мемо | Все символы кодовой страницы OEM (внутренний формат записи - 10 цифр, содержащих номер .DBT-блока) |
Записи в табличном файле располагаются непосредственно за заголовком таблицы. Данным записи предшествует байт, указывающий на удаленность записи: значение 20h (пробел) указывает что запись не удалена, значение 2Ah (звездочка) - запись была удалена. Поля упаковываются записями без разделителей полей или терминаторов записи. Конец файла помечается единственным байтом (с EOF-маркером), OEM-код которого соответствует значению 26 (1Ah). Вы можете ввести данные в кодовой странице OEM как показано ниже.
Допустимый тип данных таблиц dBASE | |
Тип данных | Возможные значения |
C (Символы) | Все символы кодовой страницы OEM |
D (Дата) | Числа и символ-разделитель для месяца, дня и года (внутренний формат записи - 8 цифр в формате YYYYMMDD) |
F (Бинарные числа с плавающей точкой) | - . 0 1 2 3 4 5 6 7 8 9 |
N (Числовой) | - . 0 1 2 3 4 5 6 7 8 9 |
L (Логический) | ? Y y N n T t F f (? - не инициализировано) |
M (Мемо) | Все символы кодовой страницы OEM (внутренний формат записи - 10 цифр, содержащих номер .DBT-блока) |
Записи в табличном файле располагаются непосредственно за заголовком таблицы. Данным записи предшествует байт, указывающий на удаленность записи: значение 20h (пробел) указывает что запись не удалена, значение 2Ah (звездочка) - запись была удалена. Поля упаковываются записями без разделителей полей или терминаторов записи. Конец файла помечается единственным байтом (с EOF-маркером), OEM-код которого соответствует значению 26 (1Ah). Вы можете ввести данные в кодовой странице OEM как показано ниже.
Допустимый тип данных таблиц dBASE | |
Тип данных | Возможные значения |
B (Бинарный) | Все символы кодовой страницы OEM (внутренний формат записи - 10 цифр, содержащих номер .DBT-блока). |
C (Символы) | Все символы кодовой страницы OEM |
D (Дата) | Числа и символ-разделитель для месяца, дня и года (внутренний формат записи - 8 цифр в формате YYYYMMDD) |
G (Общий) | Все символы кодовой страницы OEM или OLE (внутренний формат записи - 10 цифр, содержащих номер .DBT-блока). |
N (Числовой) | - . 0 1 2 3 4 5 6 7 8 9 |
L (Логический) | ? Y y N n T t F f (? - не инициализировано) |
M (Мемо) | Все символы кодовой страницы OEM (внутренний формат записи - 10 цифр, содержащих номер .DBT-блока) |
Ошибка в TDBComboBox или особенность работы?
Форма предназначена для ввода новой информации и работает с двумя таблицами – master и detail. Необходимо, чтобы все компоненты формы (DBEdit, DBComboBox) при открытии отображали пустые строки. Информация из таблицы детализации содержится в DBComboBox. Список создается стандартным способом - DBComboBox.Items.Add(Fields[1].AsString. Если DBComboBox.ItemIndex := -1, то должна отображаться пустая строка. При открытии формы в DBComboBox отображается информация последней записи таблицы детализации. Почему последняя строка– надеюсь понятно. Если заменить в процедуре инициализации списка DBComboBox.ItemIndex := -1 на DBComboBox.Text := ''; то появляется другая странность: Пользователь раскрывает список и, ничего не выбрав, переходит на другой компонент – в DBComboBox вновь появляется информация последней записи таблицы детализации. Исправить это поведение DBComboBox можно так - если в модуле DBCTRLS.PAS изменить SetComboText:
procedure TDBComboBox.SetComboText(const Value: string); var I: Integer; Redraw: Boolean; begin if Value <> GetComboText then begin if Style <> csDropDown then begin Redraw := (Style <> csSimple) and HandleAllocated; if Redraw then SendMessage(Handle, WM_SETREDRAW, 0, 0); try //------ ADD -------------------------------------------------------------------------------- if ItemIndex = -1 then begin Text := ''; Exit; end; //---------------------------------------------------------------------------------------------- if Value = '' then I := -1 else I := Items.IndexOf(Value); ItemIndex := I; finally if Redraw then begin SendMessage(Handle, WM_SETREDRAW, 1, 0); Invalidate; end; end; if I >= 0 then Exit; end; if Style in [csDropDown, csSimple] then Text := Value; end; end; |
После этого откомпилировать модуль DBCTRLS.PAS и заменить старый. Но может быть есть другие варианты.
С уважением, VS. [000911]
Заполнение TDBComboBox/ListBox
Как заполнить TDBComboBox и TDBListBox
Большинство Delphi-компонентов для работы с базами данных заполняются сами после того, как прилинкованный набор данных будет открыт. Тем не менее, DbListboxs и DbComboboxs не поддерживают данную характеристику. Данные два компонента не предназначены для отображения вашего набора данных, но тем не менее, они заполняются на основе полученных данных. Использование этих компонентов является прямым форвардом. При обновлении вашей таблицы, значения DbListbox и DbCombobox будут поститься в соответствующее поле.
Заполняются DbCombobox и DbListbox точно также, как и обычные ComboBox и ListBox. Строки текста в ListBox и ComboBox представляют собой список TString. Данный список у наших компонентов хранится в свойстве "Items". При добавлении новых строк к списку используется метод "Add". Если вы хотите использовать другой тип данных, не String, то он должен быть преобразован во время выполнения программы. Если в конце списка имеется пустая строка, рекомендуем установить свойство "IntegralHeight" в True.
Заполнение DbListbox четырьмя строками программным путем могло бы выглядеть примерно так:
DbListbox1.items.add('первая строка'); DbListbox1.items.add('вторая строка'); DbListbox1.items.add('третья строка'); DbListbox1.items.add('четвертая строка'); |
Заполнение DbListbox во время разработки требует использования Инспектора Объектов. Двойной щелчок на свойстве компонента "Items" позволит вызвать "String List Editor" (редактор списка строк) и ввести необходимые строки.
К сожалению, если заполнять данным способом ComboBox, значение по умолчанию отсутствует. Данный результат возможен при установке свойства DbCombobox "text". (свойство "text" не доступно в Инспекторе Объектов, оно должно быть установлено программным путем). Установка значения по умолчанию для первого значения в DbCombobox должна выглядеть примерно таким образом:
DbCombobox1.text := DbCombobox1.items[0]; |
Часто полезным бывает заполнить DBListBox из набора данных. Это может быть сделано с помощью цикла:
procedure TForm1.FormCreate(Sender: TObject); begin with table2 do begin open; while not EOF do begin DBlistbox1.items.add(FieldByName('name').AsString); next; end; end; end; |
[000846]
Различные цвета строк в DBCtrlGrid
Кто-нибудь знает как установить другие цвета для строк DBCtrlGrid?
Используйте событие drawColumnCell. И не забудьте выставить defautlDrawing в False
procedure TMain.ProjectGridDrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); begin projectGrid.canvas.brush.color := clWindow; projectGrid.canvas.fillRect(rect); if gdSelected in state then begin projectGrid.canvas.brush.color := clHighlight; if fsBold in projectGrid.canvas.font.style then begin projectGrid.canvas.font.color := clHighlightText; projectGrid.canvas.font.style := [fsBold]; end else projectGrid.canvas.font.color := clHighlightText; end else if gdFocused in state then begin projectGrid.canvas.brush.color := clWindow; if fsBold in projectGrid.canvas.font.style then begin projectGrid.canvas.font.color := clWindowText; projectGrid.canvas.font.style := [fsBold]; end else projectGrid.canvas.font.color := clWindowText; end else if gdFixed in state then begin projectGrid.canvas.brush.color := clHighlight; if fsBold in projectGrid.canvas.font.style then begin projectGrid.canvas.font.color := clHighlightText; projectGrid.canvas.font.style := [fsBold]; end else projectGrid.canvas.font.color := clHighlightText; end; with globalDataModule.qProjects do begin // тестовая запись. Устанавливаем свойства для перекрытия заданных по умолчанию; if fieldByName('EST_COMPL_DATE').asDateTime < date then projectgrid.Canvas.font.color := clRed; if compareStr(fieldByName('STAT_CODE').asString, 'HD') = 0 then projectgrid.Canvas.font.color := clOlive; if (compareStr(fieldByName('CHANGED').asString, 'Y') = 0) and (fieldByName('ASSIGN_EMP_ID').asInteger = userRecord.UserId) then projectgrid.Canvas.font.style := [fsBold]; end; projectGrid.canvas.textOut(rect.left+2, rect.top+2, column.field.text); end; |
Insert/Override с помощью DBEdit
Сама Windows не позволяет это сделать, но я нашел как это обойти с помощью одной хитрости, и, похоже, это классно работает (надеюсь вы получите даже больше, чем вы хотите :).
Сначала я добавляю к моей форме свойство (и соответствующие переменные и процедуры), наподобие этому:
private FinsertMode: boolean; procedure SetInsertMode(value: boolean); public property insertMode: boolean read FinsertMode write SetInsertMode; |
В обработчике создания события формы я инициализирую его:
procedure TForm1.FormCreate(Sender: TObject); begin {инициализация} insertMode := True; end; |
Также для этого свойства я создаю процедуру SetInsertMode, которая с помощью TPanel с именем Panel1 извещает пользователя о текущем режиме работы:
procedure TForm1.SetInsertMode(value: boolean); begin FinsertMode := value; if FinsertMode then Panel1.Caption := 'ВСТАВКА' else Panel1.Caption := 'ПЕРЕЗАПИСЬ'; end; |
Затем я добавляю три обработчика событий (OnKeyDown, OnKeyPress, OnEnter) для каждого моего DBEdit (можно при наличии нескольких компонентов создать один общий обработчик для всех):
procedure TForm1.DBEditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if (Key = VK_INSERT) then
insertMode := not insertMode;
end;
procedure TForm1.DBEditKeyPress(Sender: TObject; var Key: Char); begin if (not insertMode) and (Sender is TDBEdit) then (Sender as TDBEdit).SelLength := 1 else (Sender as TDBEdit).SelLength := 0; end; procedure TForm1.DBEditEnter(Sender: TObject); begin insertMode := True; end; |
Банзай! Похоже это работает, хотя я и не имел достаточного времени протестировать это. Естественно, вы можете изменить это по просьбе вашего заказчика (например, я всегда сбрасывал режим во вставку при перемещении к другому компоненту DBEedit). Все вышесказанное должно также работать без проблем и с компонентами Edit.
- Denis Sarrazin [001049]
Исправление DBEdit MaxLength
Я, кажется, не могу получить свойство MaxLength, чтобы работать с компонентами TDBEdit. В TEdit это работает как положено, но при попытке задать максимальную длину текста в TDBEdit это не срабатывает, и я все еще могу набрать текст сверх установленного ограничения.
По-моему, это является следствием этого кода в TDBEdit.DataChange (DBCTRLS.PAS):
if FDataLink.Field <> nil then begin ... if FDataLink.Field.DataType = ftString then MaxLength := FDataLink.Field.Size else MaxLength := 0; ... end else begin ... MaxLength := 0; ... end; |
т.к. иногда значение устанавливается на ноль...
Похоже все будет работать, если вы измените строку
MaxLength := 0; |
на
MaxLength := inherited MaxLength; |
Для того, чтобы изменения вступили в силу, вам необходимо перекомпилировать ваш complib с измененным DBCTRLS.PAS, находящимся в пути lib.
Если вы хотите использовать MaxLength с StringField, изменений необходимо сделать немного больше:
... if (FDataLink.Field.DataType = ftString) and (inherited MaxLength = 0) then MaxLength := FDataLink.Field.Size else MaxLength := inherited MaxLength; ... |
Или использовать что-то типа EditMask...
- Reinhard Kalinke [001021]
Как очистить DBEdit
Пробую так:
myDbEdit.Text := ''; |
или адрес TField, если вы хотите так:
TableNameMyField.Value := ''; |
Ответ:
Table1.Edit; Table1.FieldByName(DBEdit1.FieldName).Clear; |
[001533]
Перевод в верхний регистр первого вводимого символа
procedure TForm1.DBEdit1KeyPress(Sender: TObject; var Key: Char); begin if (DBEdit1.SelStart = 0 ) then Key := upCase(Key) ; end; |
Или для Edit:
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char); begin with Sender as TEdit do if (Text = '') or (Text[SelStart] = ' ') or (SelLength = Length(Text)) then if Key in ['a'..'z'] then Key := UpCase(Key); end; |
[000417]
Поиск и управление TEdit/TField
Я хотел бы менять цвет компонентов TDBEdit и TEdit, расположенных на форме, на другой, "отчетливый" цвет, в том случае, если с помощью них требуется ввести какие-либо данные.
Как насчет этого? Представляю вашему вниманию два метода. Первый метод задает цвет каждому DBEdit, имеющему требуемое поле. Второй метод (более сложный) задает цвет каждому БД-компоненту, имеющему необходимое поле.
procedure TForm3.Button3Click(Sender: TObject);
Var
Control : Integer;
begin
For Control := 0 To ControlCount-1 Do
If Controls[Control] Is TDBEdit
Then With TDBEdit(Controls[Control]) Do
If DataSource.DataSet.FieldByName ( DataField ).Required
Then Color := clRed;
end;
{ Данный метод будет работать только в случае, если БД-компонент обладает тремя полями: DataSource, типа TDataSource, DataField, типа String, и Color, типа TColor (это не должно быть проблемой). Также вам необходимо включить TypInfo в список используемых модулей } procedure TForm3.Button4Click(Sender: TObject); Var Control : Integer; DataSource : TDataSource; DataField : String; Function GetDataSource ( Instance : TComponent ) : Boolean; Var PropInfo : PPropInfo; Begin Result := False; PropInfo := TypInfo.GetPropInfo ( Instance.ClassInfo, 'DataSource' ); If ( PropInfo <> Nil ) And ( PropInfo^.PropType^.Kind = tkClass ) Then Begin DataSource := TDataSource ( TypInfo.GetOrdProp ( Instance, PropInfo ) ); Result := DataSource <> Nil; End; End; Function GetDataField ( Instance : TComponent ) : Boolean; Var PropInfo : PPropInfo; Begin Result := False; PropInfo := TypInfo.GetPropInfo ( Instance.ClassInfo, 'DataField' ); If ( PropInfo <> Nil ) And ( PropInfo^.PropType^.Kind = tkString ) Then Begin DataField := TypInfo.GetStrProp ( Instance, PropInfo ); Result := True; End; End; Procedure SetColor ( Instance : TComponent; Color : TColor ); Var PropInfo : PPropInfo; Begin PropInfo := TypInfo.GetPropInfo ( Instance.ClassInfo, 'Color' ); If ( PropInfo <> Nil ) And ( PropInfo^.PropType^.Kind = tkInteger ) Then TypInfo.SetOrdProp ( Instance, PropInfo, Ord(Color) ); End; begin For Control := 0 To ControlCount-1 Do If GetDataSource ( Controls[Control] ) And GetDataField ( Controls[Control] ) And ( DataSource.DataSet <> Nil ) And DataSource.DataSet.FieldByName ( DataField ).Required Then SetColor ( Controls[Control], clRed ); end; |
- Robert Wittig [001046]
Преобразование TEdit в TDBEdit
Предположим вы захотели заменить Edit на DBEdit: Выберите в дизайнере формы компонент Edit и нажмите Ctrl+X для перемещения его в буфер обмена. Переключитесь в редактор кода, перейдите в самый конец модуля (там, где "End.") и вставьте (Ctrl+V) то, что у вас хранится в буфере обмена - в коде появится текстовое описание компонента. В первой строчке замените "TEdit" на "TDBEdit". Переместите описание в буфер обмена и вставьте это на форму. Оooпс! Delphi ругнулся, потому что TDBEdit не имеет свойство Text. Возвращайтесь обратно на форму и снова вставьте компонент. Удалите строку со свойством Text, переместите описание компонента в буфер обмена и вставьте его обратно на форму. Это решение в общих чертах решает проблему замены компонентов разного типа. Довольно часто встречаются одно или несколько несовместимых свойств, и при попытке вставить измененный компонент вы получите ошибку. Не волнуйтесь, просто возвратитесь к текстовой версии, удалите проблемное свойство и попробуйте снова.
*** ПРЕДУПРЕЖДЕНИЕ *** Не используйте метод "DFM as text", иначе любое изменение может привести к ошибке (смотри выше), а то и к летальному, для формы, исходу! [000439]
Буфер обмена и ячейки DBGrid-а
Каким образом возможно вырезать/копировать/вставить ячейку компонента dbGrid или stringGrid?
Внутренний (in-place) редактор является защищенным свойством TCustomGrid, поэтому тут придется немного поколдовать. Вы можете сделать приблизительно так:
type
TMyCustomGrid = class(TCustomGrid)
public
property InplaceEditor;
end;
... if ActiveControl is TCustomGrid then TMyCustomGrid(ActiveControl).InplaceEditor.CopyToClipboard; |
-Steve Schafer [000906]
Числа с плавающей точкой в DBGrid
Для показа в табличной сетке дробных чисел, выберите таблицу, с которой связана ваша сетка (через datasource, источник данных).
Активизируйте редактор полей (правой кнопкой мыши) и выберите поле, в котором вы хотите видеть дробное число.
Измените значение свойств 'DisplayFormat' и 'EditFormat', чтобы дробь имела формат такой, какой вы хотите (к примеру, шаблон '0.00', позволяющий сетке показывать поле с двумя цифрами после запятой).
Дважды щелкните на компоненте table, расположенном на форме. Нажмите на кнопку 'Add'. Будут показаны все поля вашей таблицы. Выберите их в списке "Available field" (доступные поля) и щелкните на кнопке OK. Теперь при щелчке на имени поля, в Инспекторе Объектов будут показаны все свойства, относящиеся к данному полю, здесь можно изменить текст заголовка, выводимый формат "DisplayFormat" (это как раз то, что вам нужно, измените его на ####0.0) и пр. [001279]
DBGrid без вертикальной полосы прокрутки
Удаление вертикальной полосы прокрутки у TDBGrid
Для удаления вертикальной полосы прокрутки из компонента TDBGrid, вам необходимо перекрыть метод Paint. Внутри метода Paint вам необходимо вызвать процедуру API SetScrollRange, чтобы установить минимальные и максимальные значения полосы прокрутки в ноль (это запретит вывод полосы прокрутки), после чего вызвать родительский метод Paint. Код, приведенный ниже - новый компонент, названный TNoVertScrollDBGrid, у которого отсутствует вертикальная полоса прокрутки. Вы можете скопировать этот код в файл с именем NEWGRID.PAS и добавить данный компонент в палитру компонентов.
unit Newgrid; interface uses WinTypes, WinProcs, Classes, DBGrids; type TNoVertScrollDBGrid = class(TDBGrid) protected procedure Paint; override; end; procedure Register; implementation procedure TNoVertScrollDBGrid.Paint; begin SetScrollRange(Self.Handle, SB_VERT, 0, 0, False); inherited Paint; end; procedure Register; begin RegisterComponents('Data Controls', [TNoVertScrollDBGrid]); end; end. |
[000629]
DBGrid c FixedCols
Кам мне при прокрутке "зафиксировать" левое поле табличной сетки?
Я делаю это таким образом:
unit Fcdgrid;
interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, Grids, DBGrids,DBCtrls, DB,Menus; type TFixedColDBGrid = class(TDBGrid) private FUserFixedCols : Integer; protected procedure LayoutChanged; override; procedure SetUserFixedCols(I:Integer); published property UserFixedCols: Integer read FUserFixedCols write SetUserFixedCols; end; procedure Register; implementation procedure Register; begin RegisterComponents('Data Controls', [TFixedColDBGrid]); end; procedure TFixedColDBGrid.LayoutChanged; begin inherited LayoutChanged; { присваиваем FixedCols 1 если индикатор, иначе 0 } if ((inherited FixedCols + FUserFixedCols) < inherited ColCount) then inherited FixedCols := (FUserFixedCols + inherited FixedCols); end; procedure TFixedColDBGrid.SetUserFixedCols(I:Integer); begin FUserFixedCols := I; LayoutChanged; end; end |
Cheers [000569]
DBGrid DefaultDrawDataCell
TDBGrid имеет недокументированный в электронной справке метод DefaultDrawDataCell.
Вот пример его использования:
procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect; Field: TField; State: TGridDrawState); begin with Sender as TDBGrid do begin Canvas.Font.Color := clRed; DefaultDrawDataCell(Rect, Field, State); end; end; |
[000676]
DbGrid и клавиши акселерации
Форма содержит поле TEdit, поле TComboBox, TDBGrid и несколько BitBtn-кнопок (в перечисленном порядке идет и обход компонентов клавишей Tab). Табличная сетка была установлена для выбора целой колонки, и обратной операции при потере фокуса. Некоторым кнопкам были назначены клавиши акселерации путем установки символа "&" в их заголовках. Нажимая <alt> и соответствующую клавишу, ожидаешь соответствующей реакции, но не тут-то было. Беда приходит когда табличная сетка получает фокус.
Все работает превосходно, если вы используете клавиши навигации (например up, down, pgup и др.); тем не менее, если использовать клавиши акселерации (не используя клавишу <alt>), вызникают соответствующие клавишам события onClick. [000329]
Dbgrid и множественный выбор
Тема: TDBGrid и множественный выбор записей (Multi-Selecting Records)
При включении флажка [dgMultiSelect] в свойстве-наборе Options компонента DBGrid, вы добавляете к табличной сетке возможность множественного выбора записей.
Выбранные вами записи представлены в виде закладок и храняться в свойстве SelectedRows.
Свойство SelectedRows является объектом, имеющим тип TBookmarkList. Его свойства и методы описаны ниже.
// property SelectedRows: TBookmarkList read FBookmarks;
// TBookmarkList = class // public {* Метод Clear освобождает все выбранные в DBGrid записи *} // procedure Clear; {* Метод Delete удаляет все выбранные строки из набора данных *} // procedure Delete; {* Метод Find определяет наличие закладки в выбранном списке. *} // function Find(const Item: TBookmarkStr; // var Index: Integer): Boolean; {* Метод IndexOf возвращает индекс закладки, расположенной в свойстве Items. *} // function IndexOf(const Item: TBookmarkStr): Integer; {* Метод Refresh возвращает логическую величину, уведомляющую о том, что в то время, пока в табличной сетке была выбрана запись, были добавлены (удалены) какие-то данные. Метод Refresh может быть использован для обновления списка выбранных записей для уменьшения возможности получения удаленной записи. *} // function Refresh: Boolean; True = orphans found {* Свойство Count возвращает количество выбранных в настоящий момент элементов в DBGrid *} // property Count: Integer read GetCount; {* Свойство CurrentRowSelected содержит логическую величину, зависящую от того, выбрана текущая строка или нет. *} // property CurrentRowSelected: Boolean // read GetCurrentRowSelected // write SetCurrentRowSelected; {* Свойство Items - TStringList TBookmarkStr *} // property Items[Index: Integer]: TBookmarkStr // read GetItem; default; // end; unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Grids, DBGrids, DB, DBTables; type TForm1 = class(TForm) Table1: TTable; DBGrid1: TDBGrid; Count: TButton; Selected: TButton; Clear: TButton; Delete: TButton; Select: TButton; GetBookMark: TButton; Find: TButton; FreeBookmark: TButton; DataSource1: TDataSource; procedure CountClick(Sender: TObject); procedure SelectedClick(Sender: TObject); procedure ClearClick(Sender: TObject); procedure DeleteClick(Sender: TObject); procedure SelectClick(Sender: TObject); procedure GetBookMarkClick(Sender: TObject); procedure FindClick(Sender: TObject); procedure FreeBookmarkClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; Bookmark1: TBookmark; z: Integer; implementation {$R *.DFM} //Пример использования свойства Count procedure TForm1.CountClick(Sender: TObject); begin if DBgrid1.SelectedRows.Count > 0 then begin showmessage(inttostr(DBgrid1.SelectedRows.Count)); end; end; //Пример использования свойства CurrentRowSelected procedure TForm1.SelectedClick(Sender: TObject); begin if DBgrid1.SelectedRows.CurrentRowSelected then showmessage('Выбрана'); end; //Пример использования метода Clear procedure TForm1.ClearClick(Sender: TObject); begin dbgrid1.SelectedRows.Clear; end; //Пример использования метода Delete procedure TForm1.DeleteClick(Sender: TObject); begin DBgrid1.SelectedRows.Delete; end; {* Данные пример проходит в цикле все выбранные записи табличной сетки и отображает второе поле набора данных. Метод DisableControls используется в случае, когда необходимо запретить обновление DBGrid при изменении набора данных. Последняя позиция набора данных сохраняется как TBookmark. Метод IndexOf вызывается при необходимости проверить существование закладки. Решение использовать метод IndexOf, а не Refresh, должно приниматься исходя из специфики приложения. *} procedure TForm1.SelectClick(Sender: TObject); var x: word; TempBookmark: TBookMark; begin DBGrid1.Datasource.Dataset.DisableControls; with DBgrid1.SelectedRows do if Count > 0 then begin TempBookmark:= DBGrid1.Datasource.Dataset.GetBookmark; for x:= 0 to Count - 1 do begin if IndexOf(Items[x]) > -1 then begin DBGrid1.Datasource.Dataset.Bookmark:= Items[x]; showmessage(DBGrid1.Datasource.Dataset.Fields[1].AsString); end; end; end; DBGrid1.Datasource.Dataset.GotoBookmark(TempBookmark); DBGrid1.Datasource.Dataset.FreeBookmark(TempBookmark); DBGrid1.Datasource.Dataset.EnableControls; end; {* Данный пример позволит вам установить закладку и затем найти ее в списке выбранных записей компонента DBGrid. *} //Устанавливаем закдадку procedure TForm1.GetBookMarkClick(Sender: TObject); begin Bookmark1:= DBGrid1.Datasource.Dataset.GetBookmark; end; //Освобождаем закладку procedure TForm1.FreeBookmarkClick(Sender: TObject); begin if assigned(Bookmark1) then begin DBGrid1.Datasource.Dataset.FreeBookmark(Bookmark1); Bookmark1:= nil; end; end; //Испольуем метод Find для установления позиции //записи-закладки в списке выбранных записей компонента DBGrid procedure TForm1.FindClick(Sender: TObject); begin if assigned(Bookmark1) then begin if DBGrid1.SelectedRows.Find(TBookMarkStr(Bookmark1),z) then showmessage(inttostr(z)); end; end; end. |
[001010]
DBGrid и TQuery
Расположите на вашей форме 2 TQuerie с двумя соответствующими TDatasource (Query1 будет вашим Мастером, Query2 будет вашей Деталью)
Разместите 2 TDBGrid, связанных с Datasource'ами (вероятно, вы уже это сделали)
Используйте базу данных, поставляемую с Delphi:
Query1.SQL := 'Select * from customer' Query2.SQL := 'Select * from Orders where Orders."CustNo" = :CustNo' |
(это можно сделать как во время выполнения приложения, так и во время его разработки)
В свойствах Query2 выберите свойство Params и напишите в строке 'CustNo'. 'CustNo' был определен как параметр, поскольку в SQL строке было использовано ':'.
ОЧЕНЬ ВАЖНО: установите Query2.Datasource в набор данных, связанный с Query1.
Каждый раз при изменении записи в наборе данных Query1, Query2 будет обновляться. Имя параметра 'CustNo' соответствует имени реального поля в таблице Customer.
P.S.: Для получения дополнительной информации обратитесь к разделу электронной справки 'dynamic SQL' [001246]
DBGrid как навигатор
Расположите компонент table на пустой форме и свяжите его с вашего таблицей Client.
Добавьте компонент Datasource и свяжите его с компонентом table, описанным выше.
Добавьте компонент grid и свяжите его с компонентом datasource, описанным выше.
Используя Редактор Полей (Fields Editor), создайте компоненты TField для всех полей таблицы client.
Установите свойство Visible всех компонентов TField, кроме Client Name (или другого поля, которое будет отображаться в DBGrid), в False. Gird теперь будет отображать только Client Name.
Для отображения полей таблицы Client (которые вы хотите показать, или которые вы хотите сделать доступными для редактирования пользователем), ниже табличной сетки расположите компоненты DBEdit. Они могут использовать тот же набор данных, что и DBGrid. Теперь пользователь может воспользоваться DBGrid для навигации и ввода/редактирования данных посредством DBEdit'ов. [001248]
DBGrid - переход к следующей записи
Для перехода к следующей записи:
MyDBGrid.SelectedIndex := MyDBGrid.SelectedIndex + 1; |
Колонки DBGrid индексируются с 0, поэтому SelectedIndex := 0 даст вам первую колонку. Свойство FieldCount вернет вам количество колонок, так что вы без труда можете пробежаться по всей матрице данных. [001252]
Dbgrid с цветными ячейками I
Не знаю, помогу ли я Вам, но я расскажу как можно изменить цвет отдельных ячеек GBGrid без необходимости создания нового компонента. Я только что протестировал этот код....
Я создал форму, поместил на ней компонент TTable и указал ему на таблицу EMPLOYEE.DB в базе данных DBDEMOS. Затем я разместил на форме Datasource и DBGrid, "соединил" их и получил живые данные.
Для демонстрации данной технологии я выбрал поле "номер служащего" в таблице EMPLOYEE.DB и "покрасил" ячейки с нечетными числами. То есть, если число нечетное, красим ячейку в зеленый цвет.
Единственный код расположился в обработчике события OnDrawColumnCell компонента DBGrid и выглядел он так:
procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); var holdColor: TColor; begin holdColor := DBGrid1.Canvas.Brush.Color; {сохраняем оригинальный цвет} if Column.FieldName = 'EmpNo' then {"раскрашиваем" ячейки только для поля EmpNo} if (Column.Field.AsInteger mod 2 <> 0) then begin DBGrid1.Canvas.Brush.Color := clGreen; DBGrid1.DefaultDrawColumnCell(Rect, DataCol, Column, State); DBGrid1.Canvas.Brush.Color := holdColor; end; end; |
В данном случае мы использовали метод DefaultDrawColumnCell компонента TCustomDBGrid, являющегося родителем для TDBGrid. Он раскрасил зеленым цветом нечетные ячейки поля EmpNo. [000067]
Dbgrid с цветными ячейками II
Есть ли какой-либо способ придать ячейке DBGrid другой цвет? Мне хотелось бы выделить отдельные ячейки строки по определенному признаку. Типа флага, который, если если счет просрочен свыше 90 дней, делает строчку красной. Буду благодарен за любую помощь.
Обработайте событие OnDrawDataCell. Вот пример, который использует демонстрационную таблицу COUNTRY и рисует текст красным цветом во всех строках, содержащих страны с населением свыше 10 миллионов человек:
begin if Table1.FieldByName('Population').AsFloat < 10000000 then DBGrid1.Canvas.Font.Color := clRed; dbGrid1.DefaultDrawDataCell(Rect,Field,State); end; |
[000403]
Dbgrid с цветными ячейками III
C цветом ячейки DBGrid не так все просто, ведь в Delphi 1.0 разукрасить ячейку можно в обработчике события OnDrawDataCell, тогда как в Delphi 2.0 вам придется прибегнуть к событию OnDrawColumnCell. Для того чтобы создать код, который бы работал в ОБОИХ версих Delphi, вам необходимо прибегнуть к механизму условной компиляции, с помощью которого вы можете СКРЫТЬ обработчик OnDrawColumnCell в Delphi 1.0 и ВКЛЮЧИТЬ его в Delphi 2.0. Вот пример, в котором все ячейки строки рисуются красным, если колонка 'Preferred' содержит 'True':
...
private
{ Private declarations }
{$IFDEF Win32}
procedure DBGrid1DrawColumnCell(Sender: TObject;
const Rect: TRect; DataCol: Integer; Column: TColumn;
State: TGridDrawState);
{$ENDIF}
procedure DrawField(const Value: String; const Rect: TRect;
vCanvas: TCanvas; vFont: TFont; vAlignment: TAlignment);
...
procedure TForm1.DrawField(const Value: String; const Rect: TRect;
vCanvas: TCanvas; vFont: TFont; vAlignment: TAlignment);
VAR X : Integer;
BEGIN
vCanvas.Font := vFont;
vCanvas.Font.Color := clRed;
vCanvas.Font.Style := vCanvas.Font.Style + [fsUnderline];
CASE vAlignment OF
taRightJustify : BEGIN
SetTextAlign(vCanvas.Handle, TA_RIGHT);
X := Rect.Right-2;
END;
taLeftJustify : BEGIN
SetTextAlign(vCanvas.Handle, TA_LEFT);
X := Rect.Left+2;
END;
taCenter : BEGIN
SetTextAlign(vCanvas.Handle, TA_CENTER);
X := (Rect.Right+Rect.Left) DIV 2;
END;
END;
vCanvas.TextRect(Rect, X, Rect.Top+2, Value);
SetTextAlign(vCanvas.Handle, TA_LEFT);
END;
procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect; Field: TField; State: TGridDrawState); begin WITH Sender AS TDBGrid, DataSource.DataSet DO BEGIN IF FieldByName('Preferred').AsString <> 'True' THEN Exit; DrawField(Field.DisplayText, Rect, Canvas, Canvas.Font, Field.Alignment); END; end; {$IFDEF Win32} procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); begin WITH Sender AS TDBGrid, DataSource.DataSet DO BEGIN IF FieldByName('Preferred').AsString <> 'True' THEN Exit; DrawField(Column.Field.DisplayText, Rect, Canvas, Column.Font, Column.Alignment); END; end; {$ENDIF} procedure TForm1.FormCreate(Sender: TObject); begin {$IFDEF Win32} DBGrid1.OnDrawDataCell := NIL; DBGrid1.OnDrawColumnCell := DBGrid1DrawColumnCell; {$ENDIF} end; |
- Neil Rubenking [000968]
Dbgrid с цветными ячейками IV
Nomadic советует:
Hапример, так:
DefaultDrawing:=False; .... procedure TfrmCard.GridDrawColumnCell(Sender: TObject; constRect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); var Index : Integer; Marked, Selected: Boolean; begin Marked := False; if (dgMultiSelect in Grid.Options) and THackDBGrid(Grid).Datalink.Active then Marked:=Grid.SelectedRows.Find(THackDBGrid(Grid).Datalink.Datasource.Dataset.Bookmark,Index); Selected := THackDBGrid(Grid).Datalink.Active and (Grid.Row-1 = THackDBGrid(Grid).Datalink.ActiveRecord); if Marked then begin Grid.Canvas.Brush.Color:=$DFEFDF; Grid.Canvas.Font.Color :=clBlack; end; if Selected then begin Grid.Canvas.Brush.Color:=$FFFBF0; Grid.Canvas.Font.Color :=clBlack; if Marked then Grid.Canvas.Brush.Color:=$EFE3DF; { $8F8A30 } end; Grid.DefaultDrawColumnCell(Rect, DataCol, Column, State); end; |
где
THackDBGrid = class(TDBGrid) property DataLink; property UpdateLock; end; |
Обратите внимание на обьявление класса THackDBGrid. Таким образом можно получить доступ к приватным полям, свойствам и методам класса, что, к сожалению, приходится делать, если авторы исходного класса оказались не предусмотрительны. [001138]
Dbgrid с цветными ячейками V
Попробуйте следующий код в обработчике события TDBGrid OnDrawDataCell:
Procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect; Field: TField; State: TGridDrawState); begin If gdFocused in State then with (Sender as TDBGrid).Canvas do begin Brush.Color := clRed; FillRect(Rect); TextOut(Rect.Left, Rect.Top, Field.AsString); end; end; |
Установите рисование по умолчинию (Default drawing) в True. Только после этого можно нарисовать выделенную ячейку. Если вы установили DefaultDrawing в False, вы должны сами рисовать все ячейки, используя свойство Canvas. [001470]
Dbgrid с цветными ячейками VI
Установите defaultDrawing в false, и создайте собственный onDrawDataCell, в котором и задавайте нужный вам цвет ячеек. Примерно так:
procedure Tform1.DBgrid1DrawDataCell (Sender: TObject; const Rect: TRect; Field: TField; State: TGridDrawState); begin { выберите цвет для текста (font.color) и фона (brush.color) } if (field = table1Status) then begin { белый на красном } DBgrid1.canvas.font.color := clWhite; DBgrid1.canvas.brush.color := clRed; end else begin { черное на белом } DBgrid1.canvas.brush.color := clWhite; DBgrid1.canvas.font.color := clBlack; end; { рисуем ячейку } DBgrid1.canvas.textrect (rect,rect.left+2,rect.top+2,field.asString); end; procedure TMainForm.CharGridDrawCell(Sender: TObject; Col, Row: Longint; Rect: TRect; State: TGridDrawState); var TheText: string; begin TheText := CharGrid.Cells[Col, Row]; with CharGrid.Canvas do begin { Определяем цвет фона в зависимости от состояния ячейки: } if gdFocused in State then Brush.Color := clYellow {Цвет ячейки с фокусом} else if gdSelected in State then Brush.Color := clOlive {Цвет выбранной ячейки} else {ячейка не имеет фокуса и не выбрана} if IntFromStr(TheText) <> 0 then Brush.Color := clNavy {Цвет фона подсвеченной ячейки} else Brush.Color := clWhite; {Цвет фона нормальной ячейки} { Определяем цвет текста: } if IntFromStr(TheText) <> 0 then Font.Color := clRed {Цвет текста подсвеченной ячейки} else Font.Color := clNavy; {Цвет текста нормальной ячейки} TextRect(Rect, Rect.Left + 2, Rect.Top + 2, TheText); end; {with CharGrid.Canvas} end; |
[001501]
Dbgrid - выбранные строки
Тема: TDBGrid - Selected Rows (выбранные строки)
От: Nancy Reid 72056,212
К: Nancy Reid 72056,212
Существует одно свойство, не упомянутое в файлах помощи (оплошность, господа программисты из Borland), имеющее имя SelectedRows, и вот как его можно использовать:
procedure TfrmGrid.Button1Click(Sender: TObject); var i:integer; begin For i:=0 to DBGrid1.SelectedRows.Count -1 do begin Table1.GoToBookmark(TBookmark(DBGrid1.SelectedRows[i])); Table1.Delete; end; end; |
- Nancy Reid [000970]