• BatchMove 
  • Пересборка индексов с помощью TBatchMove
  • Есть некоторая таблица и требуется при нажатии на кнопку создавать таблицы такой же структуры. Подскажите, как это удобнее всего сделать?
  • Button 
  • Цветная кнопка
  • Обработка щелчка нескольких кнопок, используя их заголовок
  • CheckBox 
  • Массив из CheckBox – использование разделяемого обработчика события I
  • Массив из CheckBox – использование разделяемого обработчика события II
  • Идентификация CheckBox'ов
  • BitBtn 
  • Кнопка с несколькими строчками текста III
  • ComboBox 
  • Хочу реализовать правильный выпадающий контрол (combo). Как это сделать?
  • Программное открытие ComboBox II
  • Проблемы с ComboBox
  • DBEdit 
  • Исправление DBEdit MaxLength
  • Поиск и управление TEdit/TField
  • Insert/Override с помощью DBEdit
  • Как очистить DBEdit
  • DBGrid
  • Dbgrid и множественный выбор
  • Вертикальная полоса прокрутки Dbgrid
  • TDBGrid Lookup-поле в D2
  • Как зафиксировать один или несколько столбцов в TDBGrid с возможностью навигации по этим столбцам?
  • Dbgrid с цветными ячейками IV
  • Dbgrid с цветными ячейками V
  • Что я получаю от наличия ConstraintBroker (брокера ограничений)?
  • Улучшенный Dbgrid
  • Пример Drag and Drop между двумя Dbgrid
  • Как заставить DBGrid сортировать данные по щелчку на заголовке столбца?
  • DBGrid и TQuery
  • DBGrid как навигатор
  • Позиция DBGrid
  • DBGrid – переход к следующей записи
  • onClick и DBGrid
  • Числа с плавающей точкой в DBGrid
  • Получение данных DBGrid прежде, чем они будут отправлены: как мне узнать, что пользователь вводит в DBGrid?
  • Хочу шапку в TDBGrid. Как сделать?
  • Несколько таблиц в одном TDBGrid
  • Как сделать так, чтобы в DBGrid напротив некоторых строк можно было бы галочку поставить?
  • Как в TDBGrid разрешить только операции UPDATE записей и запретить INSERT/DELETE?
  • Обновление TDBGrid после редактирования отдельной записи на отдельной форме
  • Пересортица в коде полей TDBGrid во время вополнения программы
  • В Delphi 3 и выше ползунок TDBGrid иногда может находится не только в трех фиксированных позициях. Что для этого нужно?
  • Изменение месторасположение колонок в TDBGrid
  • Решение проблемы передачи фокуса TDBGrid
  • Как отучить TDBGrid от автодобавления новой записи?
  • Две таблицы в одном TDBGrid
  • Добавление к TDBGrid события OnClick
  • Позиция ячейки в TDBGrid
  • Dbgrid с цветными ячейками VI
  • Показ Memo-поля в Dbgrid
  • Как определить изменение фокуса строки в TDBGrid?
  • Включение ComboBox в TDBGrid
  • DBLookupComboBox 
  • Предустановка DBLookupComboBox
  • Сортировка DBLookupComboBox по вторичному индексу
  • Значение DBLookupComboBox
  • DBMemo 
  • Копирование содержимого DBMemo в DBMemo другого поля
  • Поиск текста в DBMemo
  • DBNavigator 
  • Настройки всплывающих подсказок в DBNavigator во время выполнения приложения
  • Выключение кнопок в DBNavigator
  • Работа в коде с кнопками DBNavigator
  • Edit 
  • Денежное поле редактирования
  • Отслеживаем позицию курсора в EditBox
  • GroupBox
  • Рисование на GroupBox
  • Доступ к компонентам GroupBox
  • Label
  • Как сделать бегущую строку?
  • ListBox 
  • Навигация в ListBox при множественном выборе
  • Внешние данные и ListBox
  • Инкрементальный поиск в ListBox II
  • Табуляция в графическом ListBox'е
  • Выравнивание в ListBox'е
  • Создание ListBox во время выполнения программы
  • Двойной ListBox
  • ListBox — OnChange
  • MainManu 
  • Как рисовать картинки в пунктах меню (через OwnerDraw)?
  • Memo 
  • Получение данных из компонента Memo
  • Изменение поведения Delete в компоненте Memo
  • Вставка текста в TMemo II
  • NoteBook 
  • Включение/Выключение закладки Notebook II
  • OutLine 
  • Раскрытие пути к элементу TOutline по его индексу
  • PageControl 
  • Динамические PageControl/TabSheet I
  • Динамические PageControl/TabSheet II
  • Клавиши-акселераторы для TPageControl
  • Panel 
  • Создание панелей во время работы приложения
  • PopupMenu 
  • Вызов контекстного меню в позиции курсора II
  • Иконки в PopupMenu
  • ProgressBar 
  • ProgressBar — невидимка
  • Query 
  • Можно ли использовать результаты выполнения одного TQuery для другого TQuery?
  • Можно ли вызвать хранимую процедуру через TQuery, если она не возвращает курсора?
  • TQUERY и TDBGRID
  • Две и более команд в свойстве TQUERY.SQL
  • RichEdit 
  • Как вставить в нужное место Rich Text в TRichEdit?
  • Как указать максимальный размер текста для TRichEdit?
  • Позиция курсора в TRichEdit
  • RadioGroup 
  • Группа радиокнопок и ActiveControl
  • ScrollBar 
  • Мерцание ScrollBar
  • SpeedButton 
  • Speedbutton и Glyph
  • StringGrid 
  • Обновление картинки в ячейке StringGrid
  • Многострочность в заголовках колонок StringGrid
  • StringGrid без выделенной ячейки
  • Один щелчок на StringGrid вместо трех
  • StringGrid как DBGrid
  • `Авторазмер` для StringGrid
  • Выравнивание колонок StringGrid III
  • Выравнивание колонок StringGrid IV
  • Покрашенный StringGrid I
  • Покрашенный StringGrid II
  • Редактирование в StringGrid
  • Tabbednotebook 
  • Tabbednotebook и куча ресурсов
  • Доступ к страницам Tabbednotebook
  • TabControl 
  • Над какой закладкой курсор?
  • Table 
  • Создание таблицы в модуле
  • TabSet 
  • Изменение количества закладок в TTabSet во время выполнения программы
  • Timer 
  • Остановка таймера на `полпути`
  • TreeView 
  • Поточность TreeView
  • Получение доступа к узлам TreeView
  • Хочется выделять некоторые стpочки в TTreeView жирным или бледным. Как?
  • UpdateSQL 
  • Что нужно знать о принципе и порядке работы с TUpdateSQL для работы с неживыми запросами?
  • Разное 
  • Создание компонентов для работы с базами данных
  • Динамическое создание компонент во время работы приложения
  • Решение для динамически создаваемых компонентов
  • Как правильно создавать органы управления в runtime?
  • Как создать клон (копию, достаточно близкую к оригиналу) произвольного компонента?
  • Как заставить произвольный компонент реагировать на изменения в TDataSource?
  • Доступ к другим компонентам из базового
  • CANVAS.TEXTWIDTH
  • Создание компонента
  • Циклический опрос компонентов
  • Мне надо добавить много строк в TListbox или в TCombobox или в TMemo или в TRichEdit, при этом сам объект постоянно мигает, перерисовываясь. Как избавиться от этого?
  • Как мне создать компонент типа TField?
  • Инкрементация строкового поля
  • Компоненты 

    BatchMove 

    Пересборка индексов с помощью TBatchMove

    Delphi 1 

    … вы все делаете правильно. BatchMove не может пересобирать индексы. Тем не менее, следующая процедура все же поможет вам сделать это (создать индексы заново). Задайте ей необходимые параметры (.DBF. Name, исходная и целевая таблица, Source и Target) и попробуйте ее в деле!

    procedure Form1.FormCreate(Sender: TObject);

    var x: integer;

    begin

     BatchMove1.Execute;

     Source.Open;

     Target.Exclusive := True;

     Target.Open;

     Source.IndexDefs.Update;

     for x := 0 to Source.IndexDefs.Count – 1 do

      Target.AddIndex(Source.IndexDefs[x].Name, Source.IndexDefs[x].Fields, Source.IndexDefs[x].Options);

     Source.Close;

     Target.Close;

    end;

    Есть некоторая таблица и требуется при нажатии на кнопку создавать таблицы такой же структуры. Подскажите, как это удобнее всего сделать?


    Nomadic отвечает:

    Удобней всего, например, так —

    with bmovMyBatchMove do begin

     Mode := bmCopy;

     RecordCount := 1;

     Execute;

     R Destination.Delete;

    end;

    Где bmovMyBatchMove – экземпляр класса TBatchMove из VCL.

    Неправда Ваша! ;)

    Этот загадочный BatchMove имеет одну очень неприятную особенность (по крайней мере при работе с DBF-таблицами и в Delphi 1.0x), как-то:

    увеличивает в создаваемых таблицах в полях типа NUMBER количество значащих цифр после запятой (не помню – возможно, что и до), если там указаны небольшие (около 1-3 цифр) значения :(.

    Я эту особенность побороть не сумел, а мириться с ней в условиях нашей конторы (когда приходится бороться за место под солнцем с программистами на Clipper и FoxPro совершенно неприемлемо.

    Кроме того, в предложенном выше варианте еще и запись удалять приходится…:)

    Решалась же эта проблема следующим способом:

    procedure CopyStruct(SrcTable, DestTable: TTable; cpyFields: array of string);

    var

     i: Integer;

     bActive: Boolean;

     SrcDatabase, DestDatabase: TDatabase;

     iSrcMemSize, iDestMemSize: Integer;

     pSrcFldDes: PFldDesc; CrtTableDesc: CRTblDesc;

     bNeedAllFields: Boolean;

    begin

     SrcDatabase := Session.OpenDatabase(SrcTable.DatabaseName);

     try

      DestDatabase := Session.OpenDatabase(DestTable.DatabaseName);

      try

       bActive := SrcTable.Active;

       SrcTable.FieldDefs.Update;

       iSrcMemSize := SrcTable.FieldDefs.Count * SizeOf(FLDDesc);

       pSrcFldDes := AllocMem(iSrcMemSize);

       if pSrcFldDes = nil then begin

        raise EOutOfMemory.Create('Не хватает памяти!');

       end;

       try

        SrcTable.Open;

        Check(DbiGetFieldDescs(SrcTable.Handle, pSrcFldDes));

        SrcTable.Active := bActive;

        FillChar(CrtTableDesc, SizeOf(CrtTableDesc), 0);

        with CrtTableDesc do begin

         StrPcopy(szTblName, DestTable.TableName);

         StrPcopy(szTblType, 'DBASE');

         if (Length(cpyFields[0] ) = 0) or (cpyFields[0] = '*') then begin

          bNeedAllFields := True;

          SrcTable.FieldDefs.Update;

          iFldCount := SrcTable.FieldDefs.Count;

         end else begin

          bNeedAllFields := False;

          iFldCount := High(cpyFields) + 1;

         end;

         iDestMemSize := iFldCount * Sizeof(FLDDesc);

         CrtTableDesc.pFLDDesc := AllocMem(iDestMemSize);

         if CrtTableDesc.pFLDDesc = nil then begin

          raise EOutOfMemory.Create('Не хватает памяти!');

         end;

        end;

        try

         if bNeedAllFields then begin

          for i := 0 to CrtTableDesc.iFldCount - 1 do begin

           Move(PFieldDescList(pSrcFldDes)^[i], PFieldDescList(CrtTableDesc.pFLDDesc)^[i], SizeOf(FldDesc));

          end;

         end else begin

          for i:=0 to CrtTableDesc.iFldCount-1 do begin

           Move(PFieldDescList(pSrcFldDes)^[SrcTable.FieldDefs.Find(cpyFields[i]).FieldNo – 1], PFieldDescList(CrtTableDesc.pFLDDesc)^[i], SizeOf(FldDesc));

          end;

         end;

         Check(DbiCreateTable(DestDatabase.Handle, True, CrtTableDesc));

        finally

         FreeMem(CrtTableDesc.pFLDDesc, iDestMemSize);

        end;

       finally

        FreeMem(pSrcFldDes, iSrcMemSize);

       end;

      finally

       Session.CloseDatabase(DestDatabase);

      end;

     finally

      Session.CloseDatabase(SrcDatabase);

     end;

    end;

    Button 

    Цветная кнопка

    VS пишет:

    В книгах Калверта, Свана и других авторов можно найти похожий текст. Смысл текста — "Изменить цвет кнопок Button, BitBt нельзя, т.к. их рисует WINDOWS". Если нельзя, но ОЧЕНЬ НУЖНО, то можно.

    Небольшой компонент ColorBtn, дает возможность использовать в кнопках цвет. Кроме того, представлено новое свойство — Frame3D, позволяющее получить более реалистичный вид нажатой кнопки. В отличие от API, при изменении значения свойства Frame3D, не требуется переоткрытие компонента.

    unit ColorBtn;


    interface

    uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons;


    type TColorBtn = class(TButton)

    private

     { Private declarations }

     IsFocused: boolean;

     FCanvas: TCanvas;

     F3DFrame: boolean;

     FButtonColor: TColor;

     procedure Set3DFrame(Value: boolean);

     procedure SetButtonColor(Value: TColor);

     procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM;

     procedure WMLButtonDblClk(var Message: TWMLButtonDblClk); message WM_LBUTTONDBLCLK;

     procedure DrawButtonText(const Caption: string; TRC: TRect; State: TButtonState; BiDiFlags: Longint);

     procedure CalcuateTextPosition(const Caption: string; var TRC: TRect; BiDiFlags: Longint);

    protected

     { Protected declarations }

     procedure CreateParams(var Params: TCreateParams); override;

     procedure SetButtonStyle(ADefault: boolean); override;

    public

     { Public declarations }

     constructor Create(AOwner: TComponent); override;

     destructor Destroy; override;

    published

     { Published declarations }

     property ButtonColor: TColor read FButtonColor write SetButtonColor default clBtnFace;

     property Frame3D: boolean read F3DFrame write Set3DFrame default False;

    end;


    procedure Register;


    implementation

    { TColorBtn }

    constructor TColorBtn.Create(AOwner: TComponent);

    begin

     Inherited Create(AOwner);

     FCanvas:= TCanvas.Create;

     FButtonColor:= clBtnFace;

     F3DFrame:= False;

    end;


    destructor TColorBtn.Destroy;

    begin

     FCanvas.Free;

     Inherited Destroy;

    end;


    procedure TColorBtn.CreateParams(var Params: TCreateParams);

    begin

     Inherited CreateParams(Params);

     with Params do Style:= Style or BS_OWNERDRAW;

    end;


    procedure TColorBtn.Set3DFrame(Value: boolean);

    begin

     if F3DFrame <> Value then F3DFrame:= Value;

    end;


    procedure TColorBtn.SetButtonColor(Value: TColor);

    begin

     if FButtonColor <> Value then begin

      FButtonColor:= Value;

      Invalidate;

     end;

    end;


    procedure TColorBtn.WMLButtonDblClk(var Message: TWMLButtonDblClk);

    begin

     Perform(WM_LBUTTONDOWN, Message.Keys, Longint(Message.Pos));

    end;


    procedure TColorBtn.SetButtonStyle(ADefault: Boolean);

    begin

     if IsFocused <> ADefault then IsFocused:= ADefault;

    end;


    procedure TColorBtn.CNDrawItem(var Message: TWMDrawItem);

    var

     RC: TRect;Flags: Longint;

     State: TButtonState;

     IsDown, IsDefault: Boolean;

     DrawItemStruct: TDrawItemStruct;

    begin

     DrawItemStruct:= Message.DrawItemStruct^;

     FCanvas.Handle:= DrawItemStruct.HDC;

     RC:= ClientRect;

     with DrawItemStruct do begin

      IsDown:= ItemState and ODS_SELECTED <> 0;

      IsDefault:= ItemState and ODS_FOCUS <> 0;

      if not Enabled then State:= bsDisabled

      else if IsDown then State:= bsDown

      else State:= bsUp;

     end;

     Flags:= DFCS_BUTTONPUSH or DFCS_ADJUSTRECT;

     if IsDown then Flags:= Flags or DFCS_PUSHED;

     if DrawItemStruct.ItemState and ODS_DISABLED <> 0 then Flags:= Flags or DFCS_INACTIVE;

     if IsFocused or IsDefault then begin

      FCanvas.Pen.Color:= clWindowFrame;

      FCanvas.Pen.Width:= 1;

      FCanvas.Brush.Style:= bsClear;

      FCanvas.Rectangle(RC.Left, RC.Top, RC.Right, RC.Bottom);

      InflateRect(RC, -1, -1);

     end;

     if IsDown then begin

      FCanvas.Pen.Color:= clBtnShadow;

      FCanvas.Pen.Width:= 1;

      FCanvas.Rectangle(RC.Left, RC.Top, RC.Right, RC.Bottom);

      InflateRect(RC, -1, -1);

      if F3DFrame then begin

       FCanvas.Pen.Color:= FButtonColor;

       FCanvas.Pen.Width:= 1;

       DrawFrameControl(DrawItemStruct.HDC, RC, DFC_BUTTON, Flags);

      end;

     end else DrawFrameControl(DrawItemStruct.HDC, RC, DFC_BUTTON, Flags);

     FCanvas.Brush.Color:= FButtonColor;

     FCanvas.FillRect(RC);

     InflateRect(RC, 1, 1);

     if IsFocused then begin

      RC:= ClientRect;

      InflateRect(RC, -1, -1);

     end;

     if IsDown then OffsetRect(RC, 1, 1);

     FCanvas.Font:= Self.Font;

     DrawButtonText(Caption, RC, State, 0);

     if IsFocused and IsDefault then begin

      RC:= ClientRect;

      InflateRect(RC, -4, -4);

      FCanvas.Pen.Color:= clWindowFrame;

      Windows.DrawFocusRect(FCanvas.Handle, RC);

     end;

     FCanvas.Handle:= 0;

    end;


    procedure TColorBtn.CalcuateTextPosition(const Caption: string; var TRC: TRect; BiDiFlags: Integer);

    var

     TB: TRect;

     TS, TP: TPoint;

    begin

     with FCanvas do begin

      TB:= Rect(0, 0, TRC.Right + TRC.Left, TRC.Top + TRC.Bottom);

      DrawText(Handle, PChar(Caption), Length(Caption), TB, DT_CALCRECT or BiDiFlags);

      TS := Point(TB.Right - TB.Left, TB.Bottom - TB.Top);

      TP.X := ((TRC.Right - TRC.Left) - TS.X + 1) div 2;

      TP.Y := ((TRC.Bottom - TRC.Top) - TS.Y + 1) div 2;

      OffsetRect(TB, TP.X + TRC.Left, TP.Y + TRC.Top);

      TRC:= TB;

     end;

    end;


    procedure TColorBtn.DrawButtonText(const Caption: string; TRC: TRect; State: TButtonState; BiDiFlags: Integer);

    begin

     with FCanvas do begin

      CalcuateTextPosition(Caption, TRC, BiDiFlags);

      Brush.Style:= bsClear;

      if State = bsDisabled then begin

       OffsetRect(TRC, 1, 1);

       Font.Color:= clBtnHighlight;

       DrawText(Handle, PChar(Caption), Length(Caption), TRC, DT_CENTER or DT_VCENTER or BiDiFlags);

       OffsetRect(TRC, -1, -1);

       Font.Color:= clBtnShadow;

       DrawText(Handle, PChar(Caption), Length(Caption), TRC, DT_CENTER or DT_VCENTER or BiDiFlags);

      end else DrawText(Handle, PChar(Caption), Length(Caption), TRC, DT_CENTER or DT_VCENTER or BiDiFlags);

     end;

    end;


    procedure Register;

    begin

     RegisterComponents('Controls', [TColorBtn]);

    end;

    end.

    Небольшое дополнение. Кнопку по прежнему рисует WINDOWS, а раскрашивает ее ColorBtn. Код компонента на 90% повторяет код BitBtn, ничего необычного здесь нет. Хочется повторить слова Калверта — «Пользуйтесь исходным кодом». Чаще заглядывайте в VCL – можно найти много интересного. 

    Обработка щелчка нескольких кнопок, используя их заголовок

    Delphi 1 

    …с ваших слов я понял, что вы все уже реализовали, но давайте все повторим: вы должны убедиться в том, что событие OnClick привязано к каждой кнопке калькулятора (числовые кнопки 0..9) и указывают на общий обработчик события.

    В разделяемом обработчике события получите заголовок обрабатываемой кнопки следующим образом:

    Edit1.Text := TButton(Sender).Caption;

    …я думаю в этом случае самым разумным будет использование свойства Tag каждой кнопки:

    1. назначьте уникальный Tag для каждой кнопки (например, эквивалент арабским цифрам)

    2.

    procedureTForm1.Button1Click(Sender: TObject);

    begin

     if (Sender is TButton) then with (Sender as TButton) do

      {используем Tag}

    end;

    Если вам нужен только заголовок, то есть изящный способ получить к нему доступ. Подключите общий обработчик события для всех кнопок и используйте приведение типа как показано ниже:

    procedure TForm1.Edit1Click(Sender: TObject);

    begin

     edit1.text := (sender as TButton).caption;

    end;

    Приведенная ниже конструкция будет недостаточной:

    sender.caption

    поскольку компилятор не знает о том, имеет ли «sender» свойство caption, или нет. 

    CheckBox 

    Массив из CheckBox – использование разделяемого обработчика события I

    Delphi 1 

    Поместите несколько Checkbox в компонент TGroupBox. Во время прогона (или проектирования) назначьте общий обработчик события Click для всех checkbox'в. Чтобы в цикле обойти все «дочерние» TCheckBox'ы, можно воспользоваться свойством-массивом Controls TGroupBox (и заодно привести их к типу TCheckBox). Приблизительно так:

    for i := 0 to GroupBox1.ControlCount -1 do


     if  (GroupBox1.Controls[i] as TCheckBox).checked then

      {что-то там еще};

    Вы можете получить имя sender следующим образом:

    procedure TMain1.CheckBoxClick(Sender: TObject);

    var whodidit: string[63];

    begin

     whodidit := TComponent(sender).name;

    end;

    После приведения типа можно добраться и до других свойств. К примеру, очень полезным может оказаться свойство Tag. Во время создания, вы можете присвоить каждому checkbox.tag свой ID номер. А в обработчике события, читая ID, можно идентифицировать sender. 

    Массив из CheckBox – использование разделяемого обработчика события II

    Delphi 1

    var

     CheckArray: array[1..x] of TCheckBox;

     i:integer;

    begin

     for i:=1 to x do begin

      CheckArray[i]:=TCheckBox.Create(Form1);

      {Устанавливаем свойства}

      with CheckBox[i] do begin

       Left:=i*20;

       Width:=15;

       другое…

      end;

     end;

    Очевидно, можно сказать:

    Check[i].OnClick:=xyz.

    Пока я и сам не знаю как поступить. Динамическое создание компонентов да, но обработчики событий?

    Существует способ организации массива checkbox'ов с разделяемым обработчиком события. Расположите их на форме и дайте им «непрерывные» имена (Check1, Check2 и т.д.). Затем установите у них общий обработчик события. Обработчик события может выглядеть так:

    procedure TForm.Check1Click(Sender : TObject);

    var i : Integer;

    begin

     for i := 1 to 10 { предположим, что мы имеем 10 checkbox'ов } do

      With TCheckBox(FindComponent('Check'+IntToStr(i))) do begin

      { другой какой-то код }

     end;

    end;

    Идентификация CheckBox'ов

    Delphi 3

    В режиме проектирования вы, как программист, без труда узнаете, сколько checkbox'ов содержит ваша форма. А вот когда приложение запущено… Используйте Delphi Run Time Type Information (RTTI). Для нашей испытуемой формы вы можете попробовать следующий код:

    var i : Integer

    begin

     for i := 0 to ComponentCount - 1 do

      if Components[i] is TCheckBox then

      (Components[i] as TCheckBox).Checked then begin

      ... сюда поместите ваш код ...

     end;

    end;

    Кроме того, следующий код Delphi абсолютно корректен:

    if Components[i] = CheckBox5 then Чтотоделаем;

    Также, каждый компонент в Delphi имеет опубликованное (Published) свойство с именем 'Tag', значение которого вы можете задавать во время создания компонента, и затем, во время выполнения приложения, обращаться к нему для получения доступа к компоненту:

    var i : Integer

    begin

     for i := 0 to ComponentCount - 1 do

      if Components[i] is TCheckBox then

      with (Components[i] as TCheckBox) do

      Case Tag of

      1 : if Checked then DoSomethingOnBox1;

      2 : if Checked then DoSomethingOnBox2;

      … другое …

     end;

    end;

    Для получения дополнительной информации, обратитесь к справке Delphi с ключевым словом «ComponentCount».

    BitBtn 

    Кнопка с несколькими строчками текста III

    Вот полный код проекта, создающего на кнопке во время выполнения две строчки текста.

    program TwolnBtn;

    uses Forms,TwolnBtu in 'TWOLNBTU.PAS' {Form1};

    {$R *.RES}

    begin

     Application.CreateForm(TForm1, Form1);

     Application.Run;

    end.

    Файл TWOLNBTU.TXT → TWOLNBTU.DFM

    object Form1: TForm1

     Left = 202

     Top = 98

     Width = 320

     Height = 176

     Caption = 'Form1'

     Font.Color = clRed

     Font.Height = -12

     Font.Name = 'Arial'

     Font.Style = [fsBold]

     PixelsPerInch = 96

     OnActivate = ChgSpeedButton

     OnCreate = ChgBitBtn

     TextHeight = 15

     object SpeedButton1: TSpeedButton

      Left = 144

      Top = 24

      Width = 65

      Height = 45

      Caption = 'Это двустрочный заголовок'

      OnClick = ChgSpeedButton

     end

     object

     BitBtn1: TBitBtn

      Left = 32

      Top = 24

      Width = 69

      Height = 37

      Caption = 'Прерывание работы программы'

      TabOrder = 0

      OnClick = BitBtn1Click

     end

    end

    Файл TWOLNBTU.PAS

    unit Twolnbtu;

    interface



    uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons;


    type TForm1 = class(TForm)

     BitBtn1: TBitBtn;

     SpeedButton1: TSpeedButton;

     procedure ChgBitBtn(Sender: TObject);

     procedure ChgSpeedButton(Sender: TObject);

     procedure BitBtn1Click(Sender: TObject);

    private

     { Private declarations }

    public

     { Public declarations }

    end;


    var Form1: TForm1;


    implementation


    {$R *.DFM}


    procedure TForm1.ChgBitBtn(Sender: TObject);

    VAR

    R : TRect;

     N : Integer;

     Buff : ARRAY[0..255] OF Char;

    BEGIN

     WITH BitBtn1 DO BEGIN

      Glyph.Canvas.Font := Self.Font;

      Glyph.Width  := Width-6;

      Glyph.Height := Height-6;

      R := Bounds(0,0,Glyph.Width,0);

      StrPCopy(Buff, Caption);

      Caption := '';

      DrawText(Glyph.Canvas.Handle, Buff, StrLen(Buff), R, DT_CENTER OR DT_WORDBREAK OR DT_CALCRECT);

      OffsetRect(R, (Glyph.Width-R.Right) DIV 2, (Glyph.Height - R.Bottom) DIV 2);

      DrawText(Glyph.Canvas.Handle, Buff, StrLen(Buff), R, DT_CENTER OR DT_WORDBREAK);

     END;

    END;


    procedure TForm1.ChgSpeedButton(Sender: TObject);

    VAR

    R : TRect;

     N : Integer;

     Buff : ARRAY[0..255] OF Char;

    BEGIN

     WITH SpeedButton1 DO BEGIN

      Glyph.Canvas.Font := Self.Font;

      Glyph.Width  := Width-6;

      Glyph.Height := Height-6;

      R := Bounds(0,0,Glyph.Width,0);

      StrPCopy(Buff, Caption);

      Caption := '';

      DrawText(Glyph.Canvas.Handle, Buff, StrLen(Buff), R,DT_CENTER OR DT_WORDBREAK OR DT_CALCRECT);

      OffsetRect(R, (Glyph.Width-R.Right) DIV 2, (Glyph.Height - R.Bottom) DIV 2);

      DrawText(Glyph.Canvas.Handle, Buff, StrLen(Buff), R,DT_CENTER OR DT_WORDBREAK);

     END;

    END;


    procedure TForm1.BitBtn1Click(Sender: TObject);

    begin

     Close;

    end;

    end.

    -Dennis Passmore

    ComboBox 

    Хочу реализовать правильный выпадающий контрол (combo). Как это сделать?

    Nomadic отвечает:

    Когда-то потратил немало времени на разбор, как же все таки работают дропдаун-контролы. В итоге мной был написан маленький юнит, который я положил у себя в каталоге Demo для ознакомления интересующихся. Он маленький (его основная задача — показать принцип работы, а все остальное — как реализуешь), я думаю, что большинству он пригодиться, поэтому публикую здесь. Касательно твоего вопроса - реализуй вместо листбокса выпадающий контрол, который даст тебе функциональность дерева.

    unit edit1;


    interface


    uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;


    type

     TPopupListbox = class(TCustomListbox)

     protected

      procedure CreateParams(var Params: TCreateParams); override;

      procedure CreateWnd; override;

      procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;

     end;


     TTestDropEdit = class(TEdit)

     private

      FPickList: TPopupListbox;

      procedure CMCancelMode(var Message: TCMCancelMode); message CM_CancelMode;

      procedure WMKillFocus(var Message: TMessage); message WM_KillFocus;

     protected

      procedure CloseUp(Accept: Boolean);

      procedure DropDown;

      procedure WndProc(var Message: TMessage); override;

     public

      constructor Create(Owner: TComponent); override;

      destructor Destroy; override;

     end;


    implementation


    { TPopupListBox }


    procedure TPopupListBox.CreateParams(var Params: TCreateParams);

    begin

     inherited;

     with Params do begin

      Style := Style or WS_BORDER;

      ExStyle := WS_EX_TOOLWINDOW or WS_EX_TOPMOST;

      WindowClass.Style := CS_SAVEBITS;

     end;

    end;


    procedure TPopupListbox.CreateWnd;

    begin

     inherited CreateWnd;

     Windows.SetParent(Handle, 0);

     CallWindowProc(DefWndProc, Handle, WM_SETFOCUS, 0, 0);

    end;


    procedure TPopupListbox.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

    begin

     inherited MouseUp(Button, Shift, X, Y);

     TTestDropEdit(Owner).CloseUp((X >= 0) and (Y >= 0) and (X < Width) and (Y < Height));

    end;


    { TTestDropEdit }


    constructor TTestDropEdit.Create(Owner: TComponent);

    begin

     inherited Create(Owner);

     Parent := Owner as TWinControl;

     FPickList := TPopupListbox.Create(nil);

     FPickList.Visible := False;

     FPickList.Parent := Self;

     FPickList.IntegralHeight := True;

     FPickList.ItemHeight := 11;

     FPickList.Items.CommaText :='1,2,3,4,5,6,7,8,9,0';

    end;


    destructor TTestDropEdit.Destroy;

    begin

     FPickList.Free;

     inherited;

    end;


    procedure TTestDropEdit.CloseUp(Accept: Boolean);

    begin

     if FPickList.Visible then begin

      if GetCapture <> 0 then SendMessage(GetCapture, WM_CANCELMODE, 0, 0);

      SetWindowPos(FPickList.Handle, 0, 0, 0, 0, 0, SWP_NOZORDER or SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE or SWP_HIDEWINDOW);

      if FPickList.ItemIndex <> -1 then Text := FPickList.Items.Strings[FPickList.ItemIndex];

      FPickList.Visible := False;

      Invalidate;

     end;

    end;


    procedure TTestDropEdit.DropDown;

    var

     P: TPoint;

     I,J,Y: Integer;

    begin

     if Assigned(FPickList) and (not FPickList.Visible) then begin

      FPickList.Width := Width;

      FPickList.Color := Color;

      FPickList.Font := Font;

      FPickList.Height := 6 * FPickList.ItemHeight + 4;

      FPickList.ItemIndex := FPickList.Items.IndexOf(Text);

      P := Parent.ClientToScreen(Point(Left, Top));

      Y := P.Y + Height;

      if Y + FPickList.Height > Screen.Height then Y := P.Y - FPickList.Height;

      SetWindowPos(FPickList.Handle, HWND_TOP, P.X, Y, 0, 0, SWP_NOSIZE or SWP_NOACTIVATE or SWP_SHOWWINDOW);

      FPickList.Visible := True;

      Invalidate;

      Windows.SetFocus(Handle);

     end;

    end;


    procedure TTestDropEdit.CMCancelMode(var Message: TCMCancelMode);

    begin

     if (Message.Sender <> Self) and (Message.Sender <> FPickList) then CloseUp(False);

    end;


    procedure TTestDropEdit.WMKillFocus(var Message: TMessage);

    begin

     inherited;

     CloseUp(False);

    end;


    procedure TTestDropEdit.WndProc(var Message: TMessage);

     procedure DoDropDownKeys(var Key: Word; Shift: TShiftState);

     begin

      case Key of

      VK_UP, VK_DOWN:

       if ssAlt in Shift then begin

        if FPickList.Visible  then CloseUp(True)

        else DropDown;

        Key := 0;

       end;

      VK_RETURN, VK_ESCAPE:

       if FPickList.Visible  and not (ssAlt in Shift) then begin

        CloseUp(Key = VK_RETURN);

        Key := 0;

       end;

      end;

     end;

    begin

     case Message.Msg of

     WM_KeyDown, WM_SysKeyDown, WM_Char:

      with TWMKey(Message) do begin

       DoDropDownKeys(CharCode, KeyDataToShiftState(KeyData));

       if (CharCode <> 0) and FPickList.Visible then begin

        with TMessage(Message) do SendMessage(FPickList.Handle, Msg, WParam, LParam);

        Exit;

       end;

      end

     end;

     inherited;

    end;

    end.
     

    Программное открытие ComboBox II

    Delphi 1

    procedureTForm1.ComboBox1Enter(Sender:TObject);

    begin

     SendMessage(ComboBox1.Handle, CB_SHOWDROPDOWN, Integer(True), 0);

    end;

    Поместите эту строку в обработчик события OnEnter ComboBox:

    SendMessage(combobox1.Handle, CB_SHOWDROPDOWN, 1, 0);

    Измените третий параметр (1) на 0, если вы хотите спрятать список. 

    Проблемы с ComboBox

    Delphi 1 

    …попробуйте сохранять в переменной в методе формы OnEnter или OnCreate значение Index. Затем, чтобы отменить выбор пользователя, сделайте:

    ComboBox1.ItemIndex := var1;

    DBEdit 

    Исправление DBEdit MaxLength

    Delphi 1

    Я, кажется, не могу получить свойство 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

    Поиск и управление 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

    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 

    Как очистить DBEdit

    Delphi 1 

    Пробую так:

    myDbEdit.Text := '';

    или адрес TField, если вы хотите так:

    TableNameMyField.Value := '';

    Ответ:

    Table1.Edit;

    Table1.FieldByName(DBEdit1.FieldName).Clear;

    DBGrid

    Dbgrid и множественный выбор

    Delphi 2 

    Тема: 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.

    Вертикальная полоса прокрутки Dbgrid

    Delphi 1

    Это небольшое исправление к исходному коду VCL, позволяющее поддерживать перемещение по таблице с помощью изменения позиции движка вертикальной полосы прокрутки.

    (Примечание: это работает только с таблицами Paradox и BDE. Для использования этого кода с другими таблицами/движками вам необходимо заменить DBIGetSeqNo на функцию, надежно возвращающую текущую позицию записи вне зависимости от того, использует ли таблица индекс или нет.)

    В DBGRID.PAS измените две следующих процедуры:

    procedure TCustomDBGrid.UpdateScrollBar;

    var

     Pos: Integer;

     mPos, mMax: longint;

    begin

     if FDatalink.Active and HandleAllocated then

      with FDatalink.DataSet do begin

      UpdateCursorPos;

      if (DBIGetSeqNo(Handle,mPos) = DBIERR_NONE) then begin

      mMax := RecordCount;

       while mMax > 1000 do begin

        mMax := mMax div 10;

        mPos := mPos div 10;

       end;

       SetScrollRange(Self.Handle, SB_VERT, 1, mMax, False);

      end else begin

       if BOF then mPos := 0

       else if EOF then mPos := 4

       else mPos := 2;

       SetScrollRange(Self.Handle, SB_VERT, 0, 4, False);

      end; (**)

      if GetScrollPos(Self.Handle, SB_VERT) <> mPos then

       SetScrollPos(Self.Handle, SB_VERT, mPos, True);

     end;

    end;


    procedure TCustomDBGrid.WMVScroll(var Message: TWMVScroll);

    var

     mMin, mMax: integer;

     RecCount, RecNo, NewRecNo: longint;

    begin

     if not AcquireFocus then Exit;

     if FDatalink.Active then

      with Message, FDataLink.DataSet, FDatalink do

      case ScrollCode of

      SB_LINEUP: MoveBy(-ActiveRecord - 1);

      SB_LINEDOWN: MoveBy(RecordCount - ActiveRecord);

      SB_PAGEUP: MoveBy(-VisibleRowCount);

      SB_PAGEDOWN: MoveBy(VisibleRowCount);

      SB_THUMBPOSITION:

       if (DBIGetSeqNo(Handle,RecNo) = DBIERR_NONE) then begin

        GetScrollRange(self.Handle, SB_VERT, mMin, mMax);

        NewRecNo := Pos*(FDataLink.DataSet.RecordCount div mMax);

        MoveBy(NewRecNo-RecNo);

       end else case Pos of

       0: First;

       1: MoveBy(-VisibleRowCount);

       2: Exit;

       3: MoveBy(VisibleRowCount);

       4: Last;

       end;

      SB_BOTTOM: Last;

      SB_TOP: First;

     end;

    end;

    Имейте в виду, что из-за небольшой ошибки в VCL (MoveBy использует integer-параметр вместо longint), могут быть проблемы с большими таблицами (RecordCount>MaxInt). Объяснение этому факту я нашел в журнале Delphi Magazine. Для больших таблиц вы должны заменить вызовы MoveBy на DBISetToSeqNo или DBIGetRelativeRecord. Не забудьте после данного вызова вызвать Resnyc([]) или Refresh!

    P.S. Пока вы ковыряетесь в DBGRIDS.PAS: найдите и замените TitleColor на FixedColor в TCustomDBGrid.Create и в TCustomDBGrid.DrawCell. Значение свойства FixedColor влияет на показ заголовков колонок, и они будут выводится как и ожидалось.

    – Reinhard Kalinke 

    TDBGrid Lookup-поле в D2

    Delphi 2 

    1. Как создать lookup-поле в TDBGrid для Delphi 2.0

    2. Разместите на форме 2 компонента TTable, 1 компонент TDataSource и 1 – TDBGrid.

     • Подключите Table1 – к DataSource1 – к DBGrid1

     • DataSource1.DataSet = Table1

     • DBGrid1.DataSource = DataSource1

    3. Установка Table1

     • Table1.Database = DBDemos

     • Table1.TableName = Customer

     • Table1.Active = True

    4. Установка Table2

     • Table2.Database = DBDemos

     • Table2.TableName = Orders

     • Table2.Active = True

    5. Добавьте все поля для Table1, используя Fields Editor (редактор полей):

     • Дважды щелкните на Table1

     • Нажмите правую кнопку мыши в редакторе полей

     • Выберите пункт Add New Fields. Добавьте их все.

    6. Добавьте новое поле для Table1.

     • Нажмите правую кнопку мыши в редакторе полей и выберите пункт «New Field».

    7. Определите следующие параметры для вновь добавленного поля:

     • Name: Bob

     • Type: String

     • Size: 30

     • Select Lookup

     • Key Fields: CustNo    –  Поле в Table1 для хранения значения

     • DataSet: Table2       –  Здесь устанавливается табличный lookup

     • LookUpKeys: CustNo  –  Данный ключ копируется в KeyField

     • Result Field: OrderNo –  Значение для показа пользователю в выпадающем списке

    8. Запустите приложение

    Как зафиксировать один или несколько столбцов в TDBGrid с возможностью навигации по этим столбцам?

    Nomadic советует:

    Это маленькая вставка в Ваш наследник от TCustomDBGrid, которая решает данную задачу.

    // DBGRIDEX.PAS

    // ----------------------------------------------------------------------------

    destructor TDbGridEx.Destroy;

    begin

     _HideColumnsValues.Free;_HideColumns.Free;

     inherited Destroy;

    end;


    // ----------------------------------------------------------------------------

    constructor TDbGridEx.Create(Component : TComponent);

    begin

     inherited Create(Component);

     FFreezeCols   := ?;

     _HideColumnsValues := TList.Create;

     _HideColumns       := TList.Create;

    end;


    // ----------------------------------------------------------------------------

    procedure TDbGridEx.KeyDown(var Key: Word; Shift: TShiftState);

    begin

     if (Key = VK_LEFT) then ColBeforeEnter(-1);

     if (Key = VK_RIGHT) then ColBeforeEnter(1);

     inherited;

    end;


    // ----------------------------------------------------------------------------

    procedure TDbGridEx.SetFreezeColor(AColor : TColor);

    begin

     InvalidateRow(0);

    end;


    // ----------------------------------------------------------------------------

    procedure TDbGridEx.SetFreezeCols(AFreezeCols : Integer);

    begin

     FFreezeCols := AFreezeCols;

     InvalidateRow(0);

    end;


    // ----------------------------------------------------------------------------

    procedure TDbGridEx.ColEnter;

    begin

     ColBeforeEnter(0);

     if Assigned(OnColEnter) then OnColEnter(Self);

    end;


    // ----------------------------------------------------------------------------

    procedure TDbGridEx.ColBeforeEnter(ADelta : Integer);

    var nIndex : Integer;


     function ReadWidth : Integer;

     var i : Integer;

     begin

      i := _HideColumns.IndexOf(Columns[nIndex]);

      if i = -1 then result := 120

      else result := Integer(_HideColumnsValues[i]);

     end;


     procedure SaveWidth;

     var i : Integer;

     begin

      i := _HideColumns.IndexOf(Columns[nIndex]);

      if i <> - 1 then begin

       _HideColumnsValues[i] := Pointer(Columns[nIndex].Width);

      end else begin

       _HideColumns.Add(Columns[nIndex]);

       _HideColumnsValues.Add(Pointer(Columns[nIndex].Width));

      end;

     end;


    begin

     for nIndex := 0 to Columns.Count - 1 do begin

      if (Columns[nIndex].Width = 0)  then begin

       if (nIndex + 1 <= FreezeCols) or (nIndex >= SelectedIndex + ADelta) then

        Columns[nIndex].Width := ReadWidth;

      end else begin

       SaveWidth;

       if (nIndex + 1 > FreezeCols) and (nIndex < SelectedIndex + ADelta) and

        (nIndex + 1 < Columns.Count) and (FreezeCols > 0) then

        Columns[nIndex].Width := 0;

      end;

     end;

    end;

    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. Таким образом можно получить доступ к приватным полям, свойствам и методам класса, что, к сожалению, приходится делать, если авторы исходного класса оказались не предусмотрительны. 

    Dbgrid с цветными ячейками V

    Delphi 1 

    Попробуйте следующий код в обработчике события 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. 

    Что я получаю от наличия ConstraintBroker (брокера ограничений)?

    Nomadic отвечает:

    ConstraintBroker позволяет Вам включать проверки на ограничения в данные.

    Это означает, что когда Вы запрашиваете данные, Вы получаете вместе с ними и правила, которым они дорлжны удовлетворять. Эти правила автоматически без дополнительного кода входят в силу.

    Поскольку это происходит без единой строчки кода, то Вам не требуется переписывать или обновлять приложение каждый раз при изменении правил.

    Фактически это простое решение задачи обновления клиентского приложения без выхода из него.

    Каждое приложение, использующее ConstraintBroker, автоматически получает это качество…

    Улучшенный Dbgrid

    Delphi 1

    {

    Код улучшенного TDBGrid, имеющего свойства Col, Row и Canvas и метод CellRect. Это чрезвычайно полезно в случае, если вы, к примеру, хотите получить выпадающий список на месте редактируемой пользователем ячейки.

    }

    unit VUBComps;


    interface


    uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,Forms, Dialogs, Grids, DBGrids, DB, Menus;


    type TDBGridVUB = class(TDBGrid)

    private

      { Private declarations }

    protected

     { Protected declarations }

    public

     property Canvas;

     function CellRect(ACol, ARow: Longint): TRect;

     property Col;

     property Row;


    procedure Register;


    implementation


    procedure Register;

    begin

     RegisterComponents('VUBudget', [TDBGridVUB]);

    end;


    function TDBGridVUB.CellRect(ACol, ARow: Longint): TRect;

    begin

     Result := inherited CellRect(ACol, ARow);

    end;

    end.

    Пример Drag and Drop между двумя Dbgrid

    Delphi 3

    Данный пример компонента и демонстрационный проект показывают простой путь осуществления операции "drag and drop" (перетащи и брось) между двумя полями различных табличных сеток.

    Запустите Delphi 3 (с незначительными изменениями данный код может работать и в Delphi 1-2).


    Активизируйте File|New|Unit. Скопируйте приведенный ниже модуль MyDBGrid во вновь созданный модуль. Сделайте File|Save As. Сохраните модуль как MyDBGrid.pas.


    Выберите пункт меню Component|Install Component. Переключитесь на страницу Info New Package. Поместите MyDBGrid.pas в поле редактирования "Unit file name" (имя файла модуля). Назовите модуль MyPackage.dpk. Ответьте Yes на вопрос Delphi 3 о необходимости сборки и установки пакета. Нажмите OK на сообщение Delphi 3 о необходимости включения VCL30.DPL. После этого пакет будет собран и установлен. Теперь компонент TMyDBGrid будет отображен в Палитре Компонентов в группе "Samples". Закройте редактор пакетов и сохраните пакет.


    Выберите пункт меню File|New Application. Щелкните правой кнопкой мыши на форме (Form1) и выберите View As Text. Скопируйте приведенный ниже исходный код формы GridU1 в Form1. Щелкните правой кнопкой мыши на форме и выберите View As Form. Убедитесь в активности ваших таблиц. Скопируйте расположенный ниже модуль GridU1 в ваш модуль Unit1.


    Выберите пункт меню File|Save Project As. Сохраните модуль как GridU1.pas. Сохраните проект как GridProj.dpr.


    Теперь запустите проект и наслаждайтесь функцией Drag and Drop между двумя табличными сетками.

    Модуль MyDBGrid

    unit MyDBGrid;


    interface


    uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Grids, DBGrids;


    type TMyDBGrid = class(TDBGrid)

    private

     { Private declarations }

     FOnMouseDown: TMouseEvent;

    protected

     { Protected declarations }

     procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;

    published

     { Published declarations }

     property Row;

     property OnMouseDown read FOnMouseDown write FOnMouseDown;

    end;


    procedure Register;


    implementation


    procedure TMyDBGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

    begin

     if Assigned(FOnMouseDown) then FOnMouseDown(Self, Button, Shift, X, Y);

     inherited MouseDown(Button, Shift, X, Y);

    end;


    procedure Register;

    begin

     RegisterComponents('Samples', [TMyDBGrid]);

    end;

    end.

    Модуль GridU1

    unit GridU1;


    interface


    uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Db, DBTables, Grids, DBGrids, MyDBGrid, StdCtrls;


    type TForm1 = class(TForm)

     MyDBGrid1: TMyDBGrid;

     Table1: TTable;

     DataSource1: TDataSource;

     Table2: TTable;

     DataSource2: TDataSource;

     MyDBGrid2: TMyDBGrid;

     procedure MyDBGrid1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

     procedure MyDBGrid1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);

     procedure MyDBGrid1DragDrop(Sender, Source: TObject; X, Y: Integer);

    private

     { Private declarations }

    public

     { Public declarations }

    end;


    var Form1: TForm1;


    implementation


    {$R *.DFM}


    var SGC : TGridCoord;


    procedure TForm1.MyDBGrid1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

    var DG : TMyDBGrid;

    begin

     DG := Sender as TMyDBGrid;

     SGC := DG.MouseCoord(X,Y);

     if (SGC.X > 0) and (SGC.Y > 0) then (Sender as TMyDBGrid).BeginDrag(False);

    end;


    procedure TForm1.MyDBGrid1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);

    var GC : TGridCoord;

    begin

     GC := (Sender as TMyDBGrid).MouseCoord(X,Y);

     Accept := Source is TMyDBGrid and (GC.X > 0) and (GC.Y > 0);

    end;


    procedure TForm1.MyDBGrid1DragDrop(Sender, Source: TObject; X, Y: Integer);

    var

     DG     : TMyDBGrid;

     GC     : TGridCoord;

     CurRow : Integer;

    begin

     DG := Sender as TMyDBGrid;

     GC := DG.MouseCoord(X,Y);

     with DG.DataSource.DataSet do begin

      with (Source as TMyDBGrid).DataSource.DataSet do

       Caption := 'Вы перетащили «'+Fields[SGC.X-1].AsString+'"';

      DisableControls;

      CurRow := DG.Row;

      MoveBy(GC.Y-CurRow);

      Caption := Caption+' в «'+Fields[GC.X-1].AsString+'"';

      MoveBy(CurRow-GC.Y);

      EnableControls;

     end;

    end;

    end.

    Форма GridU1

    object Form1: TForm1

     Left = 200

     Top = 108

     Width = 544

     Height = 437

     Caption = 'Form1'

     Font.Charset = DEFAULT_CHARSET

     Font.Color = clWindowText

     Font.Height = -11

     Font.Name = 'MS Sans Serif'

     Font.Style = []

     PixelsPerInch = 96

     TextHeight = 13

     object MyDBGrid1: TMyDBGrid

      Left = 8

      Top = 8

      Width = 521

      Height = 193

      DataSource = DataSource1

      Row = 1

      TabOrder = 0

      TitleFont.Charset = DEFAULT_CHARSET

      TitleFont.Color = clWindowTextTitle

      Font.Height = -11

      TitleFont.Name = 'MS Sans Serif'

      TitleFont.Style = []

      OnDragDrop = MyDBGrid1DragDrop

      OnDragOver = MyDBGrid1DragOver

      OnMouseDown = MyDBGrid1MouseDown

     end

     object MyDBGrid2: TMyDBGrid

      Left = 7

      Top = 208

      Width = 521

      Height = 193

      DataSource = DataSource2

      Row = 1

      TabOrder = 1

      TitleFont.Charset = DEFAULT_CHARSET

      TitleFont.Color = clWindowText

      TitleFont.Height = -11

      TitleFont.Name = 'MS Sans Serif'

      TitleFont.Style = []

      OnDragDrop = MyDBGrid1DragDrop

      OnDragOver = MyDBGrid1DragOver

      OnMouseDown = MyDBGrid1MouseDown

     end

     object Table1: TTableActive = True

      DatabaseName = 'DBDEMOS'

      TableName = 'ORDERS'

      Left = 104

      Top = 48

     end

     object DataSource1: TDataSource

      DataSet = Table1

      Left = 136

      Top = 48

     end

     object Table2: TTable

      Active = True

      DatabaseName = 'DBDEMOS'

      TableName = 'CUSTOMER'

      Left = 104

      Top = 240

     end

     object DataSource2: TDataSource

      DataSet = Table2

      Left = 136

      Top = 240

     end

    end

    Как заставить DBGrid сортировать данные по щелчку на заголовке столбца?

    Nomadic советует:

    Кyсочек кода, чтобы повесить на clickable столбец RxGrid, показывающий RxQuery с определенным макросом %Order. Работать не бyдет (без модyлей), но в качестве идеи может быть полезен.

    unit vgRXutil;


    interface


    uses SysUtils, Classes, DB, DBTables, rxLookup, RxQuery;


    { TrxDBLookup }

    procedure RefreshRXLookup(Lookup: TrxLookupControl);

    procedure RefreshRXLookupLookupSource(Lookup: TrxLookupControl);


    function RxLookupValueInteger(Lookup: TrxLookupControl): Integer;


    { TRxQuery }


    { Applicatable to SQL's without SELECT * syntax }


    { Inserts FieldName into first position in '%Order' macro and refreshes query }

    procedure HandleOrderMacro(Query: TRxQuery; Field: TField);


    { Sets '%Order' macro, if defined, and refreshes query }

    procedure InsertOrderBy(Query: TRxQuery; NewOrder: String);


    { Converts list of order fields if defined and refreshes query }

    procedure UpdateOrderFields(Query: TQuery; OrderFields: TStrings);


    implementation

    uses vgUtils, vgDBUtl, vgBDEUtl;


    { TrxDBLookup refresh }


    type TRXLookupControlHack = class(TrxLookupControl)

     property DataSource;

     property LookupSource;

     property Value;

     property EmptyValue;

    end;


    procedure RefreshRXLookup(Lookup: TrxLookupControl);

    var SaveField: String;

    begin

     with TRXLookupControlHack(Lookup) do begin

      SaveField := DataField;

      DataField := '';

      DataField := SaveField;

     end;

    end;


    procedure RefreshRXLookupLookupSource(Lookup: TrxLookupControl);

    var SaveField: String;

    begin

     with TRXLookupControlHack(Lookup) do begin

      SaveField := LookupDisplay;

      LookupDisplay := '';

      LookupDisplay := SaveField;

     end;

    end;


    function RxLookupValueInteger(Lookup: TrxLookupControl): Integer;

    begin

     with TRXLookupControlHack(Lookup) do try

      if Value <> EmptyValue then Result := StrToInt(Value)

      else Result := 0;

     except

      Result := 0;

     end;

    end;


    procedure InsertOrderBy(Query: TRxQuery; NewOrder: String);

    var

     Param: TParam;

     OldActive: Boolean;

     OldOrder: String;

     Bmk: TPKBookMark;

    begin

     Param := FindParam(Query.Macros, 'Order');

     if not Assigned(Param) then Exit;

     OldOrder := Param.AsString;

     if OldOrder <> NewOrder then begin

      OldActive := Query.Active;

      if OldActive then Bmk := GetPKBookmark(Query, '');

      try

       Query.Close;

       Param.AsString := NewOrder;

       try

        Query.Prepare;

       except

        Param.AsString := OldOrder;

       end;

       Query.Active := OldActive;

       if OldActive then SetToPKBookMark(Query, Bmk);

      finally

       if OldActive then FreePKBookmark(Bmk);

      end;

     end;

    end;


    procedure UpdateOrderFields(Query: TQuery; OrderFields: TStrings);

    var NewOrderFields: TStrings;


     procedure AddOrderField(S: String);

     begin

      if NewOrderFields.IndexOf(S) < 0 then NewOrderFields.Add(S);

     end;


    var

     I, J: Integer;

     Field: TField;

     FieldDef: TFieldDef;

     S: String;

    begin

     NewOrderFields := TStringList.Create;

     with Query do try

      for I := 0 to OrderFields.Count - 1 do begin

       S := OrderFields[I];

       Field := FindField(S);

       if Assigned(Field) and (Field.FieldNo > 0) then AddOrderField(IntToStr(Field.FieldNo))

       else try

        J := StrToInt(S);

        if J < FieldDefs.Count then AddOrderField(IntToStr(J));

       except

       end;

      end;

      OrderFields.Assign(NewOrderFields);

     finally

      NewOrderFields.Free;

     end;

    end;


    procedure HandleOrderMacro(Query: TRxQuery; Field: TField);

    var

     Param: TParam;

     Tmp, OldOrder, NewOrder: String;

     I: Integer;

     C: Char;

     TmpField: TField;

     OrderFields: TStrings;

    begin

     Param := FindParam(Query.Macros, 'Order');

     if not Assigned(Param) or Field.Calculated or Field.Lookup then Exit;

     OldOrder := Param.AsString;

     I := 0;

     Tmp := '';

     OrderFields := TStringList.Create;

     try

      OrderFields.Ad(Field.FieldName);

      while I < Length(OldOrder) do begin

       Inc(I);

       C := OldOrder[I];

       if C in FieldNameChars then Tmp := Tmp + C;

       if (not (C in FieldNameChars) or (I = Length(OldOrder))) and (Tmp <> '') then begin

        TmpField := Field.DataSet.FindField(Tmp);

        if OrderFields.IndexOf(Tmp) < 0 then OrderFields.Add(Tmp);

        Tmp := '';

       end;

      end;

      UpdateOrderFields(Query, OrderFields);

      NewOrder := OrderFields[0];

      for I := 1 to OrderFields.Count – 1 do NewOrder := NewOrder + ', ' + OrderFields[1];

     finally

      OrderFields.Free;

     end;

     InsertOrderBy(Query, NewOrder);

    end;

    end.
     

    DBGrid и TQuery

    Delphi 1 

    1. Расположите на вашей форме 2 TQuerie с двумя соответствующими TDatasource (Query1 будет вашим Мастером, Query2 будет вашей Деталью)

    2. Разместите 2 TDBGrid, связанных с Datasource'ами (вероятно, вы уже это сделали)

    3. Используйте базу данных, поставляемую с Delphi:

    Query1.SQL := 'Select * from customer'

    Query2.SQL := 'Select * from Orders whereOrders."CustNo" = :CustNo'

    (это можно сделать как во время выполнения приложения, так и во время его разработки)

    4. В свойствах Query2 выберите свойство Params и напишите в строке 'CustNo'. 'CustNo' был определен как параметр, поскольку в SQL строке было использовано ':'.

    5. ОЧЕНЬ ВАЖНО: установите Query2.Datasource в набор данных, связанный с Query1.

    Каждый раз при изменении записи в наборе данных Query1, Query2 будет обновляться. Имя параметра 'CustNo' соответствует имени реального поля в таблице Customer.

    P.S.: Для получения дополнительной информации обратитесь к разделу электронной справки 'dynamic SQL' 

    DBGrid как навигатор

    Delphi 1 

    1. Расположите компонент table на пустой форме и свяжите его с вашего таблицей Client.

    2. Добавьте компонент Datasource и свяжите его с компонентом table, описанным выше.

    3. Добавьте компонент grid и свяжите его с компонентом datasource, описанным выше.

    4. Используя Редактор Полей (Fields Editor), создайте компоненты TField для всех полей таблицы client.

    5. Установите свойство Visible всех компонентов TField, кроме Client Name (или другого поля, которое будет отображаться в DBGrid), в False. Grid теперь будет отображать только Client Name.

    6. Для отображения полей таблицы Client (которые вы хотите показать, или которые вы хотите сделать доступными для редактирования пользователем), ниже табличной сетки расположите компоненты DBEdit. Они могут использовать тот же набор данных, что и DBGrid.

    Теперь пользователь может воспользоваться DBGrid для навигации и ввода/редактирования данных посредством DBEdit'ов. 

    Позиция DBGrid

    Delphi 1 

    В режиме разработки дважды щелкните на компоненте TQuery, и выберите все поля, которые вы хотите использовать в DBGrid. Затем в обработчике события DBGrid doubleclick смотрите значение DBGrid.SelectedIndex. Если оно  < 0, выбранных пунктов нет. Также, текущая запись TQuery будет указывать на ту же самую строку, которая выбранна в DBGrid. Таким образом, вы можете использовать что-то типа requiredvalue := Query1Field1.AsString; и т.д., естественно, компоненты TQuery и DBGrid должны быть подключены друг к другу. 

    DBGrid – переход к следующей записи

    Delphi 1 

    Для перехода к следующей записи:

    MyDBGrid.SelectedIndex := MyDBGrid.SelectedIndex + 1;

    Колонки DBGrid индексируются с 0, поэтому SelectedIndex := 0 даст вам первую колонку. Свойство FieldCount вернет вам количество колонок, так что вы без труда можете пробежаться по всей матрице данных.

    onClick и DBGrid

    Многие программисты хотели бы использовать OnClick у TDBGrid. Но TDBGrid не имеет такого события. В данном документе рассказывается о том, как обеспечить поддержку события OnClick для TDBGrid. Рассказанная здесь технология может пригодиться при добавлении других свойств к различным объектам. Если вы знаете, что сделать это мог предок, то можно заставить сделать это и наследника. Ключевым моментом здесь можно считать добавление csClickEvents к свойству-набору элемента управления ControlStyle. Это позволит элементу управления, приведенному к типу THack, получать и правильно обрабатывать системные сообщение о щелчке мышью. Назначение OnClick какого-либо элемента управления OnClick DBGrid1 позволяет воспользоваться событием OnClick для элемента управления, которое его не поддерживает.

    Это "неофициальный" путь. Существует несколько причин того, почему dbgrid не поддерживает этого события. Используйте этот код на свой страх и риск.


    unit Udbgclk;


    interface


    uses Windows, Messages, SysUtils, Classes, Graphics,Controls, Forms, Dialogs, StdCtrls, Grids, DBGrids, DBTables, DB;


    type

     thack = class(tcontrol);


     TForm1 = class(TForm)

      DBGrid1: TDBGrid;

      Button1: TButton;

      DataSource1: TDataSource;

      Table1: TTable;

      procedure Button1Click(Sender: TObject);

      procedure FormClick(Sender: TObject);

     private

      { Private declarations }

     public

      { Public declarations }

     end;


    var Form1: TForm1;


    implementation


    {$R *.DFM}


    procedure TForm1.Button1Click(Sender: TObject);

    begin

     THack(dbgrid1).controlstyle :=THack(dbgrid1).controlstyle + [csClickEvents];

     THack(dbgrid1).OnClick := Form1.OnClick;

    end;


    procedure TForm1.FormClick(Sender: TObject);

    begin

     messagebeep(0);

     application.processmessages;

    end;

    end.
     

    Числа с плавающей точкой в DBGrid

    Delphi 1 

    Для показа в табличной сетке дробных чисел, выберите таблицу, с которой связана ваша сетка (через datasource, источник данных).

    Активизируйте редактор полей (правой кнопкой мыши) и выберите поле, в котором вы хотите видеть дробное число.

    Измените значение свойств 'DisplayFormat' и 'EditFormat', чтобы дробь имела формат такой, какой вы хотите (к примеру, шаблон '0.00', позволяющий сетке показывать поле с двумя цифрами после запятой).

    Дважды щелкните на компоненте table, расположенном на форме. Нажмите на кнопку 'Add'. Будут показаны все поля вашей таблицы. Выберите их в списке «Available field» (доступные поля) и щелкните на кнопке OK. Теперь при щелчке на имени поля, в Инспекторе Объектов будут показаны все свойства, относящиеся к данному полю, здесь можно изменить текст заголовка, выводимый формат «DisplayFormat» (это как раз то, что вам нужно, измените его на ####0.0) и пр. 

    Получение данных DBGrid прежде, чем они будут отправлены: как мне узнать, что пользователь вводит в DBGrid?

    Delphi 3 

    Вы можете «видеть» что набирается в TDBGrid, «смотря» на контрол сетки TInPlaceEdit. Вы должны убедиться только в том, что к моменту использования TInPlaceEdit, контрол уже создан. Следующая функция покажет данные, редактируемые в колонках сетки:

    procedure TForm1.DBGrid1KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);

    var B: byte;

    begin

     for B := 0 to DBGrid1.ControlCount - 1 do

      if DBGrid1.Controls[B] is TInPlaceEdit then begin

       with DBGrid1.Controls[B] as TInPlaceEdit do begin

        Label1.Caption := 'Текст = ' + Text;

       end;

      end;

    end;

    Хочу шапку в TDBGrid. Как сделать?

    Nomadic советует:

    Уже реализовано в виде вот этого компонента — © Andre

    unit bdbgrid;

    interface

    uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Grids, DBGrids, Math;

    type

     TOnDrawTitleEvent = procedure(ACol : integer; ARect : TRect; var TitleText : string) of object;


     TBitDBGrid = class(TDBGrid)

     private

      FBitmapBrowse : TBitmap;

      FBitmapEdit : TBitmap;

      FBitmapInsert : TBitmap;

      FBitmapFill : TBitmap;

      FRealTitleFont : TFont;

      FOnDrawTitle : TOnDrawTitleEvent;

      FResizeFlag : boolean;

      { Private declarations }

      procedure SetRealTitleFont(Value : TFont);

      procedure UpdateTitlesHeight;

     protected

      procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState); override;

      procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;

      procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;

      { Protected declarations }

     public

      constructor Create(AOwner : TComponent);override;

      destructor Destroy; override;

      { Public declarations }

     published

      property OnDrawTitle : TOnDrawTitleEvent read FOnDrawTitle write FOnDrawTitle;

      property RealTitleFont : TFont read FRealTitleFont write SetRealTitleFont;

      { Published declarations }

     end;


    procedure Register;


    implementation


    var DrawBitmap : TBitmap;


    function Max(X, Y: Integer): Integer;

    begin

     Result := Y;

     if X > Y then Result := X;

    end;


    procedure WriteText(ACanvas: TCanvas; ARect: TRect; DX, DY: Integer; const Text: string; Alignment: TAlignment);

    // © Borland function :)

    const AlignFlags : array [TAlignment] of Integer =

     ( DT_LEFT or DT_WORDBREAK or DT_EXPANDTABS or DT_NOPREFIX,

     DT_RIGHT or DT_WORDBREAK or DT_EXPANDTABS or DT_NOPREFIX,

     DT_CENTER or DT_WORDBREAK or DT_EXPANDTABS or DT_NOPREFIX );

    var

     B, R: TRect;

     I, Left: Integer;

    begin

     with DrawBitmap, ARect do { Use offscreen bitmap to eliminate flicker and }

     begin { brush origin tics in painting / scrolling. }

      Width := Max(Width, Right - Left);

      Height := Max(Height, Bottom - Top);

      R := Rect(DX, DY, Right - Left - 1, Bottom - Top - 1);

      B := Rect(0, 0, Right - Left, Bottom - Top);

     end;

     with DrawBitmap.Canvas do begin

      DrawBitmap.Canvas.CopyRect(B, ACanvas, ARect);

      Font := ACanvas.Font;

      Font.Color := ACanvas.Font.Color;

      Brush := ACanvas.Brush;

      SetBkMode(Handle, TRANSPARENT);

      DrawText(Handle, PChar(Text), Length(Text), R, AlignFlags[Alignment]);

     end;

     ACanvas.CopyRect(ARect, DrawBitmap.Canvas, B);

    end;


    constructor TBitDBGrid.Create(AOwner : TComponent);

    begin

     inherited Create(Aowner);

     FRealTitleFont := TFont.Create;

     FResizeFlag := false;

    end;


    destructor TBitDBGrid.Destroy;

    begin

     FRealTitleFont.Free;

     inherited Destroy;

    end;


    procedure TBitDBGrid.UpdateTitlesHeight;

    var

     Loop : integer;

     MaxTextHeight : integer;

     RRect : TRect;

    begin

     MaxTextHeight := 0;

     for loop := 0 to Columns.Count - 1 do begin

      RRect := CellRect(0, 0);

      RRect.Right := Columns[Loop].Width;

      RRect.Left := 0;

      Canvas.Font := RealTitleFont;

      MaxTextHeight := Max(MaxTextHeight, DrawText(Canvas.Handle, PChar(Columns[Loop].Title.Caption), Length(Columns[Loop].Title.Caption), RRect, DT_CALCRECT + DT_WORDBREAK));

     end;

     if TitleFont.Height <> - MaxTextHeight then TitleFont.Height := - MaxTextHeight;

    end;


    procedure TBitDBGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

    begin

     if MouseCoord(X, Y).Y = 0 then FResizeFlag := true;

     inherited MouseDown(Button, Shift, X, Y);

    end;


    procedure TBitDBGrid.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

    begin

     inherited MouseUp(Button, Shift, X, Y);

     if FResizeFlag then begin

      FResizeFlag := false;

      UpdateTitlesHeight;

     end;

    end;


    procedure TBitDBGrid.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);

    var

     Indicator : TBitmap;

     TitleText : string;

     Al : TAlignment;

    begin

     if not ((gdFixed in AState) and ((ARow = 0) and (dgTitles in Options) and (ACol <> 0))) then

      inherited DrawCell(ACol, ARow, ARect, AState)

     else begin

      if DefaultDrawing then begin

       DrawEdge(Canvas.Handle, ARect, BDR_RAISEDINNER, BF_BOTTOMLEFT);

       DrawEdge(Canvas.Handle, ARect, BDR_RAISEDINNER, BF_TOPRIGHT);

       InflateRect(ARect, -1, -1);

       Canvas.Brush.Color := FixedColor;

       Canvas.FillRect(ARect);

      end;

      TitleText := Columns[ACol - 1].Title.Caption;

      if Assigned(OnDrawTitle) then OnDrawTitle(ACol, ARect, TitleText);

      if DefaultDrawing and (TitleText <> '') then begin

       Canvas.Brush.Style := bsClear;

       Canvas.Font := RealTitleFont;

       if ACol > 0 then Al := Columns[ACol - 1].Title.Alignment

       else Al := Columns[0].Title.DefaultAlignment;

       WriteText(Canvas, ARect, 2, 2, TitleText, Al);

      end;

     end;

    end;


    procedure TBitDBGrid.SetRealTitleFont(Value : TFont);

    begin

     FRealTitleFont.Assign(Value);

     Repaint;

    end;


    procedure Register;

    begin

     RegisterComponents('Andre VCL', [TBitDBGrid]);

    end;


    initialization

     DrawBitmap := TBitmap.Create;

    finalization

     DrawBitmap.Free;

    end.
     

    Несколько таблиц в одном TDBGrid

    Delphi 1 

    Насколько я знаю, единственное легкое решение заключается в использовании вычисляемых полей.

    Для того, чтобы поместить данные из нескольких таблиц в один DBGrid, нужно воспользоваться объектом TQuery. На заметку: используйте TQuery в режиме только для чтения, если вы не можете обеспечить гарантию выполнения некоторых из его руководящих принципов, один из которых – данные могут быть получены только от одной таблицы.

    Как сделать так, чтобы в DBGrid напротив некоторых строк можно было бы галочку поставить?

    Nomadic советует:

    Ну примерно так (лишнее мало-мало порезал, больно много его, но идея видна :) на сервере — тaблицa Advertis.DB, первичный ключ ID — autoincrement. На локальном диске — тaблицa Founds.DB, с полем Advertis: integer, по которому есть индекс, и tblFounds.IndexFieldNames = 'Advertis'.

    На гриде:

    === cut ===

    procedure TMainForm.dbgWorkDblClick(Sender: TObject);

    begin

     TriggerRowSelection;

    end;


    procedure TMainForm.TriggerRowSelection;

    begin

     if dmFile.AdvertisCount <> 0 then begin

      with dmFile do if not tblFounds.FindKey([tblAdvertisID.Value]) then begin

       tblFounds.AppendRecord([tblAdvertisID.Value]);

      end else begin

       tblFounds.Delete;

      end;

      dbgWork.Refresh;

     end;

    end;


    procedure TMainForm.dbgWorkDrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState);

    begin

     if DataCol = 0 then with dmFile, dbgWork.Canvas do begin

      FillRect(Rect); {clear the cell}

      if tblFounds.FindKey([tblAdvertisID.Value]) then begin

       TextOut(Rect.Left, Rect.Top, '?');

      end else begin

       TextOut(Rect.Left, Rect.Top, 'o');

      end;

     end;

    end;

    === cut ===

    Оказывается, я переопределял рисование гридa, а не вычислял поле. Не помню точно, но кажется, чтобы не перечитывать таблицу на каждый даблклик, а только перерисовать грид.

    А колонка для галки в гриде определялась так:

    === cut ===

    with dmFile, dbgWork.Columns do begin

     BeginUpdate;

     Clear;

     {check mark}

     nc := Add;

     nc.Width := 14;

     nc.Font.Name := 'Wingdings';

     nc.Font.Size := 11;

     nc.Alignment := taRightJustify;

     nc.Title.Caption := 'y';

     nc.Title.Font.Name := 'Wingdings';

     nc.Title.Font.Size := 10;

     nc.Title.Alignment := taCenter;

     [skip определения остaльных колонок]

     EndUpdate;

    end;

    === cut ===

    Вроде всё.

    Ну, как напечатать/обработать только помеченное, сам разберёшься. У меня там накручено чего-то с фильтрами, думаю, можно проще.

    Что касается других способов – можно вместо временной тaблицы попользовать список, массив или in-memory table. 

    Как в TDBGrid разрешить только операции UPDATE записей и запретить INSERT/DELETE?

    Nomadic советует:

    А я делаю так.

    На DataSource, к которому прицеплен Grid, вешаю обработчик на событие OnStateChange.

    Ниже текст типичного обработчика –

    if DBGrid1.DataSource.DataSet.State in [dsEdit, dsInsert] then

    DBGrid1.Options := DBGrid1.Options + goRowSelect

    else DBGrid1.Options := DBGrid1.Options – goRowSelect;

    Дело в том, что если у Grid'а стоит опция goRowSelect, то из Grid'а невозможно добавить запись. Ну а когда програмно вызываешь редактирование или вставку, то курсор принимает обычный вид и все Ok.

    Лучше использовать конструкцию «State in dsEditModes». 

    Обновление TDBGrid после редактирования отдельной записи на отдельной форме

    Delphi 1 

    А вы постите запись, прежде чем закрыть форму? При закрытии, форма самостоятельно данных не постит. Вы должны постить изменения или с помощью компонента dbnavigator, или c помощью кода, который при закрытии формы постит данные в основную таблицу.

    На странице 95 Database Application Developers Guide (руководство разработчиков приложений баз данных), поставляемое с Delphi, приведен демонстрационный проект с двумя формами, демонстрирующий хорошую технику при использовании ttable на мастер-форме в качестве набора данных для детали.

    Одним из решений вашей проблемы может служить связывание компонента DataSource на Form2 с набором данных DataSet на Form1. Это может быть достигнуто путем добавления следующей строки в обработчик события OnActivate для Form2:

    MyDataSource.DataSet := Form1.MyTable;

    Данный метод имеет 3 преимущества:

    1. сделанные вами изменения немедленно отображаются, поскольку вы используете одну и ту же логическую таблицу;

    2. если вам нужно определить установки для ваших полей таблицы, например, DisplayFormat или EditMask, вам нужно сделать это только один раз в таблице на Form1, вам не нужно это делать на каждой форме, которая использует таблицу;

    3. это сохраняет ресурсы и повышает производительность, поскольку ваше приложение при работе с таблицей использует только одну сессию. Тем не менее, в проектном времени вам нужно иметь TTable на вашей Form2 для того, чтобы вы могли выбрать поля для БД-контролов, после чего вы можете удалить TTable. 

    Пересортица в коде полей TDBGrid во время вополнения программы

    Одной строкой 

    используйте <имя поля>.index := <желаемый номер поля>

    В Delphi 3 и выше ползунок TDBGrid иногда может находится не только в трех фиксированных позициях. Что для этого нужно?


    Nomadic отвечает:

    Здесь отрывки из исходников VCL —

    unit DBGrids;


    procedure TCustomDBGrid.UpdateScrollBar;

    var

     SIOld, SINew: TScrollInfo;

    begin

     [skipped]

     if IsSequenced then begin

      SINew.nMin := 1;

      SINew.nPage := Self.VisibleRowCount;

      SINew.nMax := RecordCount + SINew.nPage -1;

      if State in [dsInactive, dsBrowse, dsEdit] then SINew.nPos := RecNo; // else keep old pos

     end else begin

      SINew.nMin := 0;

      SINew.nPage := 0;

      SINew.nMax := 4;

      if BOF then SINew.nPos := 0

      else if EOF then SINew.nPos := 4

      else SINew.nPos := 2;

     end;

     [skipped]


    unit dbtables;


    function TBDEDataSet.IsSequenced: Boolean;

    begin

     Result := (FRecNoStatus = rnParadox) and (not Filtered);

    end;

    То есть, к примеру, все будет работать «красиво» на таблицах BDE, если они:

    • таблицы Paradox;

    • на них не установлен фильтр.

    TClientDataSet в режиме single-tier (briefcase) также работает «красиво». 

    Изменение месторасположение колонок в TDBGrid

    Delphi 1

    Var

     i: Integer;

     fName: string;

     …………

     { Определение изменения месторасположения колонок }

     …………

     with dbgrid1.datasource.dataset as ttable do

      for i := 0 to indexdefs.count – 1 do begin

      fName := DBGrid1.Fields[0].FieldName;

      if copy(indexdefs[i].fields, 1, length(fname)) = fname then IndexName := IndexDefs[i].Name

     end;

    Решение проблемы передачи фокуса TDBGrid

    В данном документе содержится решение проблемы невозможности получения DBGrid-ом фокуса после щелчка на каком-либо элементе управления родительской формы, в то время, как DBGrid находится на ее дочерней MDI-форме.

    Относится ко всем версиям Delphi.

    Очевидно, DBGrid имеет некоторые проблемы с управлением фокусом, если он находится на дочерней MDI-форме. Эта проблема решена в приведенном ниже наследнике TDBGrid, в котором обрабатываются мышиные сообщения и выясняется когда фокус должен быть передан сетке. Наследник создан в виде компонента, который легко устанавливается в Палитру Компонентов. Примечание: код адаптирован для всех версий Delphi. Проблемы могут быть в Delphi 2 и 3, если вы забудете заменить устаревшие в этих версиях модули "winprocs" и "wintypes" на "windows."

    unit FixedDBGrid;


    interface


    uses Winprocs,wintypes, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Grids, DBGrids;


    type TFixedDBGrid = class(TDBGrid)

    private

     { Private declarations }

    protected

     { Protected declarations }

    public

     { Public declarations }

     procedure WMRButtonDown(var Message: TWMRButtonDown); message WM_RBUTTONDOWN;

     procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;

    published

     { Published declarations }

    end;


    procedure Register;


    implementation


    procedure TFixedDBGrid.WMRButtonDown(var Message: TWMRButtonDown);

    begin

     winprocs.SetFocus(handle); {помните, что winprocs относится только к Delphi 1!}

     inherited;

    end;


    procedure TFixedDBGrid.WMLButtonDown(var Message: TWMLButtonDown);

    begin

     winprocs.SetFocus(handle); {помните, что winprocs относится только к Delphi 1!}

     inherited;

    end;


    procedure tfixeddbgrid.wmlbuttondown(var Message: twmlbuttondown);

    begin

     winprocs.SetFocus(handle); {помните, что winprocs относится только к Delphi 1!}

     inherited;

    end;


    procedure Register;

    begin

     RegisterComponents('Samples', [TFixedDBGrid]);

    end;

    end.
     

    Как отучить TDBGrid от автодобавления новой записи?

    Добавьте в обработчик события вашего TTable «BeforeInsert» следующую строку:

    procedure TForm1.Tbable1BeforeInsert(DataSet: TDataset);

    begin

     Abort;  ←эту строчку

    end;

    Осуществляем перехват нажатия клавиши и проверку на конец файла (end-of-file):

    procedure TForm8.DBGrid1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);

    begin

     if (Key = VK_DOWN) then begin

      TTable1.DisableControls;

      TTable1Next;

      if TTable1.EOF then Key := 0

      else TTable1.Prior;

      TTable1.EnableControls;

     end;

    end;
     

    Две таблицы в одном TDBGrid

    Delphi 2 

    Если у вас D2, вы можете воспользоваться свойством Lookup. Для этого выберите в контекстном меню объекта table редактор полей (fields editor). Затем для добавления нового поля нажмите <Ctrl>+N. Просто раскройте combobox и выберите lookup-поле. TDBGrid автоматически создаст выпадающий список, в котором пользователь сможет выбрать нужный элемент. 

    Добавление к TDBGrid события OnClick

    Delphi 1

    TGroothuisGrid = class() {!}

    published

     property OnClick;

    end;

    Это все! OnClick уже объявлен в TControl как защищенное свойство. Все, что вы должны сделать, это опубликовать это свойство в компоненте-наследнике, зарегистрировать его (смотри гл. 8 Руководства по созданию компонентов, Component Writer's Guide) и использовать взамен TDBGrid.

    Позиция ячейки в TDBGrid

    Delphi 1

    В TCustomGrid определен метод CellRect, который, к сожалению, защищен. Это означает, что даный метод доступен только для TCustomGrid и его наследников. Но все-таки существует немного мудреное решение вызова данного метода:

    type TMyDBGrid = class(TDBGrid)

    public

     function CellRect(ACol, ARow: Longint): TRect;

    end;


    function TMyDBGrid.CellRect(ACol, ARow: Longint): TRect;

    begin

     Result := inherited CellRect(ACol, ARow);

    end;

    Вы можете сделать приведение типа вашего DBGrid к TMyDBGrid (это возможно, поскольку CellRect статический метод) и вызвать CellRect:

    Rectangle := TMyDBGrid(SomeDBGrid).CellRect(SomeColumn, SomeRow);

    procedure TfmLoadIn.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn;State: TGridDrawState);

    const Disp = 2;        //Правильно выравниваем компонент

    begin

     inherited;

     if (gdFocused in State) then begin

      if (Column.FieldName = 'TYPEDescription') then begin

       dlTYPEDescription.Left := Rect.Left + DBGrid1.Left + Disp;

       dlTYPEDescription.Top := Rect.Top + DBGrid1.top + Disp;

       dlTYPEDescription.Width := Rect.Right – Rect.Left;

       dlTYPEDescription.Height := Rect.Bottom – Rect.Top;

       dlTYPEDescription.Visible := True;

      end;

     end;

    end;

    Dbgrid с цветными ячейками VI

    Delphi 1

    Установите 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;
     

    Показ Memo-поля в Dbgrid

    Delphi 1 

    …я все же лелею надежду, что когда-нибудь увижу TMemoField.DataSize, имеющим значение, отличное от нуля. Может быть значение DataSize является размером части Memo, которая сохранилась в .db-файле? Вместо этого я теперь пользуюсь объектом TBlobStream, который вполне хорошо справляется с этой работой. Все это у меня происходит примерно так:

    Var

     pBuffer: PChar;

     Blob: TBlobStream;

    begin

     {FDataField – это TMemoField}

     Blob := TBlobStream.Create(FDataField, bmRead);

     try

      if Blob.Size > 0 then try

       GetMem(pBuffer, Blob.Size);

       Blob.Read(pBuffer^, Blob.Size);

       { что-то тут делаем }

       FreeMem(pBuffer, Blob.Size);

      except

       ShowMessage('Нехватка памяти' );

      end;

     finally

      Blob.Free

     end;

    Как определить изменение фокуса строки в TDBGrid?

    Используйте событие OnDataChange объекта Datasource, соединенного с DBGrid. Если параметр State в обработчике событие равен dsBrowse, значит вы перешли в новую строку (или только что открыли таблицу).

    Почему сетка не поддерживает такое событие? Поскольку сетка может быть не единственным элементом управления, оторбажающим данные из текущей строки и может быть не единственным элементом, позволяющим осуществлять перемещение от строки к строке. С помощью Datasource обработка события осуществляется централизованно.

    Я не уверен в том, что проблему можно решить, обрабатывая событие одинарного щелчка, для отслеживания события изменения строк я рекомендую использовать событие TDatasource.OnDataChange, а для колонок — TDBGrid.OnColEnter/Exit.

    Лично я пользуюсь следующей рабочей технологией:

    1. Для того, чтобы обнаружить изменения текущей строки, воспользуйтесь событием TDataSource OnDataChange. OnDataChange возникает при прокрутке или щелчке на другой строке. Обработчик события может выглядеть приблизительно так:

    procedure Form1.DSrc1DataChange(Sender: TObject; Field: TField);

    где Field является колонкой, где произошло изменение.

    Поля TTable могут использоваться для сравнения текущих выбранных строк полей (ключ) с вашими требованиями. С той же целью может быть использовано и свойство TDBGrid Fields. Для примера:

    if tbl1.Fields[0].AsString = 'BlaBlaBla' then …

    или

    if dbGrid1.Fields[I].IsNull then …

    2. Для отслеживания изменения колонки, используйте события TDBGrid OnColExit & OnColEnter. Для определения выбранной к настоящему времени колонки воспользуйтесь свойствами TDBGrid SelectedField и SelectedIndex.

    Когда выбирается другая колонка другой строки, вы получаете события OnColExit, OnColEnter и OnDataChange.

    3. Можно пойти и «кривым» путем, взявшись за обработку события TDBGrid OnDrawDataCell, которое возникает когда ячейка выбирается, или когда сетка скроллируется. Обработчик события может выглядеть примерно так:

    procedure Form1.dbGrid1DrawDataCell(Sender: TObject; Rect: TRect; Field: TField; State: TGridDrawState);

    При изменении ячейки вы получаете поток событий, поэтому вам нужно каким-то образом их фильтровать.

    4. Если у вас нет проблем в создании «101 изменения» стандартных компонентов – что является проблемой для меня 8-), то попробуйте это. Это легко.

    Чтобы иметь доступ к индексу строки или колонки выбранной ячейки, вы должны унаследовать ваш класс от TCustomGrid и опубликать свойства времени выполнения Row и Col (текущие строка и колонка сетки, не таблицы!!):

    type TSampleDBGrid = class(TCustomGrid)

    public

     property Col;

     property Row;

    end;

    в соответствующей процедуре или обработчике события осуществите приведение типа:

    var G: TSampleDBGrid;

    begin

     G := TSampleDBGrid(myDBGrid1);

     if G.Row = I then …

     if G.Col = J then …

    Дело в том, что TDBGrid является потомком TCustomGrid, который имеет несколько свойств, содержащих координаты сетки, но это не опубликовано в TDBGrid.

    …из чего я могу заключить, что вы должны это сделать программным путем. Подразумеваем, что сетка уже существует, и у вас есть доступ к основной таблице TTable:

    grid.colcount := dbGrid.fieldcount;

    table.first;

    row := 0;

    while not table.eof do begin

     grid.rowcount := row + 1;

     for i := 0 to grid.colcount-1 do

      grid.cells[i,row] := dbGrid.fields[i].asString;

     table.next;

     inc(row);

    end;

    Могут быть ошибки, но это должно помочь.

    Посмотрите на следующий код, он может вам помочь. Он берет у элемента управления свойсто 'Name' и помещает его в свойство 'Caption' метки.

    unit Unit1;


    interface


    uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;


    type TForm1 = class(TForm)

     Label1: TLabel;

     Edit1: TEdit;

     Edit2: TEdit;

     Button1: TButton;

     procedure Button1Click(Sender: TObject);

     procedure Edit1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

     procedure Edit2MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

    private

     { Private declarations }

    public

     { Public declarations }

    end;


    var Form1: TForm1;


    implementation


    {$R *.DFM}


    procedure TForm1.Button1Click(Sender: TObject);

    begin

     close;

    end;


    procedure TForm1.Edit1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

    begin

     Label1.Caption := TEdit(Sender).Name;

    end;


    procedure TForm1.Edit2MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

    begin

     Label1.Caption := TEdit(Sender).Name;

    end;

    end.
     

    Включение ComboBox в TDBGrid

    Delphi 1 

    Вот основные шаги чтобы сделать это:

    1. Создавайте и рисуйте TComboBox (CB) при получении ввода ячейки необходимой колонки табличной сетки

    2. Получайте текущее значение поля (если имеется) и помещайте его в CB

    3. После всех манипуляций, поместите новое значение обратно в поле

    4. Избавляемся от CB 

    DBLookupComboBox 

    Предустановка DBLookupComboBox

    Delphi 1 

    Вы можете редактировать ваш источник данных. Говорят, вы хотите сохранить ваши lookuping-данные из таблицы customer в таблицу sales – 'Cust No'? Вы можете просто проинициализировать поля (задать значение по умолчанию), редактируя таблицу sales «Cust No»

    with tbSales do begin

     Edit;

     FieldByName('Cust No').AsInteger := 1;

     Post;

    end;
     

    Сортировка DBLookupComboBox по вторичному индексу

    Delphi 1 

    Одним из способов вывести выши данные в другом порядке сортировки является использование TQuery и включение в SQL-запрос ключевого слова «order by». После чего вы можете установить этот запрос как DataSource в вашем DBLookupComboBox.

    ПРИМЕР:

    Если у вас имеется таблица Customer, содержащая «Customer_No» и «Customer_Name», и индексированная по Customer_No, то ваш запрос должен содержать в редакторе списка строк (свойство SQL) для вашего TQuery следующую строку:

    select Customer_No, Customer_Name from Customer

     order by Customer_Name

    Значение DBLookupComboBox

    Я думаю что у меня есть то, что вы хотите. Если вы обратитесь к свойству LookUpValue, то вы получите поле, которое .... ищете.

    Я надеюсь что помог вам.

    unit clookup;

    interface

    uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, DB, DBLookup;


    type

     TDBJustLookupCombo = class(TDBLookupCombo)

     private

      { Private declarations }

     protected

      { Protected declarations }

      function GetLValue: TField;

     public

      { Public declarations }

      property LookUpValue: TField read GetLValue;

     published

      { Published declarations }

     end;


     TDBJustLookupList = class(TDBLookupList)

     private

      { Private declarations }

     protected

      { Protected declarations }

      function GetLValue: TField;

     public

      { Public declarations }

      property LookUpValue: TField read GetLValue;

     published

      { Published declarations }

     end;


    procedure Register;


    implementation


    procedure Register;

    begin

     RegisterComponents('Data Controls', [TDBJustLookupList, TDBJustLookupCombo]);

    end;


    function TDBJustLookupCombo.GetLValue: TField;

    begin

     Result := LookupSource.DataSet.FieldByName(LookUpField);

    end;


    function TDBJustLookupList.GetLValue: TField;

    begin

     Result := LookupSource.DataSet.FieldByName(LookUpField);

    end;

    end.
     

    DBMemo 

    Копирование содержимого DBMemo в DBMemo другого поля

    Delphi 1 

    Попробуй:

    DBMemo6.Lines:=DBMemo5.Lines.Assign;

    Поиск текста в DBMemo

    Delphi 1

    Попробуйте так:

    "Подключите" следующую процедуру к событию OnFind для FindDialog. Единственная проблема заключается в том, что в DBMemo я не могу получить выделенный текст, тем не менее в стандартном Memo такой проблемы нет.

    procedure TMainForm.FindDialog1Find(Sender: TObject);

    var

     Buff, P, FT : PChar;

     BuffLen     : Word;

    begin

     With Sender as TFindDialog do begin

      GetMem(FT, Length(FindText) + 1);

      StrPCopy(FT, FindText);

      BuffLen:= DBMemo1.GetTextLen + 1;

      GetMem(Buff,BuffLen);

      DBMemo1.GetTextBuf(Buff,BuffLen);

      P:= Buff + DBMemo1.SelStart + DBMemo1.SelLength;

      P:= StrPos(P, FT);

      if P = NIL then MessageBeep(0)

      else begin

       DBMemo1.SelStart:= P – Buff;

       DBMemo1.SelLength:= Length(FindText);

      end;

      FreeMem(FT, Length(FindText) + 1);

      FreeMem(Buff,BuffLen);

     end;

    end;

    Попробуйте так:

    «Подключите» следующую процедуру к событию OnFind для FindDialog. Единственная проблема заключается в том, что в DBMemo я не могу получить выделенный текст, тем не менее в стандартном Memo такой проблемы нет.

     begin

      DBMemo1.SelStart:= P – Buff;

      DBMemo1.SelLength:= Length(FindText);

     end;

     FreeMem(FT, Length(FindText) + 1);

     FreeMem(Buff,BuffLen);

     DBMemo1.SetFocus;

    end;

    DBNavigator 

    Настройки всплывающих подсказок в DBNavigator во время выполнения приложения

    Возможно ли изменение свойства Hints компонента TDBNavigator во время выполнения программы?

    Это должно работать:

    procedure TForm1.Button1Click(Sender: TObject);

    var ix : integer;

    begin

     With DBNavigator1 do

      for ix := 0 to ControlCount - 1 do

       if Controls[ix] is TNavButton then

        with Controls[ix] as TNavButton do

         case index of

         nbFirst : Hint := 'Подсказка для кнопки First';

         nbPrior : Hint := 'Подсказка для кнопки Prior';

         nbNext : Hint := 'Подсказка для кнопки Next';

         nbLast : Hint := '';

         {……}

         end;

    end;

    – Freddy Hansson

    Выключение кнопок в DBNavigator

    Delphi 1

    { Расширение DBNavigator: позволяет разработчику включать и выключать

    отдельные кнопки через методы EnableButton и DisableButton }

    unit GNav;


    interface


    uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, DBCtrls;


    type TMyNavigator = class(TDBNavigator)

    public

     procedure EnableButton(Btn : TNavigateBtn);

     procedure DisableButton(Btn : TNavigateBtn);

    end;


    procedure Register;


    implementation


    procedure TMyNavigator.EnableButton(Btn : TNavigateBtn);

    begin

     Buttons[Btn].Enabled := True;

    end;


    procedure TMyNavigator.DisableButton(Btn : TNavigateBtn);

    begin

     Buttons[Btn].Enabled := False;

    end;


    procedure Register;

    begin

     RegisterComponents('Samples', [TMyNavigator]);

    end;

    end.
     

    Работа в коде с кнопками DBNavigator

    Delphi 1 

    Я думаю вам поможет следующий пример (взят из электронной справки по DELPHI), показывающий код нажатой кнопки. Я видел пару вопросов о том, как изменять кнопки навигатора в зависимости от состояния редактируемой вами записи. Если вам необходимо подтверждение действий пользователя, то необходимо каким-то образом организовать дополнительный перехватчик. Как это сделать, я, честно говоря, еще не думал.

    Прежде, чем вы сделаете любой постинг или изменение данных, убедитесь, что таблица находится в режиме редактирования. Посмотрите описание свойства state в электронной справке по DELPHI. Там подробно рассказано как работать с ним.

    Следующий код определяет нажатую кнопку навигатора и выводит сообщение с ее именем.

    procedure TForm1.DBNavigator1Click(Sender: TObject; Button: TNavigateBtn);

    var BtnName: string;

    begin

     case Button of

     nbFirst  : BtnName := 'nbFirst';

     nbPrior  : BtnName := 'nbPrior';

     nbNext   : BtnName := 'nbNext';

     nbLast   : BtnName := 'nbLast';

     nbInsert : BtnName := 'nbInsert';

     nbDelete : BtnName := 'nbDelete';

     nbEdit   : BtnName := 'nbEdit';

     nbPost   : BtnName := 'nbPost';

     nbCancel : BtnName := 'nbCancel';

     nbRefresh: BtnName := 'nbRefresh';

     end;

     MessageDlg('Была нажата кнопка' + BtnName, mtInformation, [mbOK], 0);

    end;

    Edit 

    Денежное поле редактирования

    Delphi 1 

    unit CurrEdit;


    interface


    uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Menus, Forms, Dialogs, StdCtrls;


    type TCurrencyEdit = class(TCustomMemo)

    private

     DispFormat: string;

     FieldValue: Extended;

     procedure SetFormat(A: string);

     procedure SetFieldValue(A: Extended);

     procedure CMEnter(var Message: TCMEnter); message CM_ENTER;

     procedure CMExit(var Message: TCMExit);   message CM_EXIT;

     procedure FormatText;

     procedure UnFormatText;

    protected

     procedure KeyPress(var Key: Char); override;

     procedure CreateParams(var Params: TCreateParams); override;

    public

     constructor Create(AOwner: TComponent); override;

    published

     property Alignment default taRightJustify;

     property AutoSize default True;

     property BorderStyle;

     property Color;

     property Ctl3D;

     property DisplayFormat: string read DispFormat write SetFormat;

     property DragCursor;

     property DragMode;

     property Enabled;

     property Font;

     property HideSelection;

     property MaxLength;

     property ParentColor;

     property ParentCtl3D;

     property ParentFont;

     property ParentShowHint;

     property PopupMenu;

     property ReadOnly;

     property ShowHint;

     property TabOrder;

     property Value: Extended read FieldValue write SetFieldValue;

     property Visible;

     property OnChange;

     property OnClick;

     property OnDblClick;

     property OnDragDrop;

     property OnDragOver;

     property OnEndDrag;

     property OnEnter;

     property OnExit;

     property OnKeyDown;

     property OnKeyPress;

     property OnKeyUp;

     property OnMouseDown;

     property OnMouseMove;

     property OnMouseUp;

    end;


    procedure Register;


    implementation


    procedure Register;

    begin

     RegisterComponents('Additional', [TCurrencyEdit]);

    end;


    constructor TCurrencyEdit.Create(AOwner: TComponent);

    begin

     inherited Create(AOwner);

     AutoSize := True;

     Alignment := taRightJustify;

     Width := 121;

     Height := 25;

     DispFormat := '$,0.00;($,0.00)';

     FieldValue := 0.0;

     AutoSelect := False;

     WantReturns := False;

     WordWrap := False;

     FormatText;

    end;


    procedure TCurrencyEdit.SetFormat(A: String);

    begin

     if DispFormat <> A then begin

      DispFormat:= A;

      FormatText;

     end;

    end;


    procedure TCurrencyEdit.SetFieldValue(A: Extended);

    begin

     if FieldValue <> A then begin

      FieldValue := A;

      FormatText;

     end;

    end;


    procedure TCurrencyEdit.UnFormatText;

    var

     TmpText : String;

     Tmp     : Byte;

     IsNeg   : Boolean;

    begin

     IsNeg := (Pos('-',Text) > 0) or (Pos('(',Text) > 0);

     TmpText := '';

     For Tmp := 1 to Length(Text) do

      if Text[Tmp] in ['0'..'9','.'] then

       TmpText := TmpText + Text[Tmp];

     try

      FieldValue := StrToFloat(TmpText);

      if IsNeg then FieldValue := -FieldValue;

     except

      MessageBeep(mb_IconAsterisk);

     end;

    end;


    procedure TCurrencyEdit.FormatText;

    begin

     Text := FormatFloat(DispFormat,FieldValue);

    end;


    procedure TCurrencyEdit.CMEnter(var Message: TCMEnter);

    begin

     SelectAll;

     inherited;

    end;


    procedure TCurrencyEdit.CMExit(var Message: TCMExit);

    begin

     UnformatText;

     FormatText;

     Inherited;

    end;


    procedure TCurrencyEdit.KeyPress(var Key: Char);

    begin

     if Not (Key in ['0'..'9','.','-']) Then Key := #0;

     inherited KeyPress(Key);

    end;


    procedure TCurrencyEdit.CreateParams(var Params: TCreateParams);

    begin

     inherited CreateParams(Params);

     case Alignment of

     taLeftJustify : Params.Style := Params.Style or ES_LEFT and Not ES_MULTILINE;

     taRightJustify: Params.Style := Params.Style or ES_RIGHT and Not ES_MULTILINE;

     taCenter      : Params.Style := Params.Style or ES_CENTER and Not ES_MULTILINE;

     end;

    end;

    end.
     

    Отслеживаем позицию курсора в EditBox

    Совет от читателя 

    The_Sprite советует:

    В форму добавляются TEditBox и TLabel, при этом TLabel постоянно показывает позицию курсора в элементе редактирования.

    Совместимость: Все версии Delphi

    Пример:

    procedure TForm1.Edit1Change(Sender: TObject);

    begin

     CurPos := Edit1.SelStart;

     Label1.Caption := IntToStr(CurPos);

    end;


    procedure TForm1.Edit1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);

    begin

     If Key = VK_LEFT then dec(CurPos);

     if Key = VK_RIGHT then inc(CurPos);

     Label1.Caption:= IntToStr(CurPos);

    end;
     

    GroupBox

    Рисование на GroupBox

    Я хочу рисовать на холсте (Canvas) моего компонента GroupBox. Но когда я пробую рисовать на Component.Parent.Canvas, рисование происходит на форме, а не на моем компоненте GroupBox. Что я делаю неправильно?

    Canvas – защищенное свойство TGroupBox и, поэтому, недоступное. Вы можете сделать его доступным следующим образом:

    type TMyGroupBox = class(TGroupBox)

    public

     property Canvas;

    end;


    procedure SomeProcedure;

    begin

     …

     with TMyGroupBox(GroupBox1).Canvas do

      CopyRect(ClipRect, Image1.Canvas, ClipRect);

     …

    end;

    – Ralph Friedman 

    Доступ к компонентам GroupBox

    Delphi 1 

    Одно из свойств всех элементов управления – указатель на другие элементы, которые он содержит. Это свойство – свойство Controls, которое индексируется наподобие массива. Количество элементов управления содержится в свойстве ControlCount. Если вы хотите получить доступ к свойству или методу, которого нет у TControl, вам неоходимо осуществить приведение типа элемента списка.

    procedure DoSomethingWithAGroupBox;

    var i: integer;

    begin

     with  AGroupBox do

      for i := 0 to ControlCount - 1 do

       if controls[i] is TEdit then

        TEdit(controls[i]).text := 'Как насчет этого?';

    end;

    end;

    Приведенный выше пример будет работать, если элементом управления является TEdit или его наследник, например, TDBEdit или TMaskEdit. Все объекты могут быть приведены к типу одного из объектов, являющегося наследником базового типа (или им самим). Но не спешите приводить все к родительскому классу, родитель в данном случае здесь не подходит, поскольку он означает объект, который содержит сам себя.

    Label

    Как сделать бегущую строку?

    Письмо читателя 

    The_Sprite отвечает:

    с помощью TLabel и TTimer. Пример:

    procedure TForm1.Timer1Timer(Sender: TObject);

    Const

     LengthGoString = 10;

     GoString = 'В конце строку желательно повторить,'+

      ' чтоб получить эффект кольцевого движения! В конце ст';

    Const i: Integer = 1;

    begin

     Label1.Caption:=Copy(GoString,i,LengthGoString);

     Inc(i);

     If Length(GoString)-LengthGoString < i then i:=1;

    end;
     

    ListBox 

    Навигация в ListBox при множественном выборе

    Тема: Навигация в ListBox при множественном выборе

    Данный пример выводит сообщение для каждого элемента Listbox, выбранного пользователем.

    procedure TForm1.Button1Click(Sender: TObject);

    var Loop: Integer;

    begin

     for Loop := 0 to Listbox1.Items.Count – 1 do begin

      if listbox1.selected[loop] then ShowMessage(Listbox1.Items.Strings[Loop]);

     end;

    end;
     

    Внешние данные и ListBox

    Delphi 2 

    Мне необходимо создать Listbox с использованием внешних данных, хранимых в огромном (!) TStringList. Существует ли какое-нибудь системное сообщение, которое я мог бы перехватывать для получения данных Listbox из внешнего TStringlist?

    Просматривая справочник по API, я нашел интересный пункт, который может помочь вам решить проблему: в Win32 вы можете создать Listbox со стилем LBS_NODATA:

    (из описания CreateWindow:)

    LBS_NODATA

    Определяет ListBox со стилем no-data (без данных). Данный стиль необходимо применять в случае, если количество элементов в ListBox превышает одну тысячу. no-data ListBox также должен иметь стиль LBS_OWNERDRAWFIXED, но не может иметь стиль LBS_SORT или LBS_HASSTRINGS.

    no-data ListBox похож на owner-drawn ListBox за исключением того, что он не содержит в своих элементах строк и изображений (иконок). Команды добавления, вставки или удаления данных в элементах такого типа ListBox будут проигнорированы, а запросы для поиска строк всегда будут заканчиваться неудачей. При необходимости отрисовки данного элемента, Windows посылает родительскому окну сообщение WM_DRAWITEM. Член itemID стуктуры DRAWITEMSTRUCT, передаваемой с сообщением WM_DRAWITEM, определяет номер строки (элемент), который должен быть перерисован. no-data ListBox не посылает сообщение WM_DELETEITEM.

    Количество элементов в таком списке вы можете установить с помощью сообщения LB_SETCOUNT. Это позволит вам создать «виртуальный» ListBox с очень небольшой загрузкой.

    Чтобы воспользоваться новым стилем, вам нужно создать новый класс-наследник от TListbox и перекрыть метод CreateParams.

    – Peter Below

    Инкрементальный поиск в ListBox II


    Я видел приложение, в котором ListBox позволял осуществлять инкрементальный поиск. При вводе очередного символа он позиционирует вас к первой ячейке, начало значения которой совпадает с введенным пользователем текстом, или выделяет все строки с текстом, содержащим введенный текст.

    Как это осуществить на Delphi?

    Здесь придется немного воспользоваться Win API. Установите свойство формы KeyPreview в True и сделайте примерно следующее:

    unit LbxSrch;

    interface


    uses Windows, Messages, SysUtils, Classes, Controls, Forms, StdCtrls;


    type TFrmLbxSrch = class(TForm)

     Edit1: TEdit;

     Edit2: TEdit;

     ListBox1: TListBox;

     Label1: TLabel;

     procedure FormKeyPress(Sender: TObject; var Key: Char);

     procedure ListBox1Enter(Sender: TObject);

    private

     { Private declarations }

     FPrefix: array[0..255] of char;

    public

     { Public declarations }

    end;


    varFrmLbxSrch: TFrmLbxSrch;


    implementation


    {$R *.DFM}


    procedure TFrmLbxSrch.FormKeyPress(Sender: TObject; var Key: Char);

    { Помните о том, что свойство KeyPreview должно быть установлено в True }

    var

     curKey: array[0..1] of char;

     ndx: integer;

    begin

     if ActiveControl = ListBox1 then begin

      if key = #8 {Backspace (клавиша возврата)} then begin

       if FPrefix[0] <> #0 then begin

        FPrefix[StrLen(FPrefix) - 1] := #0;

       end

      end else begin

       curKey[0] := Key;

       curKey[1] := #0;

       StrCat(FPrefix, curKey);

       ndx := SendMessage(ListBox1.Handle, LB_FINDSTRING,-1, longint(@FPrefix));

       if ndx <> LB_ERR then ListBox1.ItemIndex := ndx;

      end;

      Label1.Caption := StrPas(FPrefix);

      Key := #0;

     end;

    end;


    procedure TFrmLbxSrch.ListBox1Enter(Sender: TObject);

    begin

     FPrefix[0] := #0;

     Label1.Caption := StrPas(FPrefix);

    end;

    end.

    – Ralph Friedman

    Табуляция в графическом ListBox'е

    Письмо читателя

    Использование табуляции в ListBox'е когда компонент находится в стандартном режиме не составляет труда. Но что делать если надо использовать графическое отображение элементов списка? Ведь при этом надо самому писать обработчик отрисовки элементов с разбиением на колонки. Элементарное решение — использование API функции TabbedTextOut, однако результаты работы этой функции меня явно не удовлетворили. Пришлось-таки "выкручиваться"… Символ-разделитель можно использовать любой. Например, будем использовать символ "|", тогда обработчик OnDrawItem может выглядеть следующим образом:

    procedure TBrowser.ListBox1DrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState);

    var

     S, Ss: String;

     P: Integer; // Флаг символа-разделителя

    begin


     begin

      ListBox1.Canvas.FillRect(Rect);

      //Отрисовка графики

     

      //

      S:=ListBox1.Items.Strings[Index];

      P:=Pos('|', S);

      If P=0 then Ss:=S

      else Ss:=Copy(S, 1, P-1);

      // Если нет табуляции, то пишем всю строку, иначе отрезаем кусок до разделителя

      ListBox1.Canvas.TextOut(Rect.Left + 20, Rect.Top + 2, Ss);

      If P>0 then

       ListBox1.Canvas.TextOut(ListBox1.TabWidth, Rect.Top + 2, Copy(S, P+1, Length(S)-P+2));

     end;

    end;

    Не забудьте перед запуском поставить нужное значение TabWidth.

    Virtualik

    Выравнивание в ListBox'е

    Delphi 1

    Перед тем, как вычислить позицию фразы, необходимо с помощью функции TextWidth вычислить ее ширину.

    Например:

    var J, TempInt, LongPrefixLen, CurrPrefixLen: Integer;

    begin

     {Вычисляем TextWidth по ключевой строке}

     {Устанавливаем CurrPrefixLen в TextWidth ключевого слова строки Indexth}

     LongPrefixLen := 0;

     for J := 0 to ListBox1.Items.Count-1 do

      with ListBox1.Canvas do begin

      TempInt:= TextWidth(Copy(Items[J], 1, Pos(KeyString, Items[J]-1)));

      if LongPrefixLen < TempInt then LongPrefixLen:= TempInt;

      if J = Index  then CurrPrefixLen:= TempInt;

     end;

     {PrevTextLeft – TextLeft = Где мы хотим вывести новый элемент}

     TextOut(LongPrefixLen-CurrPrefixLen, Y, Items[I]);

    end;

    Создание ListBox во время выполнения программы

    Delphi 1

    Установка выравнивания ListBox на alLeft вызывает изменение размеров ListBox при любом изменении размеров формы. Установка ширины происходит очень легко (помните о том, что ширина Width, которую вы видите в правой части строки, является свойством Width формы).

    Количество элементов, хранимых ListBox, ограничено только доступной памятью.

    procedure TForm1.CreateListBox;

    var LB : TListBox;

    begin

     LB := TListBox.Create;

     LB.Align := alLeft;

     LB.Width := Width div 2;

    end;

    Вот логика динамического создания ListBox и изменения его размера при изменения размеров формы. Я надеюсь, что помог вам. Также я подозреваю, что данные ListBox ограничены 32 килобайтами.

    unit Unit1;


    interface


    uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls  { вам нужно это для ListBox }  ;


    type TForm1 = class(TForm)

     procedure FormCreate(Sender: TObject);

     procedure FormResize(Sender: TObject);

    private

     { Private declarations }

    public

     { Public declarations }

    end;


    var

     Form1: TForm1;

     listbox: TListBox;


    implementation


    {$R *.DFM}


    procedure TForm1.FormCreate(Sender: TObject);

    begin

     listbox := TListBox.Create(self);

     listbox.Parent := self;

     listbox.Top := 0;

     listbox.Left := 0;

     listbox.Width := self.Width div 2;

     listbox.Height := self.Height div 2;

     listbox.items.add('тест 1');

     listbox.items.add('тест 2');

     { и т.д, и т.п. … }

    end;


    procedure TForm1.FormResize(Sender: TObject);

    begin

     listbox.Width := self.Width div 2 ;

     listbox.Height := self.Height div 2 ;

    end;

    end.

    Двойной ListBox

    Я расположил на форме два компонента Listbox, и с помощью следующего кода заполнил один из них данными из таблицы:

    tableName.Refresh; {в вашем случае это может и не понадобится}

    tableName.First;   {Убедимся, что мы смотрим первую запись}

    while not tableName.Eof do {проходим в цикле таблицу}

    begin

     listbox1.items.add(tableName.FieldByName('USRID').AsString); {добавляем элемент в listbox1}

     tableName.Next; {переходим к следующей записи}

    end;

    ниже я привел процедуру из моего рабочего кода, в котором я использую эту технологию. Я передаю ей в качестве параметров имя таблицы и имена компонентов listbox1 и listbox2. Я пользуюсь этой процедурой, поскольку у меня есть несколько таблиц с полями одинакового типа:

    procedure TTemplateFrm.buildList(tableName: TTable; SelBox, AvailBox: TListBox);

    begin

     {в этой процедуре мы собираемся добавить данные в listbox'ы}

     {получаем любые новые данные}

     tableName.Refresh;

     {Убедимся, что мы смотрим первую запись}

     tableName.First;

     {Теперь очищаем ListBox'ы}

     SelBox.Clear;

     AvailBox.Clear;

     {Теперь добавляем элементы}

     while not tableName.EOF do begin

      AvailBox.Items.Add(tableName.fieldByName('USRID').AsString + ' ' + tableName.fieldByName('DESCRIPTION').AsString);

      tableName.Next;

     end;

    end;

    Как перемещать данные между этими двумя списками? Если вы хотите использовать технологию «drag and drop» (перетащи и брось), то в обработчике mousedown вашей исходной таблицы воспользуйтесь процедурой begindrag:

    if Button = mbLeft then Tlistbox(sender).BeginDrag(false);

    Затем, в вашем другом ListBox, для «опознания» и получения данных создайте следующий обработчик DragOver:

    if Source = ListBox1 then Accept := true

    else Accept := false;

    Не используйте «Accept := (Source is TListbox)», как это показано в большинстве примеров. У вас имеется два компонента ListBox, следовательно, вам нужно сослаться на имя объекта, а не на его тип, а иначе программа просто не поймет кто есть кто.

    Затем в обработчике dragDrop поместите следующий код, добавляющий данные в ListBox2 и удаляющий их из ListBox1.

    Listbox2.Items.Add(Listbox1.Items[Listbox1.ItemIndex]);

    Listbox1.Items.Delete(Listbox1.ItemIndex);

    И, наконец, добавьте кнопку «Сохранить», если вы хотите сохранить содержимое ListBox2 в базе данных.

    Я надеюсь, что это именно то, что вы искали, и что это окажется вам полезным. Если вы хотите также перемещать данные из ListBox2 в ListBox1, вам необходимо будет создать тот же код, но реверсировать его для получения в коде правильных ссылок на компоненты ListBox.

    ListBox — OnChange

    Delphi 1

    Это было два месяца тому назад. Я нашел это на одном из Delphi-сайтов. Не очень сложно и понятно.

    UNIT Lbox;


    INTERFACE


    USES SysUtils, WinTypes, Messages, Classes, Controls, Graphics, Forms,Menus, StdCtrls;


    Type TCngListBox = Class(TListBox)

    private

     FOnChange : TNotifyEvent;

     FLastSel : integer;

     procedure Click; override;

    protected

     procedure Change; Virtual;

    published

     property OnChange : TNotifyEvent read FOnChange write FOnChange;

    public

     constructor create(AOwner : TComponent); override;

    End;


    Procedure Register;


    IMPLEMENTATION


    procedure TCngListBox.Change;

    begin

     FLastSel := ItemIndex;

     if assigned(FOnChange) then FOnChange(self);

    end;


    procedure TCngListBox.Click;

    begin

     inherited Click;

     if FLastSel <> ItemIndex then Change;

    end;


    constructor TCngListBox.Create;

    begin

     Inherited Create(AOwner);

     FLastSel := –1;

    end;


    procedure Register;

    begin

     RegisterComponents('FreeWare',[TCngListBox]);

    end;

    END.

    MainManu 

    Как рисовать картинки в пунктах меню (через OwnerDraw)?

    Nomadic советует:

    Смотри пример:

    unit DN_Win;


    interface


    uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, Menus, StdCtrls,


    type TDNForm = class(TForm)

     MainMenu1: TMainMenu;

     cm_MainExit: TMenuItem;

     procedure FormCreate(Sender: TObject);

     procedure cm_MainExitClick(Sender: TObject);

    private

     { Private declarations }

    public

     { Public declarations }

     BM:TBitmap;

     Procedure WMDrawItem(var Msg:TWMDrawItem); message wm_DrawItem;

     Procedure WMMeasureItem(var Msg:TWMMeasureItem); message wm_MeasureItem;

    end;


    var DNForm : TDNForm;


    implementation


    {$R *.DFM}


    var Comm, yMenu : word;


    procedure TDNForm.FormCreate(Sender: TObject);

    begin

     {картинку в меню}

     yMenu:=GetSystemMetrics(SM_CYMENU);

     comm:=cm_MainExit.Command;

     ModifyMenu(MainMenu1.Handle, 0, mf_ByPosition or mf_OwnerDraw, comm, 'Go');

    end;{TDNForm.FormCreate}


    procedure TDNForm.cm_MainExitClick(Sender: TObject);

    begin

     DNForm.Close;

    end;{TDNForm.cmExitClick}


    {для прорисовки меню}

    Procedure TDNForm.WMMeasureItem(var Msg:TWMMeasureItem);

    Begin

     with Msg.MeasureItemStruct^ do begin

      if ItemID=comm then begin

       ItemWidth:=yMenu;

       Itemheight:=yMenu;

      end;

     end;

    End;{WMMeasureItem}

    {}

    Procedure TDNForm.WMDrawItem(var Msg:TWMDrawItem);

    var

     MemDC:hDC;

     BM:hBitMap;

     mtd:longint;

    Begin

     with Msg.DrawItemStruct^ do begin

      if ItemID=comm then begin

       BM:=LoadBitMap(hInstance,'dver');

       MemDC:=CreateCompatibleDC(hDC); {hDC входит в структуру TDrawItemStruct}

       SelectObject(MemDC,BM);

       {rcItem входит в структуру TDrawItemStruct}

       if ItemState=ods_Selected then mtd:=NotSrcCopy

       else mtd:=SrcCopy;

       StretchBlt(hDC, rcItem.left, rcItem.top, yMenu, yMenu, MemDC, 0, 0, 24, 23, mtd);

       DeleteDC(MemDC);

       DeleteObject(BM);

      end;

     end{with}

    End;{TDNForm.WMDrawItem}

    end.
     

    Memo 

    Получение данных из компонента Memo

    Delphi 1

    Для получения содержимого буфера используйте метод GetTextBuf, или воспользуйтесь приведенным ниже кодом (естественно, откорректируйте его под себя).

    procedure TForm1.SpeedButton1Click(Sender: TObject);

    var

     LineNo : integer;

     ColNo  : integer;

    begin

     LineNo:=SendMessage(Memo1.Handle, EM_LINEFROMCHAR, Memo1.SelStart, 0);

     ColNo:=Memo1.SelStart;

     if LineNo>0 then begin

      While SendMessage(Memo1.Handle, EM_LINEFROMCHAR, ColNo, 0) = LineNo  do ColNo:=ColNo-1;

      ColNo:=Memo1.SelStart-ColNo-1;

     end else ColNo:=Memo1.SelStart;

     Panel1.Caption:='Строка '+IntToStr(LineNo)+' ; Колонка '+IntToStr(ColNo);

     {Здесь вы можете получить текст через Memo1.Lines[LineNo].Text[ColNo] …}

    end;

    Предупреждение! Данный код был написан в среде WinNT/D2 с использованием элемента управления richedit. Я тестировал то же самое, но с компонентом Memo и в D1, но этот код я забыл дома. Код выше написан по памяти и не тестировался, но я думаю он должен работать. Если вы переберетесь на D2, измените вызов sendmessage на следующий:

    SendMessage(Memo1.Handle, EM_EXLINEFROMCHAR, 0, ColNo)

    Изменение поведения Delete в компоненте Memo

    Delphi 1

    Просто меняю обработчик Memo OnKeyDown следующим образом:

    if Key = VK_DELETE then begin

     здесь делайте все, что вы хотите

    end;

    if Key = VK_BACK then begin

     аналогично

    end;

    Вероятно, лучшим решением было бы использование конструкции CASE, но я не уверен, что она поймет как нужно VK_??. Возможно, после обработки нужно вызвать унаследованный обработчик, т.е. дать поработать обработчику верхнего уровня, у которого мы стырили управление. Не хотите подумать над этим?

    Чтобы понять, где мы сейчас находимся, используйте SelStart, например, так:

    var

     Lpos, Cpos : Integer;

     Lpos := SendMessage(memo1.Handle, EM_LINEFROMCHAR, Memo1.SelStart, 0);

     Cpos := SendMessage(memo1.Handle, EM_LINEINDEX, Lpos, 0);

     CPos := Memo1.SelStart-CPos;

    Ответ: поскольку vk_? имеет целочисленный тип, то это будет работать:

    case Key of

    VK_DELETE :

     begin

      Key := 0;  {этим мы не позволяем сообщению keydown передаваться дальше,

       например, форме или компонентам}

      выполняем нужный код;

     end;

    VK_BACK:

     begin

      Key := 0;  {этим мы не позволяем сообщению keydown передаваться дальше,

       например, форме или компонентам}

      выполняем нужный код;

     end;

    end;

    Вставка текста в TMemo II

    Delphi 1

    Используйте сообщение Windows API EM_REPLACESEL:

    EM_REPLACESEL

    wParam = 0; /* не используется, должен быть ноль */

    lParam = (LPARAM) (LPCSTR) lpszReplace; /* адрес новой строки */

    Для замены текущего выбранного текста в поле редактирования, приложение должно послать сообщение EM_REPLACESEL, где параметр lpszReplace содержит новый текст.

    Параметр Описание
    lpszReplace Значение lParam. Указатель на терминированную нулем строку, содержащую замещающий текст. { Указатель на строку }

    Возвращаемое значение

    Данное сообщение значение не возвращает.

    Комментарии

    Используйте сообщение EM_REPLACESEL, если вы хотите изменять только часть текста поля редактирования. Если вам нужно заменить весь текст, используйте сообщение WM_SETTEXT.

    В случае отсутствия выбранного текста, замещающий текст вставляется в текущую позицию курсора.

    ((из справки по Windows API))

    Сделайте список с вашими стандартными фразами, и используйте события "OnClick" или "OnMouseDown" в комбинации с "Alt", "Shift" или "Ctrl". Пример: Когда пользователь нажимает клавишу "Alt" в комбинации с правой кнопкой мыши, выводится список заранее подготовленных фраз и выбранная вставляется в ваш TMemo-компонент.

    Для вставки строки в Memo:

    procedure TForm1.Button1Click(Sender: TObject);

    begin

     with Memo1 do begin

      SelStart:=10;

      SelLength:=0;

      SelText:='Эта строка включается в Memo, начиная с 10-й позиции ';

     end;

    end;

    Для вставки строки и замены некоторого существующего текста:

    procedure TForm1.Button1Click(Sender: TObject);

    begin

     with Memo1 do begin

      SelStart:=10;

      SelLength:=20;

      SelText:='Эта строка включается в Мемо, начиная с 10-й позиции и замещает собой 20 символов ';

     end;

    end;

    Поместите текст, который вы хотите вставить, в переменную PChar, затем вставьте текст в Memo, используя команду SetSelTextBuf, где SelStart устанавливается в позицию курсора TMemo. Это классно работает.

    Другая полезность: вы можете обхойти предел TMemo в 32K в случае, если вы загружаете в него текст, пользуясь методом/командой Lines.LoadfromFile. Компонент имеет внутренний предел в 32K. Если вы загружаете нужный файл в указатель, и используете команду/метод SetTexBuf, то в этом случае в TMemo можно загрузить текста вплоть до 64K. 

    NoteBook 

    Включение/Выключение закладки Notebook II

    Delphi 2 

    В обработчике события OnChange вашего TTabbedNotebook разместите код примерно такого содержания:

    if (NewTab = 0) and (IWantToDisableTab0) then AllowChange := False;

    if (NewTab = 1) and (IWantToDisableTab1) then AllowChange := False;

    Да, можно использовать конструкцию Case, но If в данном случае я посчитал удобнее.

    OutLine 

    Раскрытие пути к элементу TOutline по его индексу

    Delphi 1 

    Когда я писал этот код, у меня была цель по индексу TOutlineNode (который являлся результатом поиска) раскрыть его путь (т.е. раскрыть дочерние узлы, ведующие к нему), не затрагивая при это остальные узлы. 

    Следующая процедура в качестве параметра принимает индекс, после чего раскрывает путь к элементу с этим индексом. 

    Процедура подразумевает работу с объектом TOutline, имеющим имя Outline.

    var Outline: TOutline;

    procedure TSearchDlg.ExpandPathToFoundItem(const FoundItemIndex: Longint);

    {------------------------------------------------------------------------------

     Открываем путь к данному элементу (элемент определяется номером индекса).

     До корневого элемента необходимо раскрывать только родителей.

     -----------------------------------------------------------------------------}

    var

     ItemIndex: Longint;

     Found:     Boolean;

     LastCh:    Longint;

     Path:      String;

     ItemText:  String;

     SepPos:    Integer;

     OldSep:    String;

    begin

     {Сохраняем старый ItemSpearator}

     OldSep:=Outline.ItemSeparator;

     {Устанавливаем новый ItemSeparator}

     Outline.ItemSeparator:='\';

     {Получаем полный путь к TOutlineNode и добавляем '\'. Это делается для упрощения последующего алгоритма}

     Path:=Outline.Items[FoundItemIndex].FullPath+'\';

     {Зацикливаемся до тех пор, пока не будет достигнут конец пути}

     while Length(Path) > 0 do begin

      {Определяем в пути позицию первого '\'}

      SepPos:=Pos('\',Path);

      {Изолируем элемент TOutlineNode}

      ItemText:=Copy(Path,1,SepPos-1);

      {Определяем индекс TOutlineNode}

      ItemIndex:=Outline.GetTextItem(ItemText);

      {Раскрываем его}

      Outline.Items[ItemIndex].Expand;

      {Вырезаем из строки раскрытый TOutlineNode}

      Path:=Copy(Path,SepPos+1,Length(Path)-SepPos+1);

     end;

     {Восстанавливаем оригинальный ItemSeparator}

     Outline.ItemSeparator:=OldSep;

    end;

    Детали

    Давайте присвоим элементу желаемый путь:

    "My Computer\Hardware\SoundCard\Base Adress"

    На первом шаге возвращается приведенный выше путь. Затем изолируется подстрока «My Computer». Затем с помощью метода «GetTextItem» определяется индекс TOutlineNode «My Computer». Метод «Expand» раскрывает это дерево. Впоследствие «My Computer» вырезается из пути, и новым путем становится «Hardware\SoundCard\Base Adress».

    Затем определяется индекс «Hardware», раскрывается, и снова выразается. Данная процедура повторяется до тех пор, пока не останется пути, который можно раскрыть. После чего полностью раскрывается путь передаваемой TOutlineNode.

    PageControl 

    Динамические PageControl/TabSheet I

    Delphi 2 

    Динамическое создание Page Control'ов и Tab Sheet'ов:

    var

     T : TTabSheet;

     P : TPageControl;

    begin

     // Создаем PageControl

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

     P := TPageControl.Create(application);

     with P do begin

      Parent := Form1; // устанавливаем его как элемент управления формы.

      Top := 30;

      Left := 30;

      Width := 200;

      Height := 150;

     end; // with tpagecontrol

     // Создаем 3 страницы

     T := TTabSheet.Create(P);

     with T do begin

      Visible := True;  // Это необходимо, или форма не будет корректно перерисовываться

      Caption := 'Страница 1';

      PageControl := P; // Назначаем Tab в Page Control

     end; // with

     T := TTabSheet.Create(P);

     with T do begin

      Visible := True;  // Это необходимо, или форма не будет корректно перерисовываться

      Caption := 'Страница 2';

      PageControl := P; // Назначаем Tab в Page Control

     end; // with

     T := TTabSheet.Create(P);

     with T do begin

      Visible := True;  // Это необходимо, или форма не будет корректно перерисовываться

      Caption := 'Страница 3';

      PageControl := P; // Назначаем Tab в Page Control

     end; // with

     // Создаем 3 кнопки, 1 на страницу

     with tbutton.create(application) do begin

      Parent := P.Pages[0]; // «Указываем» кнопке родительскую страницу

      Caption := 'Привет, страница 1';

      Left := 0;

      Top := 0;

     end; // with

     with tbutton.create(application) do begin

      Parent := P.Pages[1]; // «Указываем» кнопке родительскую страницу

      Caption := 'Привет, страница 2';

      Left := 50;

      Top := 50;

     end; // with

     with tbutton.create(application) do begin

      Parent := P.Pages[2]; // «Указываем» кнопке родительскую страницу

      Caption := 'Привет, страница 3';

      Left := 100;

      Top :=  90;

     end; // with

     // Это должно быть сделано, или Tab первоначально не синхронизируется

     // с правильной страницей. Только в случае, если у вас более чем одна страница.

     P.ActivePage := P.Pages[1];

     P.ActivePage := P.Pages[0]; // Реально показываемая страница

    end;

    Динамические PageControl/TabSheet II

    В данном документе показана технология динамического добавления страниц компонента PageControl (объектов TTabSheet) к элементу управления Windows 95/NT PageControl (объект TPageControl). Оба этих объекта объявлены в модуле ComCtrls. Поэтому убедитесь в том, что ComCtrls указан в списке используемых модулей.

    Как динамически создать PageControl

    Прежде, чем мы приступим к динамическому созданию страниц, давайте динамически создадим PageControl (если он еще не на форме). Это делается посредством вызова конструктора TPageControl Create с параметром owner, равным Self. Конструктор Create возвращает объектную ссылку на вновь созданный объект PageControl и назначает его переменной 'PageControl'. Вторым шагом будет установка свойства PageControl Parent в Self. Свойство Parent определяет где должен быть отображен новый PageControl; в нашем случае это будет сама форма. Вот кусок кода, демонстрирующий вышесказанное:

    var

     PageControl : TPageControl;

     PageControl := TPageControl.Create(Self);

     PageControl.Parent := Self;

    Примечание: При разрушении формы разрушаются также PageControl и ее закладки, поскольку они принадлежат форме.

    Как динамически создавать закладки

    Существует два основных способа добавления новых страниц к PageControl. Сначала вы должны динамически создать TTabSheet следующим образом:

    var

     TabSheet : TTabSheet;

     TabSheet := TTabSheet.Create(Self);

    Затем ему необходимо присвоить заголовок следующей командой:

    TabSheet.Caption := 'Закладка 1';

    И, наконец, самая важное действие заключается в том, что новой странице необходимо сообщить, какому объекту PageControl она принадлежит. Это делается с помощью присваивания свойством TTabSheet PageControl переменной-ссылки TPageControl, типа той, которую мы создали выше (PageControl). Вот кусок кода, демонстрирующий вышесказанное:

    TabSheet.PageControl := PageControl;

    Как динамически добавлять к страницам элементы управления

    Ключевым моментом при создании и размещении элемента управления на странице TabSheet является назначение свойства Parent на переменную-ссылку класса TTabSheet. Вот пример:

    var

     Button : TButton;

     Button := TButton.Create(Self);

     Button.Caption := 'Кнопка 1';

     Button.Parent := TabSheet;

    Более подробно об объектах TPageControl и TTabSheet вы можете узнать в онлайн-документации, или посмотреть код файла ComCtrls.pas, расположенного в вашем каталоге ..\Delphi 2.0\SOURCE\VCL.

    Полный код примера

    // Код использует форму с единственной на ней кнопкой.

    unit DynamicTabSheetsUnit;


    interface


    uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons;


    type TForm1 = class(TForm)

     Button1: TButton;

     procedure Button1Click(Sender: TObject);

     procedure TestMethod(Sender: TObject);

    private

     { Private declarations }

    public

     { Public declarations }

    end;


    var Form1: TForm1;


    implementation


    uses ComCtrls;


    {$R *.DFM}


    procedure TForm1.Button1Click(Sender: TObject);

    var

     PageControl : TPageControl;

     TabSheet : TTabSheet;

    begin

     // Создаем PageControl

     PageControl := TPageControl.Create(Self);

     PageControl.Parent := Self;

     // Создаем первую страницу и связываем ее с PageControl

     TabSheet := TTabSheet.Create(Self);

     TabSheet.Caption := 'Закладка 1';

     TabSheet.PageControl := PageControl;

     // Создаем первую страницу

     with TButton.Create(Self) do begin

      Caption := 'Кнопка 1';

      OnClick := TestMethod;  // Назначаем обработчик события

      Parent := TabSheet;

     end;

     // Создаем вторую страницу и связываем ее с PageControl

     TabSheet := TTabSheet.Create(Self);

     TabSheet.Caption := ' Закладка 2';

     TabSheet.PageControl := PageControl;

    end;


    procedure TForm1.TestMethod(Sender: TObject);

    begin

     ShowMessage('Привет');

    end;

    end.

    Клавиши-акселераторы для TPageControl

    Delphi 2

    Тема: Создание акселераторов, работающих с TPageControl

    TPageControl, расположенный на закладке Win95 палитры компонентов, в настоящий момент не может работать с акселераторами. Тем не менее, в наших силах создать потомок TPageControl, поддерживающий вышеназванную характеристику.

    В приведенном ниже коде показана реализация такого компонента. Наследник TPageControl осуществляет захват и обработку сообщения CM_DIALOGCHAR. Это позволяет перехватывать комбинации клавиш, которые могут быть акселератороми для данной формы. Обработчик события CMDialogChar использует функцию IsAccel, которая позволяет определить, имеет ли отношение перехваченный код клавиш к акселератору одной из страниц TPageControl. В этом случае делаем страницу активной и передаем ей фокус.

    unit tapage;

    interface

    uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls;


    type TAPageControl = class(TPageControl)

    private

     procedure CMDialogChar(var Msg: TCMDialogChar); message CM_DIALOGCHAR;

    end;


    procedure Register;


    implementation


    procedure TAPageControl.CMDialogChar(var Msg: TCMDialogChar);

    var

     i: Integer;

     S: String;

    begin

     if Enabled then

      for I := 0 to PageCount - 1 do

       if IsAccel(Msg.CharCode, Pages[i].Caption) and Pages[I].TabVisible then begin

      Msg.Result := 1;

      ActivePage := Pages[I];

      Change;

      Exit; // выход из цикла.

     end;

     inherited;

    end;


    procedure Register;

    begin

     RegisterComponents('Test', [TAPageControl]);

    end;

    end.
     

    Panel 

    Создание панелей во время работы приложения

    Delphi 1 

    …я могу просто догадываться, не видя ваш код, но вы установили у панелей свойство parent? Чтобы отобразить элементы управления на вашей форме, вам НЕОБХОДИМО вставить в обработчик события формы OnCreate следующие две строки:

    MyPanel := TPanel.Create(Self);

    MyPanel.Parent := Self;
     

    PopupMenu 

    Вызов контекстного меню в позиции курсора II

    Delphi 1

    …вызов popup-меню связан с координатами экрана. Координаты, получаемые в вашем обрабочике события, вероятно относятся к объекту, который создал это сообщение. Для преобразования координат вам необходимо воспользоваться функцией ClientToScreen.

    Вот пример вызова контекстного меню, вызываемого при щелчке правой кнопкой мыши на узле TTreeView. Этот пример не в точности отвечает на ваш вопрос, но у меня нет желания расчитывать wParams прямо сейчас. Я думаю вы можете воспользоваться предложенной мною идеей и развить ее в нужном направлении.

    procedureTfrmExplorer.TreeViewMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

    var P : TPoint;

    begin

     if  Button<>mbRight then exit;

     TreeMenu.AutoPopup := False;

     if TreeView.GetNodeAt(X,Y)<>NIL then begin

      TreeView.Selected := TreeView.GetNodeAt(X,Y);

      P.X := X;

      P.Y:=Y;

      P := TreeView.ClientToScreen(P);

      TreeMenu.Popup(P.X,P.Y);

     end;

    end;

    Иконки в PopupMenu

    Delphi 2

    type TForm1 = class(TForm)

     MainMenu1: TMainMenu;

     File1: TMenuItem; /**** Элемент для Menu Bar ****/

     Open1: TMenuItem; /**** Элемент для Menu File ****/

     procedure FormCreate(Sender: TObject);

     procedure FormShow(Sender: TObject);

    private

     {private declarations}

    public

     {public declarations}

     Icn, Txt, MnuItm: TBitmap;

    end;


    procedure TForm2.FormCreate(Sender: TObject);

    var

     R: TRect;

     HIcn: HIcon;

     Ic: TIcon;

     Index: Word;

     FileName: PChar;

    begin

     /** Получаем иконку определенного приложения **/

     Ic:=TIcon.Create;

     Ic.Handle:=ExtractAssociatedIcon(Hinstance, /* задаем путь и имя файла */, Index);

     /** Создаем для текста изображение **/

     Txt:=TBitmap.Create;

     with Txt do begin

      Width:=Canvas.TextWidth(' Тест');

      Height:=Canvas.TextHeight(' Тест');

      Canvas.TextOut(0, 0, ' Тест');

     end;

     /** Копируем иконку в bitmap для изменения его размера. Вы не можете менять размер иконки **/

     Icn:=TBitmap.Create;

     with Icn do begin

      Width:=32;

      Height:=32;

      Brush.Color:=clBtnFace;

      Canvas.Draw(0, 0, Ic);

     end;

     /** Создаем окончательное изображение, куда мы помещаем иконку и текст **/

     MnuItm:=TBitmap.Create;

     with MnuItm do begin

      Width:=Txt.Width+18;

      Height:=18;

      with Canvas do begin

       Brush.Color:=clBtnFace;

       Pen.Color:=clBtnFace;

       Brush.Style:=bsSolid;

       Rectangle(0, 0, Width, Height);

       CopyMode:=cmSrcAnd;

       StretchDraw(Rect(0, 0, 16, 16), Icn);

       CopyMode:=cmSrcAnd;

       Draw(16, 8-(Txt.Height div 2), Txt);

      end;

     end;

    end;


    procedure TForm2.FormShow(Sender: TObject);

    var

     ItemInfo: TMenuItemInfo;

     hBmp1   : THandle;

    begin

     HBmp1:=MnuItm.Handle;

     with ItemInfo do begin

      cbSize     := SizeOf(ItemInfo);

      fMask      := MIIM_TYPE;

      fType      := MFT_BITMAP;

      dwTypeData := PChar(MakeLong(hBmp1, 0));

     end;

     /** Заменяем MenuItem Open1 законченным изображением **/

     SetMenuItemInfo(GetSubMenu(MainMenu1.Handle, File1.MenuIndex), Open1.MenuIndex, true, ItemInfo);

    end;

    В меню существуют некоторые проблемы масштабированием и палитрой иконки. Я также ищу лучшее решение, но это все, что я вам могу сейчас дать.

    Листинг был изменен для того, чтобы помещать иконки в «чЕкнутое» состояние меню (просто это делает Win95). Это позволяет вам иметь «чЕкнутое» и «нечЕкнутое» состояние.

    unit Unit1;


    interface


    uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Menus,ShellAPI;


    type TForm1 = class(TForm)

     MainMenu1: TMainMenu;

     File1: TMenuItem;

     Open1: TMenuItem;

     procedure FormCreate(Sender: TObject);

     procedure FormShow(Sender: TObject);

    private

     { Private declarations }

    public

     { Public declarations }

     Icn, MnuItm : TBitmap;

    end;


    var Form1: TForm1;


    implementation


    {$R *.DFM}


    procedure TForm1.FormCreate(Sender: TObject);

    var

     R: TRect;

     HIcn: HIcon;

     Ic: TIcon;

     Index: Word;

    begin

     { /** Получаем иконку некоторого приложения **/ }

     Index := 0;

     { 11-я иконка в файле }

     Ic:=TIcon.Create;

     Ic.Handle:=ExtractAssociatedIcon(Hinstance, 'c:\win95\system\shell32.dll', Index);

     { /** Копируем иконку в bitmap для изменения его размера. Вы не можете менять размер иконки **/ }

     Icn:=TBitmap.Create;

     with Icn do begin

      Width:=32;

      Height:=32;

      Canvas.Brush.Color := clbtnface;

      Canvas.Draw(0,0,Ic);

     end;

     { /** Создаем окончательное изображение, куда мы помещаем иконку и текст **/ }

     MnuItm:=TBitmap.Create;

     with MnuItm do begin

      Width :=18;

      Height:=18;

      with Canvas do begin

       Brush.Color:=clbtnface;

       Pen.Color:=clbtnface;

       CopyMode:=cmSrcAnd;

       StretchDraw(Rect(0,0,16,16), Icn);

      end;

     end;

    end;


    procedure TForm1.FormShow(Sender: TObject);

    var

     ItemInfo: TMenuItemInfo;

     hBmp1   : THandle;

    begin

     HBmp1:=MnuItm.Handle;

     with ItemInfo do begin

      cbSize        := SizeOf(ItemInfo);

      fMask         := MIIM_CHECKMARKS;

      fType         := MFT_BITMAP;

      hBmpunChecked := HBmp1; { Неотмеченное (Unchecked) состояние }

      hBmpChecked   := HBmp1; { Отмеченное (Checked) состояние }

     end;

     { /** Заменяем MenuItem Open1 законченным изображением **/ }

     SetMenuItemInfo(GetSubMenu(MainMenu1.Handle, File1.MenuIndex), Open1.MenuIndex, true, ItemInfo);

    end;

    end.

    ProgressBar 

    ProgressBar — невидимка

    Письмо читателя 

    Здравствуйте Валентин!

    Заказчик моего проекта обратился с просьбой — "Сделать прогресс индикатор как в приложениях Нортона. Чтоб был в статус строке и НИКАКИХ рамок". ProgressBar в StatusBar — нет проблем, но как быть с рамкой от ProgressBar? ProgressBar всегда вычерчивает рамку и не имеет методов ее управления. Однако появилась интересная идея, воплотившаяся в компонент с новым свойством ShowFrame. Решение оказалось на удивление простым.

    unit SProgress;


    interface

    uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls;


    type TVSProgressBar = class(TProgressBar)

     procedure WMNCPAINT(var Msg: TMessage); message WM_NCPAINT;

    private

     { Private declarations }

     FShowFrame: boolean;

     procedure SetShowFrame(Value: boolean);

    protected

     { Protected declarations }

    public

     { Public declarations }

     constructor Create(AOwner: TComponent); override;

    published

     { Published declarations }

     property Align;

     property Anchors;

     property BorderWidth;

     property DragCursor;

     property DragKind;

     property DragMode;

     property Enabled;

     property Hint;

     property Constraints;

     property Min;

     property Max;

     property Orientation;

     property ParentShowHint;

     property PopupMenu;

     property Position;

     property ShowFrame: boolean read FShowFrame write SetShowFrame;

     property ShowHint;

     property Smooth;

     property Step;

     property TabOrder;

     property TabStop;

     property Visible;

     property OnContextPopup;

     property OnDragDrop;

     property OnDragOver;

     property OnEndDock;

     property OnEndDrag;

     property OnEnter;

     property OnExit;

     property OnMouseDown;

     property OnMouseMove;

     property OnMouseUp;

     property OnStartDock;

     property OnStartDrag;

    end;


    procedure Register;


    implementation


    { TVSProgressBar }

    constructor TVSProgressBar.Create(AOwner: TComponent);

    begin

     Inherited;

     FShowFrame:= True;

    end;


    procedure TVSProgressBar.SetShowFrame(Value: boolean);

    begin

     if FShowFrame <> Value then begin

      FShowFrame:= Value;

      RecreateWnd;

     end;

    end;


    procedure TVSProgressBar.WMNCPAINT(var Msg: TMessage);

    var

     DC: HDC;

     RC: TRect;

    begin

     if ShowFrame then begin

      Inherited; // если рамка – родитель сам разберется

      Invalidate;

     end else begin

      DC := GetWindowDC(Handle);

      try

       Windows.GetClientRect(Handle, RC); // площадка под ProgressBar

       with RC do begin // учтем 3D эффект

        Right:= Right + 2;

        Bottom:= Bottom + 2;

       end;

       Windows.FillRect(DC, RC, Brush.Handle); // зальем площадку цветом подложки

      finally

       ReleaseDC(Handle, DC);

      end;

     end;

    end;


    procedure Register;

    begin

     RegisterComponents('Controls', [TVSProgressBar]);

    end;

    end.

    Теперь ProgressBar может появиться на форме «неожиданно», как бы из ничего, если ShowFrame:= False.

    C уважением, VS 

    Query 

    Можно ли использовать результаты выполнения одного TQuery для другого TQuery?

    Nomadic отвечает:

    Если Вы работаете с локальными БД, то Вам поможет –

    DbiMakePermanent(SourceQuery.Handle, RName, false);
     

    Можно ли вызвать хранимую процедуру через TQuery, если она не возвращает курсора?

    Nomadic отвечает:

    В случае MS SQL нужно написать:

    Query1.Sql := 'declare @res' + #13#10 + 'exec MyFunc :Param1, :Param2, @res OUTPUT';

    Query1.Open;

    Result := Query1.FieldByName( 'Column1' ).Value;

    Query1.Close;
     

    TQUERY и TDBGRID

    Delphi 1 

    1. После ключевого слова where используйте оператор order

    Select fname, lname, title

    from T_EMPLOYEE

    where title = 'MGR'

    order by lname, fname

    2. Попробуйте использовать событие ColEnter. 

    Две и более команд в свойстве TQUERY.SQL

    Delphi 1 

    Я предлагаю вас попытаться подключить новый запрос к существующему TQuery.

    Query1.Sql.Clear;

    Query1.Close;

    Query1.Sql.Add('select * from «monitor.dbf» order by location,dept');

    Query1.Open;

    Query1.Refresh;

    Хитрость кроется в закрытии вашего запроса перед назначением нового. 

    RichEdit 

    Как вставить в нужное место Rich Text в TRichEdit?

    Nomadic советует:

    Вы можете послать сообщение EM_STREAMIN с параметром SFF_SELECTION методом Perform для замены текущего Selection. Выдержка из Help:

    EM_STREAMIN

    wParam = (WPARAM)(UINT) uFormat; // Integer

    lParam = (LPARAM)(EDITSTREAM FAR *) lpStream; // EDITSTREAM^

    The EM_STREAMIN message replaces the contents of a rich edit control with the specified data stream.

    Parameters

    uFormat

    One of the following data formats, optionally combined with the SFF_SELECTION flag:

    Value Meaning
    SF_TEXT Text
    SF_RTF Rich-text format

    If the SFF_SELECTION flag is specified, the stream replaces the contents of the current selection. Otherwise, the stream replaces the entire contents of the control.

    lpStream

    Pointer to an EDITSTREAM structure. The control reads (streams in) the data by repeatedly calling the function specified by the structure's pfnCallback member.

    Return Value

    Returns the number of characters read. 

    Как указать максимальный размер текста для TRichEdit?

    Nomadic советует:

    У этого компонента есть свойство MaxLength, которое работает некорректно. Поэтому лучше пользоваться

    RichEdit.Perform(EM_LIMITTEXT, нужный размер, 0);

    Причем перед каждом открытии файла это действие необходимо повторять.

    Если Вы передаете в качестве размера 0, то ОС ограничивает размер OS Specific Default Value. Реально, по результатам моих экспериментов, поставить можно размер, чуть меньший доступной виртуальной памяти. Я ограничился 90% от свободной виртуалки.

    Для того, чтобы не повторять этот вызов (EM_LIMITTEXT), можно воспользоваться сообщением EM_EXLIMITTEXT. 

    Позиция курсора в TRichEdit

    Delphi 2

    Procedure TForm1.GetPosition(Sender: TRichEdit);

    var

     iX, iY: Integer;

     TheRichEdit: TRichEdit;

    begin

     iX:= 0;

     iY:= 0;

     TheRichEdit:= TRichEdit(Sender);

     iY:= SendMessage(TheRichEdit.Handle, EM_LINEFROMCHAR, TheRichEdit.SelStart, 0);

     iX:= TheRichEdit.SelStart - SendMessage(TheRichEdit.Handle, EM_LINEINDEX, iY, 0);

     Panel1.Caption:= IntToStr(iY + 1) + ':' + IntToStr(iX + 1);

    end;


    procedure TForm1.RichEditMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

    begin

     GetPosition(RichEdit);

    end;


    procedure TForm1.RichEditKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);

    begin

     GetPosition(RichEdit);

    end;
     

    RadioGroup 

    Группа радиокнопок и ActiveControl

    На форме я имею группу радиокнопок. Я хотел бы вызывать контекстно-зависимую подсказку, если пользователь нажал F1. Для данной конкретной группы радиокнопок я установил HelpContext равным 22, но при любом вызове ActiveControl.HelpContext это возвращает (0). Все другие элементы управления работают как положено. Что я делаю неправильно?

    Нет. Проблема в том, что ActiveControl – RadioButton, а не RadioButtonGroup. Поместите следующий код в обработчик события формы OnShow, он должен решить вашу проблему:

    procedure TForm1.FormShow(Sender: TObject);

    var c: integer;

    begin

     with RadioGroup1 do begin

      for c := 0 to ControlCount – 1 do TRadioButton(Controls[c]).HelpContext := HelpContext;

     end;

    end;

    – Ralph Friedman 

    ScrollBar 

    Мерцание ScrollBar

    TScrollBar в Delphi мигает при получении фокуса. Как избежать этого мерцания?

    Такая же проблема и при перемещении стандартного бегунка полосы прокрутки. Лечится одинаково: установкой свойства TabStop в False.

    – Rick Rogers 

    SpeedButton 

    Speedbutton и Glyph

    Могу ли я из ресурсов поочередно загружать глифы для кнопок speedbutton и, если да, то как это сделать?

    Например, если в вашем проекте используется TDBGrid, то иконки кнопок компонента DBNavigator могут линковаться вашей программой, и их можно загрузить для использования в ваших speedbutton следующим образом:

    SpeedButton.Caption := '';

    SpeedButton1.Glyph.LoadFromResourcename(HInstance,'DBN_REFRESH');

    SpeedButton1.NumGlyphs := 2;

    Другие зарезервированные имена:

    DBN_PRIOR, DBN_DELETE, DBN_CANCEL, DBN_EDIT, DBN_FIRST, DBN_INSERT, DBN_LAST, DBN_NEXT, DBN_POST

    Все имена должны использовать верхний регистр.

    – Dennis Passmore 

    StringGrid 

    Обновление картинки в ячейке StringGrid

    SottNick советует:

    Если в таблице вы используете событие OnDrawCell для помещения в ячейку рисунка, причем различного, в зависимости, например, от соответствующего значения в двумерном массиве, и вам надо, чтобы после изменения значения в массиве обновилось изображение (Refresh не подходит, т.к. будет мелькать), то измените значение у ячейки (DrawGrid не годится):

    StringGrid1.Cells[i,j]:='';

    или

    StringGrid1.Cells[i,j]:=StringGrid1.Cells[i,j];

    если там что-то хранится.

    Многострочность в заголовках колонок StringGrid


    У меня есть StringGrid, который выглядит очень красивым, за исключением заголовков колонок, где я хотел бы иметь их размер равным 1 ячейке, но с заголовком, размещенным в нескольких строках, например,

    Индекс Фондовой Биржи

    показывалось бы как

     Индекс

     Фондовой

     Биржи

    было бы классно, если можно было этот заголовок размещать еще и по центру.

    Рисовать сами ячейки вы можете в обработчике события OnDrawCell. Для определения ячейки (заголовок?), обрабатываемой в текущий момент, используйте параметр GridState.

    Я выводил тест с помощью обычных методов рисования (которые хорошо "приживаются" в данном компоненте), с поддержкой вертикального выравнивания, полей и переноса слов. Вот сам код:

    TFTVerticalAlignment = (vaTop, vaMiddle, vaBottom);

    procedure DrawTextAligned(const Text: string; Canvas: TCanvas; var Rect: TRect; Alignment: TAlignment; VerticalAlignment: TFTVerticalAlignment; WordWrap: Boolean);

    var

     P : array[0..255] of Char;

     H : Integer;

     T : TRect;

     F : Word;

    begin

     StrPCopy(P, Text);

     T := Rect;

     with Canvas, Rect do begin

      F := DT_CALCRECT or DT_EXPANDTABS or DT_VCENTER or TextAlignments[Alignment];

      if WordWrap then F := F or DT_WORDBREAK;

      H := DrawText(Handle, P, -1, T, F);

      H := MinInt(H,Rect.Bottom - Rect.Top);

      if VerticalAlignment = vaMiddle then begin

       Top := ((Bottom+Top) - H) div 2;

       Bottom := Top + H;

      end else if VerticalAlignment = vaBottom then Top := Bottom - H - 1;

      F := DT_EXPANDTABS or DT_VCENTER or TextAlignments[Alignment];

      if WordWrap then F := F or DT_WORDBREAK;

      DrawText(Handle, P, –1, Rect, F);

     end;

    end;

    – Rick Roger

    StringGrid без выделенной ячейки


    Я пытаюсь показать StringGrid без выделенной ячейки. Первая нефиксированная ячейка всегда имеет состояние "инвертированного" цвета. Я не хочу позволить пользователю редактировать сетку, но эта выделенная ячейка производит впечатление того, что сетка имеет возможность редактирования…

    Вам необходимо создать обработчик события OnDrawCell. Это легче чем вы думаете. Вот образец кода, который сделает вас счастливым:

    procedure TForm.sgrDrawCells(Sender: TObject; Col, Row: Longint; Rect: TRect; State: TGridDrawState);

    var

     ACol: longint absolute Col;

     ARow: longint absolute Row;

     Buf: array[byte] of char;

    begin

     if State = gdFixed then Exit;

     with sgrGrid do begin

      Canvas.Font := Font;

      Canvas.Font.Color := clWindowText;

      Canvas.Brush.Color := clWindow;

      Canvas.FillRect(Rect);

      StrPCopy(Buf, Cells[ACol,ARow]);

      DrawText(Canvas.Handle, Buf, -1, Rect, DT_SINGLELINE or DT_VCENTER or DT_NOCLIP or DT_LEFT);

     end;

    end;

    – Jeff Fisher 

    Один щелчок на StringGrid вместо трех

    Как сделать так, чтобы после ПЕРВОГО щелчка на ячейке возможно было бы начать редактировать ее содержимое?

    Включите goAlwaysShowEditor в свойство TStringGrid Options.

    – Rick Rogers

    StringGrid как DBGrid

    Delphi 1

    Ну это может выглядеть приблизительно так (возможно нужна некоторая доработка, написал от руки, не проверяя):

    table.first;

    row := 0;

    grid.rowcount := table.recordCount;

    while not table.eof do begin

     for i := 0 to table.fieldCount-1 do

      grid.cells[i,row] := table.fields[i].asString;

     inc(row);

     table.next;

    end;

    У меня тоже имееются свои причины использования TStringGrid. Вот мой код, который загружает данные из отфильтрованной таблицы. Он не очень изящен, т.к. реально является лишь черновиком. У меня это работает, а большего мне и не нужно. Работает очень быстро, даже в случае сотни загруженных колонок. Есть много ссылок на внешние переменные. Надеюсь что они не слишком заумные.

    PROCEDURE TformLookupDB.FillCells;

    VAR

     Row, i :INTEGER

     w      :INTEGER

     grid   :TStringGrid

    BEGIN

     doGrid.RowCount := 0;

     IF NOT ASSIGNED(fDB) THEN EXIT;

     Row := 0;

     FOR i := LOW(fColWidths) TO HIGH(fColWidths) DO fColWidths[i] := 100

     // Данный временный объект-сетка используется для предохранения от огромного

     // количества подразумеваемых событий Application.ProcessMessages,

     // инициируемых базой данных, и вызывающих противное моргание объекта

     // doGrid. Итак, мы загружаем данные в объект-сетку

     // и затем копируем их в стобцы, начиная с верхней части.

     grid := TStringGrid.Create(Self);

     grid.Visible := FALSE;

     WITH fDB DO TRY

      grid.ColCount := fFields.Count;

      DisableControls;

      // Фильтр был установлен с помощью свойства Self.Filter

      First;

      WHILE NOT EOF DO TRY

       grid.RowCount := Row+1;

       FOR i := 0 TO grid.ColCount-1 DO BEGIN

        grid.Cells[i,Row] :=FieldByName(fFields.Strings[i]).AsString

        w := doGrid.Canvas.TEXTWIDTH(grid.Cells[i,Row]);

        IF fColWidths[i]<w THEN fColWidths[i] := w;

       END

       INC(Row);

      FINALLY

       Next;

      END

     FINALLY

      doGrid.RowCount := grid.RowCount;

      doGrid.ColCount := grid.ColCount;

      FOR i := 0 TO grid.ColCount-1 DO BEGIN

       doGrid.Cols[i] := grid.Cols[i];

       doGrid.ColWidths[i] := fColWidths[i] + 4

      END

      grid.Free;

      EnableControls

     END

    END;
     

    `Авторазмер` для StringGrid

    …да, реально это утомляет, но эту проблему можно решить программным путем (это нужно делать после того, как вы загрузите данные, или же, если вы загружаете данные по столбцам, их загружать в самом цикле, приведенном ниже):

    i, j, temp, max: integer;

    for i := 0 to grid.colcount-1 do begin

     max := 0;

     for j := 0 to grid.rowcount-1 do begin

      temp := grid.canvas.textWidth(grid.cells[i,j]);

      if temp > max then max := temp;

     end;

     grid.colWidths[i] := max + grid.gridLineWidth +1;

    end;

    Вероятно, вам необходимо будет добавить +1, чтобы текст не прилипал к границам ячеек.

    Выравнивание колонок StringGrid III

    Вот некоторый код, который делает то, что вы хотите:

    procedure WriteText(ACanvas: TCanvas; const ARect: TRect; DX, DY: Integer; const Text: string; Format: Word);

    var

     S: array[0..255] of Char;

     B, R: TRect;

    begin

     with ACanvas, ARect do begin

      case Format of

      DT_LEFT:

       ExtTextOut(Handle, Left + DX, Top + DY, ETO_OPAQUE or ETO_CLIPPED,@ARect, StrPCopy(S, Text), Length(Text), nil);

      DT_RIGHT:

       ExtTextOut(Handle, Right - TextWidth(Text) - 3, Top + DY,ETO_OPAQUE or ETO_CLIPPED, @ARect, StrPCopy(S, Text),Length(Text), nil);

      DT_CENTER:

       ExtTextOut(Handle, Left + (Right - Left - TextWidth(Text)) div 2, Top + DY, ETO_OPAQUE or ETO_CLIPPED, @ARect,StrPCopy(S, Text), Length(Text), nil);

      end;

     end;

    end;


    procedure TBEFStringGrid.DrawCell(Col, Row: Longint; Rect: TRect; State: TGridDrawState);

    var


     procedure Display(const S: string; Alignment: TAlignment);

     const Formats: array[TAlignment] of Word = (DT_LEFT, DT_RIGHT, DT_CENTER);

     begin

      WriteText(Canvas, Rect, 2, 2, S, Formats[Alignment]);

     end;


    begin

     { здесь задаем аргументы Col и Row, и форматируем как угодно ячейки }

     case Row of

     0: { Центрирование заголовков колонок }

      if (Col < ColCount) then Display(Cells[Col,Row], taCenter)

      else

       { Все другие данные имеют правое центрирование }

       Display(Cells[Col,Row], taRight);

     end;

    end;
     

    Выравнивание колонок StringGrid IV

    Delphi 1 

    Создайте ваш собственный метод drawcell на примере того, что приведен ниже:

    procedure Tsearchfrm.Grid1DrawCell(Sender: TObject; Col, Row: Longint; Rect: TRect; State: TGridDrawState);

    var l_oldalign : word;

    begin

     if (row=0) or (col<2) then grid1.canvas.font.style:= grid1.canvas.font.style+[fsbold]; {устанавливаем заголовок в жирном начертании}

     if col<>1 then begin

      l_oldalign:=settextalign(grid1.canvas.handle, ta_right);

      {NB использует для рисования правую сторону квадрата}

      grid1.canvas.textrect(rect,rect.right-2, Rect.top+2,grid1.cells[col,row]);

      settextalign(grid1.canvas.handle,l_oldalign);

     end else begin

      grid1.canvas.textrect(rect, rect.left+2, rect.top+2, grid1.cells[col,row]);

     end;

     grid1.canvas.font.style:= grid1.canvas.font.style-[fsbold];

    end;

    Покрашенный StringGrid I

    Delphi 1

    …вы можете попробовать использовать StringGrid. У него имеется свойство Objects, через которое вы можете назначать объекты. Создайте объект, содержащий переменную типа TColor, и назначьте это Objects[col,row], что позволит иметь к нему доступ в любое время. Назначьте событие OnDrawCell StringGrid, позволяющее рисовать текст ячейки правильного цвета. Чтобы убедиться, что ячейка выбрана, воспользуйтесь свойством Selection, содержащим то, что выбрал пользователь. Все это должно выглядеть приблизительно так:

    type TStrColor = class(TObject)

    public

      Color : TColor; {вы могли бы также определить частные и публичные методы доступа}

    end;

    procedure TForm1.FormCreate(Sender:TObject)

    var i,j : Integer;

    begin

     With StringList1 do

      for i := 0 to ColCount-1

       for j := 0 to RowCount-1 Objects[i,j] := TStrColor.Create;

    end;

    procedure TForm1.StringGrid1DrawCell(Sender: TObject; Col, Row: Longint; Rect: TRect; State: TGridDrawState);

    var OldColor : TColor;

    begin

     with StringGrid1.Canvas do begin

      OldColor := Font.Color;

      Font.Color := (StringGrid1.Objects[col,row] as TStrColor).Color;

      TextOut(Rect.Left+2, Rect.Top+2, StringGrid1.Cells[Col,Row]);

      Font.Color := OldColor;

     end;

    end;

    procedure TForm1.ProcessSelection(Sender: TObject);

    var i, j : Integer;

    begin

     With StringGrid1.Selection do

      For i := left to right do

       for j := top to bottom do

        MessageDlg(IntToStr(i) + ',' + IntToStr(j) + '-' + IntToStr((StringGrid1.Objects[i,j] as tstrcolor).color), mtInformation, [mbOk], 0);

    end;

    Этот компонент не позволяет делать многочисленный выбор….

    Покрашенный StringGrid II

    Delphi 1

    В данном модуле демонстрируется техника изменения цвета у выводимого в StringGrid текста.

    unit Strgr;


    interface


    uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, Grids, StdCtrls, DB;


    type TForm1 = class(TForm)

     StringGrid1: TStringGrid;

     procedure StringGrid1DrawCell(Sender: TObject; Col, Row: Longint; Rect: TRect; State: TGridDrawState);

    private

     { Private declarations }

    public

     { Public declarations }

    end;


    var Form1: TForm1;


    implementation


    {$R *.DFM}


    procedure TForm1.StringGrid1DrawCell(Sender: TObject; Col, Row: Longint; Rect: TRect; State: TGridDrawState);

    const CharOffset = 3;

    begin

     with StringGrid1.canvas do begin

      font.color := clMaroon;

      textout(rect.left + CharOffset, rect.top + CharOffset, 'L');

      font.color := clNavy;

      textout(rect.left + CharOffset + TextWidth('L'), rect.top + CharOffset, 'loyd');

     end;

    end;

    end.
     

    Редактирование в StringGrid

    Delphi 1 

    …правда, я этого не пробовал, но в голову пришли две идеи:

    1. Нажмите на втором поле редактировании, переведите фокус на другое поле (например, x.focus, где x не сетка), сбросьте goEditing и selectRow и затем верните фокус назад сетке. (Эта техника работала у меня в нескольких местах, например, в градах и мемах.)

    2. Нажмите на втором поле редактирования, и после сброса goEditing и selectRow, попробуйте создать tGridRect, подсвечивающий нужную вам строку, после чего делайте grid.Selection := gridRect; 

    Tabbednotebook 

    Tabbednotebook и куча ресурсов

    Тема: Как избежать использования кучи ресурсов (Resource Heap) при работе с TabbedNotebook

    Данный документ расскажет о том, как с помощью Object Pascal можно управлять числом активных handlesWindows (оконных дескрипторов), в особенности кучей ресурсов пользователя (User Resource heap), а также следить за этими показателями. О чем этот документ? Попробую коротко и доходчиво: Windows следит за каждым элементом, имеющим фокус, через его дескриптор (Handle). Исходя из этого, Windows не может одновременно поддерживать несколько оконных дескрипторов (4-байтных указателей), и в этом совете мы приведем простой пример кода, позволяющего «легко» загружать ресурсы и обходить эти ограничения, встающие перед разработчиками Delphi.

    USER DLL в действительности является библиоткой, распределяющей и поддерживающей ресурсы для всех окон и связанных структур данных, включая элементы управления, имеющие фокус, и другие неупомянутые объекты, но вместе с тем необходимо помнить, что эта библиотека работает под Windows. С этим связаны ограничения при работе с ресурсами USER DLL, и эта та проблема, над которой мы будем работать в этом совете. Данный пример добавляет загрузку ресурса для каждого элемента управления, добавляемого на форму, здесь мы берем 4 байта из кучи USER в 64K[1].

    Почему мы уверены в том, что у нас это получится? Мы будем разрушать[2] дескрипторы окон, которые Windows, согласно своей архитектуре, должна помнить. Разрушая эти дескрипторы, мы, таким образом, избегаем освобождения пользовательских (USER) ресурсов, это означает, что нам не нужно будет снова создавать вышеуказанные объекты. Наоборот, текущая архитектура VCL обладает способностью следить за вышеуказанными объектами, которые, в действительности, являются указателями на структуру. Так, зная, что VCL поддерживает дескриптор и windows создаст новый дескриптор КАК ТРЕБУЕТСЯ, то вместо поддержания постоянно одного дескриптора (как это подразумевалось при создании архитектуры Windows), мы можем управлять пользовательскими (USER) ресурсами вручную, позволяя разработчику легко загружать их по мере необходимости. 

    Данный пример демонстрирует работу с дескрипторами пользовательских (USER) ресурсов компонента Delphi TTabbedNoteBook (в части освобождения дескрипторов страниц), Delphi DestroyHandle (процедура TWinControl для удаления пользовательских (USER) дескрипторов), и работу вызова Windows API LockWindowUpdate (блокировка нежелательной перерисовки).

    Технология освобождения дескриптора страницы TTabbedNoteBook может работать и с любыми потомками TWinControl. TWinControl – класс предка, который умеет создавать и разрушать оконные дескрипторы; CreateHandle & DestroyHandle.

    Демонстрационный код

    Следующий код с приведенными обработчиками событий является «отрывком» из большого проекта с компонентами TTimer, TTabbedNotebook (с множеством страниц) и большим разнообразием визуальных элементов управления на каждой странице компонента. (Позже мы подчеркнем преимущества кода, приведенного ниже, перед его добавлением в ваш проект) Приведенный код должен располагаться соответственно в обработчиках событий OnTimer компонента TTimer и OnChange компонента TTabbedNotebook. Вот каким должен быть ваш новый код:

    <Модуль с объявленными в нем TTabbedNotebook и TTimer>

    Implementation

    Type TSurfaceWin = class(TWinControl);


    procedure TForm1.Timer1Timer(Sender: TObject);

    begin

     {Данный код заменяет заголовок формы на системную информацию,

     содержащую в процентах free SYSTEM, GDI, &USER для windows.}

     caption := 'SYSTEM: ' +

      inttostr(getfreesystemresources(GFSR_SYSTEMRESOURCES)) + ' GDI: ' +

      inttostr(getfreesystemresources(GFSR_GDIRESOURCES)) + ' USER: ' +

      inttostr(getfreesystemresources(GFSR_USERRESOURCES));

    end;


    procedure TForm1.TabbedNotebook1Change(Sender: TObject; NewTab: Integer; var AllowChange: Boolean);

    begin

     {LockWindowUpdate запрещает перерисовку данного окна}

     LockWindowUpdate(handle);

     {Причина использования TSurfaceWin в том, что вызов DestroyHandle в TWinControl объявлен как абстрактный, поэтому данный вызов возможен только его потомками, реализовавшими данную процедуру. Следующая строка читает индекс текущей страницы TabbedNotebook и разрушает ее дескриптор при перемещении на другую страницу. ПРИМЕЧАНИЕ: Даже если мы уничтожаем дескриптор, Windows помнит страничный объект и переназначает/создает новый при нажатии на другой закладке. }

     TSurfaceWin(TabbedNotebook1.pages.objects[tabbedNotebook1.pageindex]).DestroyHandle;

     {Выключаем блокировку формы, чтобы любой элемент управления мог перерисовывать себя}

     LockWindowUpdate(0);

    end;

    Доступ к страницам Tabbednotebook

    Delphi 1

    При добавлении компонентов во время выполнения программы, вам необходимо присвоить для каждого компонента свойству parent (контейнер) _страницу_ компонента notebook, а не сам notebook.

    Вы можете сделать это следующим образом (пример дан для кнопки):

    MyButton := TButton.Create(Form1);  {как обычно…}

    MyButton.Parent := TTabPage(TabbedNotebook1.Pages.Objects[n]);

    { <== где 'n' – индекс желаемой страницы ==> }

    Свойство notebook 'Pages' имеет тип StringList и содержит список заголовков и объектов 'TTabPage'.

    Я сам пользовался этой техникой несколько месяцев. Не могу вспомнить где я сам раздобыл эту информацию, но в документации про это ничего не сказано. Может кто-нибудь знает, где об этом написано?

    При добавлении компонента на страницу TabbedNotebook во время выполнения приложения, указатель на желаемую страницу для свойства Parent нового компонента должен быть назначен перед тем, как он будет реально показан. Способ получить доступ ко всем страницам TTabbedNotebook во время выполнения программы – с помощью свойства-массива Objects свойства TabbedNotebook Pages. Другими словами, страничные компоненты хранятся как объекты, присоединенные к имени страницы в списке строк свойства Pages. В следующим коде показано создание кнопки на второй странице компонента TabbedNotebook1:

    var NewButton : TButton;

    begin

     NewButton := TButton.Create(Self);

     NewButton.Parent := TWinControl(TabbedNotebook1.Pages.Objects[1])

     …

    Вот как страница TNotebook может быть использована в качестве родителя для вновь создаваемого на ней компонента:

    NewButton.Parent := TWinControl(Notebook1.Pages.Objects[1])

    Вот как страница (закладка) TTabSet может быть использована в качестве родителя для вновь создаваемого на ней компонента:

    NewButton.Parent := TWinControl(TabSet1.Tabs.Objects[1])
     

    TabControl 

    Над какой закладкой курсор?

    YoungHacker советует:

    Получение позиции мышиного курсора для TabControl над какой закладкой находится курсор.

    function Form1.ItemAtPos(TabControlHandle : HWND; X, Y : Integer) : Integer;

    var

     HitTestInfo : TTCHitTestInfo;

     HitIndex : Integer;

    begin

     HitTestInfo.pt.x := X;

     HitTestInfo.pt.y := Y;

     HitTestInfo.flags := 0;

     HitIndex := SendMessage(TabControlHandle, TCM_HITTEST, 0, Longint(@HitTestInfo));

     Result := HitIndex;

    end;
     

    Table 

    Создание таблицы в модуле

    Delphi 3 

    Объект TTable может быть создан с владельцем, а может и без оного. Поскольку вы объявляете его локально в процедуре, то владелец в этом случае не требуется. Если владелец не задан, то забота об освобождении объекта ложится на вас. В противном случае объект освобождается владельцем всякий раз, когда освобождается сам владелец. Имеет смысл? Чтобы создать таблицу без владельца, сделайте следующее:

    procedure CreateATableInAUnit;

    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;
     

    TabSet 

    Изменение количества закладок в TTabSet во время выполнения программы

    Delphi 1 

    Вначале сделайте где-то в вашем коде следующее объявление:

    TabSet1: TTabSet; { подразумевается, что это принадлежит Form1 }

    Затем следующей строкой мы очищаем заголовки всех закладок:

    Form1.TabSet1.Tabs.Clear;

    Для того, чтобы добавить новую закладку с определенным именем, воспользуйтесь следующим кодом:

    Form1.TabSet1.Tabs.Add('какой-то заголовок');

    Пожалуйста, помните о том, что я назначил имя в предположении, что вы имеете ссылку на юнит, где оно определено [но не исключаю возможности, что вы можете получить ссылку на закладку и через обработчика соответствующего события, что еще проще, но мы то с вами должны знать все!]. Если вам нужно сослаться на объект из другого модуля, просто добавьте к вызову имя модуля (и добавьте этот модуль в список «uses»), например так:

    Unit1.Form1.TabSet1.Tabs.Add('Заголовок');

    Поскольку «TabSet1.Tabs» имеет тип TStrings, вы можете использовать любой из доступных методов этого типа (AddObject, LoadFromFile и т.д.). 

    Timer 

    Остановка таймера на `полпути`

    Delphi 1

    Timer1.Enabled := False;

    Timer1.Enabled := True;

    Это полностью «сбрасывает» таймер, другими словами, перезапускает его.

    BTW: изменение интервала (в другое значение) также производит сброс таймера.

    Вы можете включать и выключать ваш таймерный компонент, устанавливая соответствующее свойство, например:

    Timer1.Enabled := True; { или False, если вы хотите выключить его }

    Но при этом свои 5 секунд таймер продолжает отсчитывать. Если вы хотите изменить это, присвойте ему другой интервал, например так:

    Timer1.Interval := 100;

    TreeView 

    Поточность TreeView

    Delphi 2

    На пустой форме у меня располагается TTreeView. Затем я сохраняю это в файле, используя WriteComponent. Это работает как положено; я могу из DOS c помощью команды "type" посмотреть двоичный файл и увидеть его содержимое, типа строк TTreeView и имя объекта. По крайней мере файл записывается и создается впечатление его "наполненности".

    Затем я освобождаю компонент TTreeView, открываю поток, делаю ReadComponent и, затем, InsertControl. И получаю исключение "TreeView1 has no parent window" (TreeView1 не имеет родительского окна).

    Это происходит из-за того, что при установке определенных свойств TreeView требуется дескриптор окна элемента управления, а для этого необходимо иметь родителя. Решение заключается в создании пустого TreeView и передаче его в качестве параметра ReadComponent - вы наверняка меня спросите, почему ReadComponent необходим параметр, правильно? Смотрите дальше.

    procedure TForm1.Button1Click(Sender: TObject);

    var TreeView : TTreeView;

    begin

     with TFileStream.Create('JUNK.STR', fmCreate) do try

      WriteComponent(TreeView1);

      TreeView1.Name := 'TreeView';

      Position := 0;

      TreeView := TTreeView.Create(Self);

      TreeView.Visible := false;

      TreeView.Parent := Self;

      ReadComponent(TreeView);

      TreeView.Top := TreeView1.Top + TreeView1.Height + 10;

      TreeView.Visible := true;

     finally

      Free;

     end;

    end;

    Два небольших замечания:

    1. Убедитесь в отсутствии конфликта имен. Данный код делает форму владельцем второго TreeView и при ее освобождении разрушает компонент. Я просто переименовываю существующий TreeView перед загрузкой 'клона'.

    2. Я установил свойство visible в false перед установкой свойства parent, этим я предотвратил показ только что созданного TreeView до момента загрузки его из потока.

     – Mike Scott

    Получение доступа к узлам TreeView

    Delphi 2

    Небольшие хитрости для работы с узлами TreeView:

    Если вы хотите производить поиск по дереву, может быть для того, чтобы найти узел, соответствующий определенному критерию, то НЕ ДЕЛАЙТЕ ЭТО ТАКИМ ОБРАЗОМ:

    for i := 0 to MyTreeView.Items.Count) do begin

     if MyTreeView.Items[i].Text = 'Банзай' then break;

    end;

    …если вам не дорого время обработки массива узлов.

    Значительно быстрее будет так:

    Noddy := MyTreeView.Items[0];

    Searching := true;

    while (Searching) and (Noddy <> nil) do begin

     if Noddy.text = SearchTarget then begin

      Searching := False;

      MyTreeView.Selected := Noddy;

      MyTreeView.SetFocus;

     end else begin

      Noddy := Noddy.GetNext

     end;

    end;

    В моей системе приведенный выше код показал скорость 33 милисекунды при работе с деревом, имеющим 171 узел. Первый поиск потребовал 2.15 секунд.

    Оказывается, процесс индексирования очень медленный. Я подозреваю, что при каждой индексации свойства Items, вы осуществляете линейный поиск, но это нигде не засвидетельствовано, поэтому я могу ошибаться.

    Вам действительно не нужно просматривать все дерево, чтобы найти что вам нужно – получить таким образом доступ к MyTreeView.Items[170] займет много больше времени, чем получения доступа к MyTreeView.Items[1].

    Как правило, для отслеживания позиции в дереве TreeView, нужно использовать временную переменную TTreeNode, а не использовать целочисленные индексы. Возможно, свойство ItemId как раз и необходимо для такого применения, но, к сожалению, я никак не могу понять абзац в электронной документации, касающийся данного свойства:

    «Свойство ItemId является дескриптором TTreeNode типа HTreeItem и однозначно идентифицирует каждый элемент дерева. Используйте это свойство, если вам необходимо получить доступ к элементам дерева из внешнего по отношению к TreeView элемента управления.»

    «Я разговаривал с деревьями…вот почему они ушли от меня…»

    ((Spike Milligan))

    – Peter Kane 

    Хочется выделять некоторые стpочки в TTreeView жирным или бледным. Как?

    Nomadic советует:

    Грхм… Господа, но если речь про bold… Матчасть учить надо 8-).

    procedure SetNodeState(node: TTreeNode; Flags: Integer);

    var tvi: TTVItem;

    begin

     FillChar(tvi, Sizeof(tvi), 0);

     tvi.hItem := node.ItemID;

     tvi.mask := TVIF_STATE;

     tvi.stateMask := TVIS_BOLD or tvis_cut;

     tvi.state := Flags;

     TreeView_SetItem(node.Handle, tvi);

    end;

    И вызываем:

    SetNodeState(TreeView1.Selected, TVIS_BOLD); // Текст жирным

    SetNodeState(TreeView1.Selected, TVIS_CUT); // Иконку бледной

    (Ctrl+X)

    SetNodeState(TreeView1.Selected, TVIS_BOLD or TVIS_CUT); // Текст жирным

    SetNodeState(TreeView1.Selected, 0); // Ни того, ни другого

    Когда-то (мечтательно закатив глаза в потолок) в API было еще и TVIS_DISABLE. Снесли собаки. А рекомендуемую стилистику употребления этого добра смотри в MS Internet News. 

    UpdateSQL 

    Что нужно знать о принципе и порядке работы с TUpdateSQL для работы с неживыми запросами?

    Nomadic советует:

    Кидаешь UpdateSQL на форму, после чего в том SQL, который ты собираешься редактировать, устанавливаешь в UpdateObject имя этого UpdateSQL. После этих дел по дабл-клику на UpdateSQL выдаётся редактор, в котором ты должен для каждой из таблиц,входящих в твой запрос, указать набор полей, являющихся уникальным ключём таблицы, и набор полей, которые требуется редактировать. В общем случае возможны глюки с редактированием, если в числе изменяемых полей будут элементы ключа. Указав все поля, давишь кнопку Generate SQL и в результате у тебя генерятся запросы на редактирование, добавление и удаление, которые прописываются в том же UpdateSQL. Обычно эти запросы никакого дополнительного редактирования не требуют. После всех этих дел ты можешь нормально редактировать запрос, как обычную таблицу.

    Hекоторые моменты.

    Для того, чтобы всё это нормально работало, нужно, чтобы в TQuery были включены RequestLive и CashedUpdates. Соответственно, для подтверждения изменений нужно вызывать TQuery.ApplyUpdates и TQuery.CommitUpdates, либо TDatabase.ApplyUpdates, а для отмены – CancelUpdates.

    Если меняешь структуру таблиц, то не забывай менять списки полей в UpdateSQL, иначе можешь получить неприятный сюрприз – будешь долго сидеть и думать, почему при редактировании/добавлении некоторые поля не прописываются :-).

    – Отрезано –

    Hасчёт CachedUpdates.

    Сия хреновина придумана для того, чтобы обеспечить сохранение/отмену редактирования/добавления/удаления сразу нескольких записей. Принцип совершенно элементарен: если CachedUpdates включен, то все производимые изменения в датасете по команде Post фиксируются не в базе, а во временном файле на винте клиента. Для того, чтобы прописать изменения в таблице (физически), необходимо вызвать для соответствующего запроса последовательно методы ApplyUpdates и CommitUpdates, а для отмены ВСЕХ изменений (начиная от последнего выполненного CommitUpdates), вызвать CancelUpdates. Кроме того, метод ApplyUpdates у TDataBase. Этому методу нужен список датасетов, и он производит их обновление в одной транзакции.

    Практическое применение, например, такое: на форме редактирования с гридом и набором кнопок Добавить, Удалить, Редактировать, ОК, Отмена, вешаешь на первые три кнопки обработчики с Insert, Delete и Edit соответственно, на OK – такой примерно обработчик:

    with DataSet do begin

     if State in [dsEdit,dsInsert] then Post;

     ApplyUpdates;

     CommitUpdates;

    end;

    а на Отмену такой:

    with DataSet do begin

     if State in [dsEdit,dsInsert] then Cancel;

     CancelUpdates;

    end;

    В результате юзер может редактировать хоть всю таблицу, но если успеет спохватиться, то может отменить все свои художества. Только желательно на выходе из формы проверить, сохранены ли изменения, и если нет, то напомнить/переспросить.

    Лучше использовать конструкцию «State in dsEditModes»

    Разное 

    Создание компонентов для работы с базами данных

    Тема: Создание компонентов для работы с базами данных, позволяющих работать с самими данными

    Обзор

    Данный документ описывает минимально необходимые шаги, необходимые для создания компонента для работы с базами данных, который может отображать данные отдельного поля. Примером такого компонента может служить панель со свойствами DataSource и DataField, похожая на компонент TDBText. Для получения дополнительных примеров обратитесь к Руководству по написанию компонентов "Making a Control Data-Aware".

    Как пользоваться данным документом

    Для наилучшего понимания данного документа, вы должны быть знакомы с механизмом функционирования элементов управления для работы с базами данных и основополагающими принципами создания компонент, такими, как

    • создание компонентов на основе существующих

    • перекрытие конструкторов и деструкторов

    • создание новых свойств

    • чтение и запись значений свойств

    • назначение обработчиков событий

    Основные шаги по созданию компоненты, осуществляющей навигацию по данным

    • Создайте или наследуйте компонент, который допускает свое отображение, но не ввод данных. Например, вы могли бы использовать компонент TMemo с установленным в True свойством ReadOnly. В примере, приведенном в данном документе, мы используем TCustomPanel. TCustomPanel позволяет себя отображать, но не вводить данные.

    • Добавьте к вашему компоненту data-link object (объект для связи с данными). Данный объект позволяет управлять связью между компонентом и таблицей базы данных.

    • Добавьте к компоненту свойства DataField и DataSource.

    • Добавьте методы для получения и установления DataField и DataSource.

    • Добавьте к компоненту метод DataChange, позволяющий управлять событиями OnDataChange объекта data-link.

    • Перекройте конструктор компонента для создания datalink и перехвата метода DataChange.

    • Перекройте деструктор компонента для очищения datalink.

    Создание TDBPANEL

    • Создайте или наследуйте компонент, который допускает свое отображение, но не ввод данных. В качестве отправной точки для нашего примера мы будем использовать TCustomPanel.

    Выберите соответствующий пункт меню для создания нового компонента (он меняется от версии к версии Delphi), определите TDBPanel как имя класса, и TCustomPanel в качестве наследуемого типа. Определите любую страницу Палитры компонентов.

    • Добавьте DB и DBTables в список используемых модулей.

    • Добавьте data-link объект в секцию private вашего компонента. Данный пример отображает данные одного поля, поэтому мы используем TFieldDataLink для обеспечения связи между нашим новым компонентом и DataSource. Имя нового data-link объекта – FDataLink.

    { пример }

    private

     FDataLink: TFieldDataLink;

    • Добавьте к компоненту свойства DataField и DataSource. Мы добавим соответствующий код для методов записи/чтения в последующих шагах.

    Примечание: Наш новый компонент будет иметь свойства DataField и DataSource, FDataLink также будет иметь собственные свойства DataField и Datasource.

    { пример }

    published

     property DataField: string read GetDataField write SetDataField;

     property DataSource: TDataSource read GetDataSource write SetDataSource;

    • Добавьте частные методы для чтения/записи значений свойств DataField и DataSource, и свойств DataField и DataSource для FDataLink.

    { пример }

    private

     FDataLink: TFieldDataLink;

     function GetDataField: String;

     function GetDataSource: TDataSource;

     procedure SetDataField(Const Value: string);

     procedure SetDataSource(Value: TDataSource);

     .

     .

    implementation

     .

     .

    function TDBPanel.GetDataField: String;

    begin

     Result := FDataLink.FieldName;

    end;


    function TDBPanel.GetDataSource: TDataSource;

    begin

     Result := FDataLink.DataSource;

    end;


    procedure TDBPanel.SetDataField(Const Value: string);

    begin

     FDataLink.FieldName := Value;

    end;


    procedure TDBPanel.SetDataSource(Value: TDataSource);

    begin

     FDataLink.DataSource := Value;

    end;

    • Добавьте частный метод DataChange, назначая событие объекта datalink OnDataChange. В методе DataChange добавьте код для отображения данных поля актуальной базы данных, связь с которой обеспечивает объект data-link. В нашем примере мы назначаем значение поля FDataLink заголовку панели.

    { пример }

    private

    .

    .

    procedure DataChange(Sender: TObject); = nil then Caption := '';

    implementation

    .

    .

    procedure TDBPanel.DataChange(Sender: TObject);

    begin

     if FDataLink.Field 

     else Caption := FDataLink.Field.AsString;

    end;

    • Перекройте метод конструктора компонента Create. При реализации Create, создайте объект FDataLink и назначьте частный метод DataChange событию FDataLink OnDataChange.

    { пример }

    public

     constructor Create(AOwner: TComponent); override;

    .

    .

    implementation

    .

    .

    constructor TMyDBPanel.Create(AOwner: TComponent);

    begin

     inherited Create(AOwner);

     FDataLink := TFieldDataLink.Create;

     FDataLink.OnDataChange := DataChange;

    end;

    • Перекройте метод деструктора компонента Destroy. При реализации Destroy, установите OnDataChange в nil (чтобы избежать GPF), и освободите FDatalink.

    { пример }

    public

    .

    .

    destructor Destroy; override;

    .

    .

    implementation

    .

    .

    destructor TDBPanel.Destroy;

    begin

     FDataLink.OnDataChange := nil;

     FDataLink.Free;

     inherited Destroy;

    end;

    • Сохраните модуль и установите компонент (смотрите документацию Users Guide и Component Writers Guide для получения дополнительной информации по сохранению модулей и установке компонентов).

    • Для тестирования функциональности компонента расположите на форме компоненты TTable, TDatasource, TDBNavigator и TDBPanel. Установите TTable DatabaseName и Tablename в 'DBDemos' и 'BioLife', а свойство Active в True. Установите свойство TDatasource Dataset в Table1. Установите TDBNavigator и свойство TDBPanel DataSource в Datasource1. Имя TDBpanel DataField должно быть установлено в 'Common_Name'. Запустите приложение и, используя навигатор и перемещаясь по записям, убедитесь в том, что TDBPanel обнаруживает изменение данных и отображает значение соответствующего поля.

    Полный код компонента

    unit Mydbp;


    interface


    uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls, DB, DBTables;


    type TDBPanel = class(TCustomPanel)

    private

     FDataLink: TFieldDataLink;

     function GetDataField: String;

     function GetDataSource: TDataSource;

     procedure SetDataField(Const Value: string);

     procedure SetDataSource(Value: TDataSource);

     procedure DataChange(Sender: TObject);

    public

     constructor Create(AOwner: TComponent); override;

     destructor Destroy; override;

    published

     property DataField: string read GetDataField write SetDataField;

     property DataSource: TdataSource read GetDataSource write SetDataSource;

    end;


    procedure Register;


    implementation


    procedure Register;

    begin

     RegisterComponents('Samples', [TDBPanel]);

    end;


    function TDBPanel.GetDataField: String;

    begin

     Result := FDataLink.FieldName;

    end;


    function TDBPanel.GetDataSource: TDataSource;

    begin

     Result := FDataLink.DataSource;

    end;


    procedure TDBPanel.SetDataField(Const Value: string);

    begin

     FDataLink.FieldName := Value;

    end;


    procedure TDBPanel.SetDataSource(Value: TDataSource);

    begin

     FDataLink.DataSource := Value;

    end;


    procedure TDBPanel.DataChange(Sender: TObject);

    begin

     if FDataLink.Field = nil then Caption := ''

     else Caption := FDataLink.Field.AsString;

    end;


    constructor TDBPanel.Create(AOwner: TComponent);

    begin

     inherited Create(AOwner);

     FDataLink := TFieldDataLink.Create;

     FDataLink.OnDataChange := DataChange;

    end;


    destructor TDBPanel.Destroy;

    begin

     FDataLink.Free;

     FDataLink.OnDataChange := nil;

     inherited Destroy;

    end;

    end.

    Динамическое создание компонент во время работы приложения

    Delphi 1

    Использовать формы и компоненты Delphi очень просто. Если управлять этими объектами посредством Инспектора Объектов, то эту задачу можно отнести к числу тривиальных. Динамически создать объект также несложно. В этом документе мы обсудим некоторые вопросы, касающиеся динамического создания компонент во время работы приложения.

    (вам следует помнить, что понятие "динамическое" весьма субъективно, поскольку Delphi все объекты создает динамически. Информация, предоставленная здесь — для программиста, который сам собирается создавать/менять свойства/разрушать объекты во время выполнения программы)

    Все типы (формы или компоненты) могут создаваться динамически. Чтобы это сделать, необходимо объявить переменную нужного типа в секции VAR вашего кода. Это не создает экземпляр объекта, это создает указатель. Данный указатель расположен в сегменте данных (если переменная объявлена глобально) или в стеке (если переменная объявлена локально в процедуре или функции). Для того, чтобы создать экземпляр класса, вам необходимо вызвать конструктор. Это распределит память в глобальной компьютерной куче для экземпляра класса. При попытке получить доступ к компоненте прежде, чем мы распределим память, мы получим ошибку общей защиты.

    Конструктор Create() является классовым методом, наследуемым от класса TObject. Create() возвращает указатель. Данный метод может потребовать (а может и нет) один или несколько параметров. В большинстве компонентов (все объекты, наследуемые от TComponent, имеют право называться компонентами), конструктор на входе требует один параметр, указывающий на "владельца" и имеющий тип TComponent.

    При динамическом создании компонента в большинстве случаев владелецем становится "Self". Если вы в этот момент находитесь в одном из методов формы, "Self" в данном контексте будет ссылаться на саму форму. Если владелец является действительным объектом, освобождение этого объекта влечет за собой автоматическое освобождение "дочернего" компонента. Другим распространенным параметром является "Application". Он может использоваться в случае, когда визуальный компонент не должен быть показан программой пользователю. Тем не менее, большинство компонентов не требуют назначения владельца, так что нет ничего необычного в том, что требуемый параметр owner устанавливается в Nil. Но вы должны помнить о том, что впоследствии вы не сможете изменить владельца объекта. Если конструктору при создании был передан Nil, то после использования компонента вы должны сами освобождать его вызовом Free.

    После создания оконных компонентов (т.е. тех компонентов, которые являются наследниками TWinControl), но еще перед тем, как они будут отображены, у них необходимо установить свойство Parent. Место установки свойства Parent является хорошим местом для установки других свойств экземпляра данного компонента, включая обработчики событий (например, Width, Color, OnClick).

    Обработчики событий идентичны тем, которые определены в Инспекторе Объектов. Просто присвойте имени свойства компонента для события, которое вы хотите обработать, имя метода обработчика события, которое вы ожидаете. В примере 1, приведенном ниже, при нажатии на кнопку будет вызван метод с именем "myclick". Пожалуйста имейте в виду, что список входных параметров одного метода должен в точности соответствовать списку выходных параметров другого.

    Пример 1:

    var b1 : TButton;

    begin

     .

     .

     .

     b1 := TButton.Create(Self);

     with b1 do begin

      Left := 20;

      Top := 20;

      Width := 90;

      Height := 50;

      Caption :=  'моя кнопка';

      Parent := Form1;

      OnClick :=  MyClick; { процедура, определенная где-то еще }

     end;

     .

     .

     .

    end;

    В следующем примере показано как можно во время выполнения программы динамически создать кнопку, щелкая по другой кнопке, размещенной на форме во время проектирования (к этому моменту она уже создана). Это уже другой путь создания кнопки. Все способы рабочие. Также имейте в виду, что кнопки, не освобождаемые в данном коде, будут освобождаться при разрушении формы.

    unit Unit1;

    interface


    uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;


    type TForm1 = class(TForm)

     Button1: TButton;

     procedure Button1Click(Sender: TObject);

    private

     { Private declarations }

    public

     { Public declarations }

     procedure myClick(Sender: TObject);

    end;


    var Form1: TForm1;


    const i : integer = 0;


    implementation

    {$R *.DFM}


    procedure TForm1.myClick(Sender: TObject);

    begin

     with Sender as TButton do Self.Caption := ClassName + ' ' + Name;

    end;


    procedure TForm1.Button1Click(Sender: TObject);

    begin

     with TButton.Create(self) do begin

      Left := 20;

      Top := 30 + i;

      Width := 120;

      Height := 40;

      Name := 'ThisButton' + IntToStr(i);

      Caption := 'There' + IntToStr(i);

      OnClick := MyClick; { процедура, определенная где-то еще }

      Parent := Form1;

     end; {end with}

     inc(i, 40);

    end; {end button1.click}

    end.

    Решение для динамически создаваемых компонентов

    Delphi 1

    Предупреждение:

    Если вы просто хотите во время выполнения приложения создавать компоненты необходимого вам типа, ознакомьтесь с файлом delphi\doc\VB2Delph.wri и следуйте его рекомендациям, лучшего способа изучения этой темы пока не существует. Данный совет повествует об использовании в Delphi RTTI.

    Во-первых, в вашем приложении необходимо зарегистрировать все классы, экземпляры которых вы собираетесь в каком-то месте кода создавать. Сделать это можно с помощью функций RegisterClass(), RegisterClasses() и RegisterClassAlias().

    Пример:

    procedure TForm1.FormCreate(Sender: TObject);

    begin

     RegisterClasses([TButton, TEdit, TMemo, TLabel]);

    end;

    Это может навести вас на мысль об ограничениях, но Delphi строгий язык. Если вы хотите истинно динамическое создание объектов в слаботипизированной среде позднего связывания, используйте динамический язык типа Smalltalk. У меня есть подозрение, что Delphi использует этот механизм регистрации для регистрации всех компонентов в DCL при его запуске, позволяя этим самым создавать любой компонент во время разработки.

    Создание компонентов. Используйте функцию FindClass() для получения ссылки на класс компонента, который вы хотите создать, и вызывайте его метод Create. Легко, не правда ли? В примере у меня имеется приведение типа SomeComponent к TControl, после чего я уже могу установить свойство parent (я могу делать это, поскольку я знаю, что все зарегистрированные мною классы являются потомками TControl). Для того, чтобы визуальный компонент появился на форме, вам необходимо установить свойство parent.

    Пример:

    procedure TForm1.CreateClick(Sender: TObject);

    begin

     SomeComponent:= TComponentClass(FindClass(ClassName.Text)).Create(Self);

     (SomeComponent as TControl).Parent := Self;

    end;

    Теперь, когда вы имеете созданный компонент, как установить его свойства без использования самого большого блока case во вселенной? Очень просто: для получения информации о свойстве из структуры run-time type information (RTTI) используется функция GetPropInfo(), после чего для установления значений используется набор функций SetXXXXProp(). (Примечание: эти функции не задокументированы в файлах помощи Delphi. OO-программисты, как я понимаю, пользуются примерами из чужого кода и не изобретают свой велосипед.) У каждой функции SetXXXXProp() имеется функция-сателлит GetXXXXProp(), позволяющая узнать значения свойств объекта.

    Пример:

    procedure TForm1.SetPropertyClick(Sender: TObject);

    var

     PropType: PTypeInfo;

     PropInfo: PPropInfo;

    begin

     PropInfo := GetPropInfo(SomeComponent.ClassInfo, PropertyName.Text);

     PropType := PropInfo^.PropType;

     case PropType^.Kind of

     tkInteger:

      SetOrdProp(SomeComponent, PropInfo, StrToInt(PropertyValue.Text));

     tkChar:

      SetOrdProp(SomeComponent, PropInfo, Ord(PropertyValue.Text[1]));

     tkEnumeration:

      SetOrdProp(SomeComponent, PropInfo, GetEnumValue(PropType, PropertyValue.Text));

     tkFloat:

      SetFloatProp(SomeComponent, PropInfo, StrToFloat(PropertyValue.Text));

     tkString:

      SetStrProp(SomeComponent, PropInfo, PropertyValue.Text);

     end;

    end;

    Вы также можете установить значения свойств Set, Class и Method, но это будет немного сложнее. Немного позже я объясню как это можно сделать.

    Это все. Вы проведете время с большой пользой, изучая исходный код VCL, и удивляясь, когда вы все там увидите собственными глазами.

    Это прекрасный способ, но он имеет потенциал для массового злоупотребления. Необходимо понимание других путей достижения этой цели и выбор соответствующей техники при создании своих проектов в Delphi. 

    Как правильно создавать органы управления в runtime?

    Nomadic советует:

    Примерно таким образом (Описываем метод-обработчик события OnClick формы):

    { Example }

    procedure TForm1.OnClick(ASender: TObject);

    var btnTemp: TButton;

    begin

     { Creating }

     btnTemp := TButton.Create(Self);

     { You can use 'with btnTemp do' operator below }

     { Inserting to Form }

     btnTemp.Parent := Self;

     { Initialization }

     btnTemp.Caption := 'I''m glad to see You';

     btnTemp.SetBounds(20, 20, 80, 20);

     { You must define this event handler named 'OnBtnTempClick' }

     btnTemp.OnClick := OnBtnTempClick;

     { Ready to show }

     btnTemp.Visible := true;

     { Done. }

    end;
     

    Как создать клон (копию, достаточно близкую к оригиналу) произвольного компонента?

    Nomadic советует:

    { Здесь процедyра CreateClone, которая креатит компоненту ОЧЕНЬ ПОХОЖУЮ на входную. С такими же значениями свойств. Присваивается все, кроме методов. }

    function CreateClone(Src: TComponent): TComponent;

    var F: TStream;

    begin

     F := nil;

     try

      F := TMemoryStream.Create;

      F.WriteComponent(Src);

      RegisterClass(TComponentClass(Src.ClassType));

      F.Position := 0;

      Result := F.ReadComponent(nil);

     finally

      F.Free;

     end;

    end;

    Как заставить произвольный компонент реагировать на изменения в TDataSource?

    Nomadic советует:

    TFieldDataLink. За D2 не скажу, а в D1 в Help'е его нет, реализован в \DELPHI\SOURCE\VCL\DBTABLES.PAS.

    type TMyForm = class(TForm)

     {…}

     Table1: TTable;

     DataSource1: TDataSource;

    private

     FDL : TFieldDataLink;

     procedure RecChange(Sender: TObject);

    public

     {...}

    end;


    procedure TMyForm.FormCreate(Sender: TObject);

    begin

     FDL:=TFieldDataLink.Create;

     FDL.OnDataChange := RecChange;

     FDL.DataSource := DataSource1;

     FDL.FieldName := 'MyFieldName';

    end;


    procedure TTabEditDlg.FormDestroy(Sender: TObject);

    begin

     FDL.Free;

    end;


    procedure TTabEditDlg.MasterChange(Sender: TObject);

    begin

     {… тут реагируй на изменения …}

    end;

    За отслеживание различных событий, происходящих с TDataSource, в иерархии VCL отвечает класс TDataLink. TFieldDataLink – наследник, который выполняет маскирование событий, не относящихся к конкретному столбцу набора данных.

    Если надо отслеживать изменения в любом столбце набора, используйте TDataLink. Если необходимо отслеживать события для некоторого подмножества строк набора данных, посмотрите на реализацию TGridDataLink

    Доступ к другим компонентам из базового

    Delphi 1 

    Список-свойство Components[] существует во всех потомках TComponent и используется для хранения ссылок на все собственные компоненты. При вызове «mycomponent := TSomeComponent.Create(aComponent)», ссылка на mycomponent помещается в список aComponent Components[]. В большинстве случаев, в методе Create в качестве владельца компонентов определена форма, и ссылки на компоненты помещаются в список Components[] самой формы.

    Метод FindComponent() (упомянутый где-то еще) только производит поиск компонентов в текущем списке Components[]. Если объект, который вы хотите найти, принадлежит другому компоненту, вы должны просканировать его список компонентов.

    В зависимости от того, как вы создаете свою базу и другие компоненты, вы можете осуществить рекурсивный алгоритм поиска, который стартует в верхней части дерева собственника набора компонентов (вероятно, формы), спускаясь вниз и проходя по списку Components[] каждого вновь найденного компонента, пока желаемый объект не будет найден.

    Хорошей альтернативой может служить способ, при котором вы всегда определяете базовый компонент в качестве владельца всех других ваших «подкомпонентов» (при их создании). После этого будет работать поиск по свойству вашего базового компонента Components[]. 

    CANVAS.TEXTWIDTH

    Delphi 1 

    Установить размер шрифта для панели можно следующим образом:

    With StatusBar1.Panels[1] do begin

     Text := Edit1.Text;

     Canvas.Font.Size := StatusBar1.Font.Size;

     Width := Canvas.TextWidth(Text) + 10;

    end;

    Создание компонента

    Delphi 1

    …чтобы сгруппировать свойства наподобие Font, вам необходимо создать наследника (подкласс) TPersistent. Например:

    TBoolList = class(TPersistent)

    private

     FValue1: Boolean;

     FValue2: Boolean

    published

     property Value1: Boolean read FValue1 write FValue1;

     property Value2: Boolean read FValue2 write FValue2;

    end;

    Затем, в вашем новом компоненте, для этого подкласса необходимо создать ivar. Чтобы все работало правильно, вам =необходимо= перекрыть конструктор.

    TMyPanel = class(TCustomPanel)

    private

     FBoolList: TBoolList;

    public

     constructor Create(AOwner: TComponent); override;

    published

     property BoolList: TBoolList read FBoolList write FBoolList;

    end;

    Затем добавьте следующий код в ваш конструктор:

    constructor TMyPanel.Create(AOwner: TComponent);

    begin

     inherited Create(AOwner);

     FBoolList := TBoolList.Create;

    end;

    Циклический опрос компонентов

    Delphi 1

    procedure TForm1.FormCreate(Sender: TObject);

    var I : integer;

    begin

     for I:= 0 to ComponentCount -1 do

      if (Components[I] IS TEdit) then

       (Components[I] AS TEdit).{Вашпараметр} := {ваше значение};

    end;

    Если вам необходимо идентифицировать конкретный набор edit-компонентов, поместите их на панели и сделайте примерно так:

    procedure TForm1.FormCreate(Sender: TObject);

    var I : integer;

    begin

     with MyPanel do for I:= 0 to ControlCount -1 do

      if (Controls[I] IS TEdit) then

       (Controls[I] AS TEdit).{Вашпараметр} := {Ваше значение};

    end;

    В контексте примера, Edit1, Edit2 и т.д. есть то же самое, что и Edit[1], Edit[2]. Если вы хотите иметь доступ к серии элементов управления как к элементам массива, поместите их в TList.

    MyArr := TList.Create;

    MyArr.Add(Edit1);

    MyArr.Add(Edit2);

    For i := 0 To MyArr.count - 1 Do

     (MyArr.items[i] As TEdit).Enabled := False;

    MyArr.Free;


    procedure TForm1.FormCreate(Sender: TObject);

    var I: Integer;

    begin

     for I := 0 to ComponentCount -1 do

      if Components[I] is TEdit then

       TEdit(Components[I]).Whatever := 10;

    end;

    Для получения доступа используйте:

    TButton(mylist.items[i]).property := sumpin;

    или

    TButton(mylist.items[i]).method;

    Это хорошее решение для пакетной обработки компонентов или для получения доступа при линейном способе. Для решения вашей проблемы есть еще более легкое решение, которое требует предварительной работы в режиме проектирования. Установите свойство tag и получите преимущество в том, что все компоненты являются производными от TComponent и имеют это свойство.

    Procedure TMyForm.MyButtonHandler(Sender: TObject);

    Begin

     Case (Sender As TComponent).Tag Of

     1 : { что-то делаем }

     2 : { делаем что-то еще }

     .

     .

     End;

    End;

    Просто укажите в событии OnClick на MyButtonHandler для тех кнопок, в которых вы хотите использовать общий обработчик события. 

    Мне надо добавить много строк в TListbox или в TCombobox или в TMemo или в TRichEdit, при этом сам объект постоянно мигает, перерисовываясь. Как избавиться от этого?

    Двумя словами 

    Nomadic скупо отвечает:

    A: BeginUpdate/EndUpdate. 

    Как мне создать компонент типа TField?

    Delphi 1 

    Наверное вы хотели создать класс, а не компонент? Класс является программируемым устройством, а не частью формы. Если вы поместили класс в модуль (скажем, myclass.pas) и вставили в вашу программу строку «uses myclass;», то воспользоваться им можно следующим образом:

    type aninstance: tMyclass;

    begin

     new (aninstance);

     {эквивалент aninstance := tMyclass.create; }

     …

     { здесь используем aninstance }

     …

     dispose(aninstance);

     { эквивалент aninstance.free; }

    end;

    Инкрементация строкового поля

    Delphi 1

    Свойства text элемента управления является строкой, в свою очередь являющейся массивом символом. Вы не можете осуществить преобразование символа в строку. Тем не менее, вы можете получить доступ ко всем символам строки через их индекс.

    Попробуйте это:

    var s : string;

    begin

     s := RevField.text;

     s[1] := chr(ord(s[1]) + 1);

     RevField.text := s;

    end;

    Здесь кроются 2 проблемы:

    1. Для увеличения значения вам необходимо извлекать символы из строки.

    2. Хотя вы можете получить доступ к отдельным символам через выделение подстроки, данный метод не срабатывает у некоторых свойств, таких как, например, свойство TStringField Text.

    Лучшим решением, по-видимому, будет написание специфической функции. Например, в случае, если revision-символ всегда является конечным символом строки, функция могла бы выглядеть следующим образом:

    function IncrementTrailingVersionLetter(Str: string): string;

    begin

     Str[Length(Str)] := Char(Ord(Str[Length(Str)]) + 1);

     IncrementTrailingVersionLetter := Str;

    end;

    и использовать ее следующим образом:

    with RevField do Text := IncrementTrailingVersionLetter(Text);
     


    Примечания:



    1

    64K для Win3.1 & 64K только для 16-битной подсистемы Win95. Для получения дополнительной информации обратитесь в Microsoft или к MSDN.



    2

    Как побочный эффект при разрушении вышеописанных дескрипторов, TTabbedNotebook, используемый в данном примере, гораздо быстрее выполняет перемещение страниц.








    Главная | В избранное | Наш E-MAIL | Прислать материал | Нашёл ошибку | Наверх