Часть 2
Базы данных - компоненты и VCL.
Различные версии Delphi содержат следующие наборы компонентов:
Компоненты | Краткое описание | Версии Delphi |
TDBGrid | Представление данных в виде таблицы (очень сложный и наиболее популярный компонент) | |
TDBEdit | Редактирования одного поля | |
TDBNavigator | Как видно из названия, компонент позволяет перемещаться по таблице | |
TDBLabel | Статическое отображение содержимого поля | |
TDBMemo | Редактирования текста в поле типа BLOB | |
TDBImage | Отображение картинок из BLOB-а | |
TDBRadioGroup, TDBCheckBox | Дополнительные средства отображения данных | |
TDBComboBox, TDBListBox | Упрощают ввод данных, предлагая несколько заранее определенных вариантов | |
TDBLookupListBox, TDBLookupComboBox | То же самое, но возможные варианты выбираются из другой таблички | |
TDBCtrlGrid | Вариант представления записей с произвольным расположением полей | |
TDBChart | Компонент для построение графиков и диаграмм на основании данных, хранящихся в таблице | |
TDecisionGrid, TDecisionChart | Компоненты для поддержки принятия решений |
2. Использование псевдонимов в запросе SQL.
Я делаю запрос по двум таблицам разных форматов, находящихся по разным псевдонимам.
На самом деле вы получаете ошибку Unknown Keyword, следовательно всего-лишь нужно заключить псевдоним и имя таблицы в двойные кавычки.
У меня есть TQuery и TDataSource. В свойстве SQL для TQuery я пишу
Если свойство RequestLive=True, то имя таблицы нужно брать в кавычки:
Я безуспешно пытался использовать данные из Microsoft Access иначе, нежели просто с помощью TTable. Используя TQuery я могу только читать результат, но не могу редактировать. После "login screen" возникает сообщение типа 'Passthrough SQL connection must be shared'.
Измените
в настройке псевдонима (alias) пункт 'SQLPASSTHRU MODE' на 'SHARED AUTOCOMMIT'.
5. Как создать
таблицу при помощи SQL (или почему не работает TQuery.Open)?
TQuery.Open
возвращает результат в виде курсора, в связи с этим он работает только для тех
выражений, которые возвращают курсор. CREATE TABLE возвращает только результат
операции - поэтому для выполнения этого выражения необходимо использовать TQuery.ExecSQL.
Но и это может не сработать, если конкретный драйвер БД не поддерживает операцию
создания таблиц - для получения характеристик драйвера используйте функции BDE
(DbiOpenDriverList, DbiGetDriverDesc).
6. Возврат значения
select max() и подобных SQL-выражений.
Я хочу выполнить SQL-выражение и получить результат в свою переменную, что-то типа
Меня интересует возможность подсчета суммы по таблицам, которые уже находятся на форме.
Есть очень простой способ - предположим, что у вас есть на форме Query1, DataSource1, DBGrid1. Добавьте на эту-же форму компоненты Query2, DataSource2, DBText1. Установите property Query2.DataSource=DataSource1. В Query2.SQL напишите
При изменении Query1 (если конечно Query1.RequestLive=True) Query2 будет автоматически перевыполняться. Это решение хоть и простое, но неэкономичное - особенно при большом количестве записей в исходной таблице. Более того, запрос Query2 должен иметь WHERE идентичный Query1.
Для
подсчета сумм правильнее использовать событие TQuery.OnCalcFields. Хорошим
примером является X:\DELPHI\DEMOS\DB\MASTAPP\MASTAPP.DPR.
8. Использование
кавычек в параметризированном запросе.
Мой запрос получает параметр. Проблема в том, что строка параметра содержит " (двойную кавычку), которая приводит к Runtime Error.
Вам необходимо использовать динамический SQL-запрос, иначе при указании например
Легко
и просто - точно также как и обычный компонент. При этом в качестве параметра
конструктору можно передавать значение nil.
var MyTable: TTable; begin MyTable := TTable.Create(nil); try MyTable.DatabaseName := 'MyDB'; MyTable.TableName := 'MyTable.db'; Mytable.IndexName := 'MyIndex'; MyTable.Open; { делать то, что надо } finally MyTable.Free; end; end; |
Здесь
процедура для сохранения текущего номера строки и колонки. Следующий код в методе
MyDBGridDrawDataCell обновляет переменные Col и Row (которые
не должны быть локальными для этого метода) каждый раз, когда таблица перерисовывается.
Используя этот код, вы можете считать, что Col и Row указывают
на текущую колонку и строку соответственно.
var Col, Row: Integer; procedure TForm1.MyDBGridDrawDataCell(Sender: TObject; const
Rect: TRect; |
Для
TDBGrid в свойстве Options установите dgRowSelect в True.
12. Как изменить цвет ячейки
в 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; |
Переход
на новую запись - это событие, которое относится не к визуальному компоненту,
а к источнику данных. Соответствующее событие называется OnDataChange
и имеется у компонента TDataSource.
14. Как устанавливать
собственный цвет или шрифт для столбца TDBGrid?
Выключите
property DefaultDrawing, и обрабатывайте событие OnDrawDataCell:
procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const
Rect: TRect; Field: TField; State: TGridDrawState); begin if Field.FieldName = 'Name' then DBGrid1.Canvas.Font.Style := [fsBold]; DBGrid1.DefaultDrawDataCell(Rect, Field, State); end; |
В
Delphi 2.0 вы можете использовать редактор столбцов для той же самой цели.
15. Почему указатель
ползунка в TDBGrid не показывает текущее положение в таблице?
Дело в том, что TDBGrid предполагает многопользовательский доступ к таблице. В этом случае другие пользователи этой-же таблицы могут добавлять или удалять записи, в результате информация о количестве записей на текущий момент становится неопределенной.
Конечно, в однопользовательском варианте количество записей всегда известно, но поскольку TDBGrid работает через промежуточный источник данных DataSource, ему неизвестен конкретный способ доступа к данным - навигационный или SQL. Например, для SQL существует только один способ узнать количество записей - выполнить специальный запрос с их подсчетом, а на это может потребоваться значительное время.
По
всем этим причинам TDBGrid является универсальным средством для просмотра
таблиц, которое работает во всех случаях и с любыми источниками данных.
16. Как установить
фокус на определенное поле в TDBGrid?
Используйте код:
Как
и всякий TControl (иерархия наследования TControl-> TWinControl->
TCustomControl-> TCustomGrid-> TCustomDBGrid-> TDBGrid) у TDBGrid
есть событие OnClick, но оно protected. Так что можно либо создать
новый класс, производный от TDBGrid, в котором объявить это свойство
как published, либо использовать другой вариант. Например, вы можете
использовать событие OnColEnter.
18. Как создать маску для TDBEdit?
Маска
относится к полю в таблице (компонент TField) а не к самому TDBEdit.
Дважды щелкните мышкой на TTable и в FieldEditor'е добавьте все нужные
вам поля. Когда поле выбрано в списке, его свойства показаны в Object Inspector,
включая маску ввода. Связывание TDBEdit и любых других компонентов с
этим TTable будет вызывать наложение маски на соответствующее поле.
19. Хотелось бы иметь
для OLE объектов, сохраненных в базе данных, компонент вроде TDBImage.
В стандартном наборе такого компонента действительно нет. Возможно, кто-нибудь скоро напишет что-нибудь в этом роде. В принципе, можно обойтись и без данного компонента. Например, есть табличка .db с BLOB полем для OLE объекта. При движении по записям можно OLE сохранять в базе, уничтожать, создавать новый, считывать из базы.
Чтение/запись OLE:
Просто дайте этот пароль объекту Session перед открытием таблицы:
Если
ваш компонент доступа к данным (TTable или TQuery) связан с сессий,
отличной от той, которая выставляется по умолчанию, то добавлять пароль нужно
именно у этого компонента TSession.
21. Как
определить реальный размер поля типа BLOB, которое сохранено в таблице?
Ниже
приведена функция GetBlobSize, которая возвращает размер данного BLOB
или MEMO поля.
function GetBlobSize(Field: TBlobField): Longint;
begin with TBlobStream.Create(Field, bmRead) do try Result := Seek(0, 2); finally Free; end; end; procedure TForm1.Button1Click(Sender: TObject);
|
Вы можете добавить следующую функцию в ваш модуль и вызвать, например:
Событие
TDataSource.OnDataChange когда State=dsBrowse.
24. Как считать
даты для вычисляемых полей?
При вычислении дат важно удостовериться в том, что все используемые значения подходят по типу. В документации не совсем явно отражен тот факт, что на самом деле тип TDataTime эквивалентен типу Double, который можно использовать далее.
В
примере, D1 и D2 (поля в Table1) могут быть типа или Date,
или TDateTime, а D3 - поле типа Integer.
procedure TForm1.Table1CalcFields(DataSet: TDataset);
var T1, T2: TDateTime; begin Table1D1.AsDateTime := Date+2; { or Table1D1.Value := Date+2; } Table1D2.AsDateTime := Date-2; T1 := Table1D1.AsDateTime; T2 := Table1D2.AsDateTime; Table1D3.AsInteger := Trunc(Double(T1) - Double(T2)); end; |
Я в обработчике события OnChange для компонента TEdit пытаюсь получить содержимое его текстового буфера. Однако, следующая конструкция вызывает ошибку компиляции 'неизвестный идентификатор':
Я объявляю поле класса как TTable, но при обращении к нему происходит ошибка.
Дело
в том, что в Delphi все экземпляры объектов, объявленых как class, являются
динамическими. Соответственно поле MyTable, объявленное как
type TMyClass = class(TObject) public MyTable: TTable; constructor Create; destructor Destroy; override; end; |
constructor TMyClass.Create; begin MyTable := TTable.Create(nil); MyTable.DatabaseName := 'DBDEMOS'; end; destructor TMyClass.Destroy; |
Подробнее
см. Changes in Object Pascal Language в документации или on-line help.
3. Как закрыть
модальную форму (ShowModal) ? И вообще, каков лучший способ закрыть любую форму?
Вообще говоря, нужно вызывать метод Close для формы. Close вызывает событие OnClose (обработчик которого может решить, что форму нельзя закрывать, например, если имеются несохраненные данные). Close не освобождает память, связанную с формой, если вы, конечно, не поместите в обработчик события вызов метода Release.
Если вы хотите уничтожить форму без вызова события OnClose, используйте метод Release. Этот метод работает подобно Free, но позволяет всем обработчикам событий данной формы закончить работу перед тем, как память будет освобождена.
Модальные формы "прекращают свой модальный статус", когда вы устанавливаете свойство ModalResult формы в любое значение, отличное от нуля. Если вы поместите кнопку на модальную форму и установите свойство ModalResult для кнопки в некоторое значение, то, когда пользователь нажмет на эту кнопку, форма закроется с результатом, который вы определили. Этот результат можно узнать вызывая ShowModal как функцию. То есть:
Я поместил кнопку (или что-то другое) на форму, затем поместил панель, и решил переместить кнопку на панель, но ничего не получилось.
Действительно, чтобы поместить кнопку на панель, необходимо на форму сначала поместить панель, выбрать ее, а затем уже помещать кнопку.
Но и в вашей ситуации есть решение. Скопируйте (Copy) или вырежьте (Cut) нужный компонент, выберите панель, и сделайте вставку (Paste). Рекомендуется предварительно "подогнать" копируемый компонент в левый верхний угол формы, иначе компонент на панели может выпасть из "пределов видимости" панели (или любого другого группового компонента).
Если компонент все-таки "выпал" из пределов видимости - найдите этот компонент в Инспекторе Объектов, и установите нужные значения его свойств Left и Top.
Используя группы компонент можно огранизовать форму-шаблон, на которой можно складывать (например в Notebook) компоненты с предварительно заданными свойствами, отличными от стандартных. Это решение проще чем добавлять такие компоненты в палитру компонент - не увеличивается размер библиотеки компонентов DCL (Delphi 3.0 не считается), не загромождается палитра компонент.
Учтите,
что при таком копировании компонент их имена меняются на новые (Button1,
Button2 и т.д.).
5. Как можно
добавить новый компонент на страницу TTabbedNoteBook во время выполнения программы?
Как нужно определить свойство Parent для этого компонента?
Для
того, чтобы добавить компонент на страницу TabbedNotebook, свойству Parent
нового компонента нужно присвоить указатель на требуемую страницу. Способ для
доступа к любой странице TTabbedNotebook во время выполнения - массив
свойств Objects у свойства Pages компонента TTabbedNotebook.
Другими словами, страницы сохранены в виде объектов в свойстве Pages
(тип TStringList). Пример демонстрирует создание кнопки TButton
на второй странице TabbedNotebook1:
var NewButton: TButton; begin NewButton := TButton.Create(Self); NewButton.Parent := TWinControl(TabbedNotebook1.Pages.Objects[0]); |
То
же самое справедливо и для компонента TNotebook.
6. Как включить символ &
в надпись (Caption)?
Попробуйте:
&&
7. Как сделать окно
(TForm) без заголовка (Caption)?
Попробуйте
использовать следующий код:
constructor TPanelForm.Create(AOwner: TComponent);
{ Вызов SetWindowLong API для изменения стиля окна главной формы. } { Берется существующий стиль и убирается флаг WS_CAPTION } var LStyle: Longint; begin inherited Create(AOwner); BorderIcons := []; LStyle := GetWindowLong(Handle, GWL_STYLE); LStyle := LStyle and not WS_CAPTION; SetWindowLong(Handle, GWL_STYLE, LStyle); { Перерисуем окно } ForceRepaint; end; procedure TPanelForm.ForceRepaint; |
procedure TForm1.FormPaint(Sender: TObject); begin Canvas.Brush.Style := bsClear; Canvas.Rectangle(0, 0, Width, Height); end; |
Все
наследники TCustomControl имеют Canvas, однако, в большинстве
случаев это свойство объявлено protected для предотвращения рисования
'чужаками' на компоненте. Наследники компонента всегда могут получить доступ
к унаследованным protected свойствам (типа Canvas), но пользователь
компонента-никогда.
type TCanvasPanel = class(TPanel) public property Canvas; end; |
Если
вы хотите рисовать на компоненте, у которого нет public свойства Canvas,
то используйте, например, компонент TPaintBox: положите его на панель
TPanel, сделайте Align = Client и рисуйте на TPaintBox.Canvas.
9. Почему при
уничтожении компонента в методе OnClick происходит ошибка?
Допустим, вы поместили на форму кнопку, и создали метод OnClick в котором вызываете Button1.Free. Вы видите, что это метод формы - казалось-бы, какие препятствия для правильного уничтожения кнопки?
На самом деле Button1.OnClick является свойством и после запуска вашего приложения содержит адрес метода Form1.Button1Click. Именно кнопка вызывает этот метод как свой собственный. А это означает, что кнопка не может удалить себя в своем-же методе. Даже если вы попытаетесь удалить ссылку в OnClick:
Они
есть, но не объявлены published. Вы можете создать наследника TDBGrid
и сделать их published.
11. Поиск компонента в форме
по имени.
Я хочу делать текущими в форме произвольные компоненты. Как выставить фокус у конкретного компонента ясно - ListBox1.SetFocus. А если я хочу обращаться к некоему компоненту по имени (свойство Name)?
Свойство
TForm.Components - массив компонентов формы, который и нужен вам. Вы
можете перемещаться по этому массиву пока не найдете компонент с нужным Name.
Например:
procedure TForm1.DooDah; var Count: Integer; begin Count := 0; while (Count < ComponentCount) and (Components[Count] <> 'Button1') do Inc(Count); TButton(Components[Count]).SetFocus; end; |
procedure TForm1.DooDah; var Target: TComponent; begin Target := FindComponent('Button1'); TButton(Target).SetFocus; end; |
Пошлите
сообщение LB_SETHORIZONTALEXTENT в ListBox. Например, сообщение
может быть отослано в момент создания формы:
procedure TForm1.FormCreate(Sender: TObject);
begin SendMessage(Listbox1.Handle, LB_SETHORIZONTALEXTENT, 1000, Longint(0)); end; |
13. Как определить
текущую колонку и строку каретки в компоненте TMemo?
Вы
можете использовать сообщения Windows API EM_LINEFROMCHAR и EM_LINEINDEX
для определения положения.
var LineNum: Longint; CharsBeforeLine: Longint; begin LineNum := SendMessage(Memo1.Handle, EM_LINEFROMCHAR, Memo1.SelStart, 0); CharsBeforeLine := SendMessage(Memo1.Handle, EM_LINEINDEX, LineNum, 0); Label1.Caption := 'Line ' + IntToStr(LineNum + 1) Lebel2.Caption := 'Position ' + IntToStr(Memo1.SelStart - CharsBeforeLine + 1); end; |
14. Постранична прокрутка TMemo, реализация Undo и определение строки курсора.
Как прокрутить содержимое компонента TMemo?
Приведенная
ниже процедура предполагает, что фокус находится на Edit1 и осуществляет
прокрутку в соответствии с нажатыми клавишами.
procedure TForm1.Edit1KeyDown(Sender: TObject; var
Key: Word; Shift: TShiftState); begin if Key = VK_F8 then SendMessage(Memo1.Handle, { HWND для Memo } WM_VSCROLL, { сообщение Windows } SB_PAGEDOWN, { на страницу вниз } 0) { не используется } else if Key = VK_F7 then SendMessage(Memo1.Handle, WM_VSCROLL, SB_PAGEUP, 0); end; |
Если встроенного Undo достаточно, то это очень просто:
Весь
фокус в сообщении EM_LINEFROMCHAR. Попробуйте:
procedure TMyForm.BitBtn1Click(Sender: TObject);
var ILine: Integer; begin ILine := Memo1.Perform(EM_LINEFROMCHAR, $FFFF, 0); { Внимание: номера строк начинаются с нуля } MessageDlg('Line Number: ' + IntToStr(ILine), mtInformation, [mbOK], 0); end; |
15. Как поместить BLOB
Memo в компонент TMemo?
Попробуйте
так:
procedure TForm1.Button1Click(Sender: TObject);
var S: TBlobStream; begin S := TBlobStream.Create(Table1BBBMemo, bmRead); Memo1.Lines.LoadFromStream(S); S.Free; end; |
Используйте
следующий код для обработки события OnDrawDataCell у TDBGrid.
(Перед запуском программы создайте объект TMemoField для memo поля в
Fields Editor).
procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const
Rect: TRect; Field: TField; State: TGridDrawState); var P: array [0..1023] of Char; { MemoField buffer } BS: TBlobStream; S: string; begin if Field is TMemoField then with (Sender as TDBGrid).Canvas do begin { Table1Notes is the TMemoField } BS := TBlobStream.Create(Table1Notes, bmRead); FillChar(P, SizeOf(P), #0); BS.Read(P, SizeOf(P)); BS.Free; S := StrPas(P); { remove carriage returns & line feeds } while Pos(#13, S) > 0 do S[Pos(#13, S)] := ' '; while Pos(#10, S) > 0 do S[Pos(#10, S)] := ' '; { clear the cell } FillRect(Rect); { fill cell with memo data } TextOut(Rect.Left, Rect.Top, S); end; end; |
Я создаю событие на SpeedButton1.OnDblClick, но оно, похоже, вообще никогда не возникает. OnClick работает. Что делать?
На самом деле работает, только в определенных ситуациях. Если вы помещаете на панель несколько кнопок, то по умолчанию они независимы и соответственно не фиксируются в нажатом состоянии. Поскольку одиночное нажатие мыши на кнопку отрабатывается немедленно, двойной щелчок мыши воспринимается как два нажатия и отпускания. Поэтому OnDblClick и не срабатывает.
Если
же кнопки связаны в группу (GroupIndex <> 0), то они могут фиксироваться,
и соответственно могут воспринимать двойной щелчок мыши.
18. Как разделить
обработку OnClick и OnDblClick? Ведь OnClick будет вызываться всегда, и перед
DblClick.
Именно
так и происходит в Windows - посылаются оба сообщения. Для того чтобы обработать
только какое-то одно событие необходимо чуть "задержать" выполнение OnClick.
Сделать это можно следующим способом:
procedure TForm1.ListBox1Click(Sender: TObject);
var Msg: TMsg; TargetTime: Longint; begin { get the maximum time to wait for a double-click message } TargetTime := GetTickCount + GetDoubleClickTime; { cycle until DblClick received or wait time run out } while GetTickCount < TargetTime do if PeekMessage(Msg, ListBox1.Handle, WM_LBUTTONDBLCLK, WM_LBUTTONDBLCLK, WM_NOREMOVE) then Exit; { Double click } MessageDlg('Single clicked', mtInformation, [mbOK], 0); end; |
Используйте
свойство PopupComponent компонента TPopupMenu для определения,
где была нажата правая кнопка.
procedure TForm1.PopupItem1Click(Sender: TObject);
begin Label1.Caption := PopupMenu1.PopupComponent.Name; end; |
Свойство
ActiveControl для формы тоже можно использовать, однако, ActiveControl
не обязательно является тем элементом, для которого произошло событие.
20. Как
использовать case, чтобы определить, какой объект вызвал процедуру?
Используйте
свойство Tag. Установите значение Tag свое у каждого объекта для
опознания. (Использование констант, которые описывают объект - идеально подходит).
case (Sender as TComponent).Tag of Button1ID: SomeProcedure; Button2ID: AnotherProcedure; end; |
Таким
образом вы можете обрабатывать события как от однотипных компонент, так и от
компонент разного типа.
21. Как обрабатывать
события от множества однотипных компонентов.
На моей форме находится примерно 10 кнопок. Я хочу обрабатывать нажатие на любую из них одним событием, но как их отличить внутри обработчика события?
Для
этого базовый класс VCL TComponent имеет поле Tag типа Longint.
В момент разработки вы можете присвоить этому полю любое значение, а в момент
исполнения использовать его (или переопределять). В вашей ситуации достаточно
присвоить полю ButtonX.Tag значение от 1 до 10 (или от 0 до 9, как удобнее),
а в обработчике написать примерно следующее:
procedure MyForm.Button1Click(Sender: TObject);
begin case (Sender as TComponent).Tag of 1: {...}; 2: {...}; 3: {...}; end; end; |
Я пытаюсь использовать TPanel как индикатор процесса обновления БД. Однако надпись на панели не обновляется пока не закончится цикл обработки БД. В цикле вызывается Panel.Caption := ...
После
присвоения Panel.Caption вызывайте Panel.Refresh или Application.ProcessMessages
(второй вариант предпочтительней, так как позволяет перерисовать себя всем клмплнентам,
которые в этом нуждаются).
23. Включение
и выключение подсказок (Hints) для всех элементов на форме.
Если ваша форма содержит панель подсказки в нижней части формы, то вы можете определить подменю для этой панели, и выставлять Form.ShowHint в True или False в зависимости от состояния Checked элемента меню.
Например, в TMenuItem.OnClick напишите:
Можно
поступить таким образом:
var Bmp1: TBitmap; begin Bmp1 := TBitmap.Create; Bmp1.LoadFromFile('C:\WHERE\B1.BMP'); SetMenuItemBitmaps(MenuItemTest.Handle, 0, MF_BYPOSITION, Bmp1.Handle, Bmp1.Handle); end; |
Параметры:
Представим, что данные находятся в массиве:
У TComboBox есть run-time свойство, не упомянутое в on-line help - DroppedDown.
Для открытия ComboBox напишите:
Попробуйте
посмотреть в Windows API Help разделы, связанные с lpTemplateName. Вообще
говоря, вы можете заменить стандартный Open Dialog Box своим собственным шаблоном.
28. Проблема в использовании
компонента TCustomGrid.
Делаю так:
Проблема
в том, что TCustomGrid имеет метод DrawCell, который является
абстрактным. То, что его безусловно надо переписывать у любого наследника TCustomGrid,
к сожалению, не отражено в документации. Создайте этот метод (пусть даже пустой)
и ваша проблема исчезнет.
29. Как установить формат
для поля таблицы?
В
Fields Editor выберите поле для форматирования. Используя свойства DisplayFormat
и EditFormat сделайте то, что нужно. DisplayFormat работает для
поля, на которое не установлен фокус. EditFormat работает для поля, на
которое фокус установлен. Форматирование аналогично первому параметру в функции
FormatFloat, но без скобок.
30. Можно
ли использовать клавишу ENTER при вводе данных для перехода от поля к полю?
Используйте
данный код для события OnKeyPress компонента TEdit.
procedure TForm1.Edit1KeyPress(Sender: TObject; var
Key: Char); begin if Key = #13 then begin SelectNext(Sender as TWinControl, True, True); Key := #0; end; end; |
Теперь
Enter ведет себя как Tab. Затем, выберите все объекты, которые должны вести
себя как Edit1 (за исключением кнопок) и в Object Inspector установите
обработчик OnKeyPress в Edit1KeyPress. Каждый выбранный вами объект
воспринимает Enter как Tab. Если вы хотите обрабатывать событие на уровне формы
(а не в каждом отдельном компоненте), уберите обработчики события у всех компонент
и создайте FormKeyPress - обработчик OnKeyPress для формы:
procedure TForm1.FormKeyPress(Sender: TObject; var
Key: Char); begin if Key = #13 then begin SelectNext(Sender as TWinControl, True, True); Key := #0; end; end; |
[Назад][Содержание][Вперед]