Programming: Delphi


Советы программистов (Часть 4)





  1. Часть 1
  2. Часть 2
  3. Часть 3
  1. Обзор сети (типа Network Neighborhood)
  2. Как узнать доступные сетевые ресурсы?
  3. Список пользователей в Windows NT/2000
  4. Цветная кнопка
  5. Денежное поле редактирования в TEdit
  6. «Бегущая» строка
  7. Множественный выбор в ListBox
  8. Динамическое добавление пунктов меню
  9. Слияние MDI-меню
  10. Динамическое создание пункта всплывающего меню
  11. Использование шрифтов и стилей в TMemo
  12. Индикатор хода выполнения в строке состояния
  13. TTrackBar для эстетов
  14. Чтение текста RichEdit из базы данных
  15. Исправление загрузки текста RTF через поток
  16. Динамическое создание компонента TTable
  17. Различные цвета строк в DBCtrlGrid
  18. Использование опции MultiSelect в DBGrid
  19. Сортировка колонок в DBGrid
  20. DBGrid с цветными ячейками
  21. TDBGrid -– копирование в буфер обмена
  22. Заголовок в DBGrid
  23. Отображение логотипа при запуске приложения
  24. Поддержка команд Cut, Copy, Paste




Обзор сети (типа Network Neighborhood)

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

unit netres_main_unit; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, StdCtrls, Buttons, Menus, ExtCtrls; type TfrmMain = class(TForm) tvResources: TTreeView; btnOK: TBitBtn; btnClose: TBitBtn; Label1: TLabel; barBottom: TStatusBar; popResources: TPopupMenu; mniExpandAll: TMenuItem; mniCollapseAll: TMenuItem; mniSaveToFile: TMenuItem; mniLoadFromFile: TMenuItem; grpListType: TRadioGroup; grpResourceType: TRadioGroup; dlgOpen: TOpenDialog; dlgSave: TSaveDialog; procedure FormCreate(Sender: TObject); procedure btnCloseClick(Sender: TObject); procedure FormShow(Sender: TObject); procedure mniExpandAllClick(Sender: TObject); procedure mniCollapseAllClick(Sender: TObject); procedure mniSaveToFileClick(Sender: TObject); procedure mniLoadFromFileClick(Sender: TObject); procedure btnOKClick(Sender: TObject); private ListType, ResourceType: DWORD; procedure ShowHint(Sender: TObject); procedure DoEnumeration; procedure DoEnumerationContainer(NetResContainer: TNetResource); procedure AddContainer(NetRes: TNetResource); procedure AddShare(TopContainerIndex: Integer; NetRes: TNetResource); procedure AddShareString(TopContainerIndex: Integer; ItemName: String); procedure AddConnection(NetRes: TNetResource); end; var frmMain: TfrmMain; implementation {$R *.DFM} procedure TfrmMain.ShowHint(Sender: TObject); begin barBottom.Panels.Items[0].Text := Application.Hint; end; procedure TfrmMain.FormCreate(Sender: TObject); begin Application.OnHint := ShowHint; barBottom.Panels.Items[0].Text := ''; end; procedure TfrmMain.btnCloseClick(Sender: TObject); begin Close; end; { Перечисляем все сетевые ресурсы } procedure TfrmMain.DoEnumeration; var NetRes: Array[0..2] of TNetResource; Loop: Integer; r, hEnum, EntryCount, NetResLen: DWORD; begin case grpListType.ItemIndex of { Подключенные ресурсы: } 1: ListType := RESOURCE_CONNECTED; { Возобновляемые ресурсы: } 2: ListType := RESOURCE_REMEMBERED; { Глобальные: } else ListType := RESOURCE_GLOBALNET; end; case grpResourceType.ItemIndex of { Дисковые ресурсы: } 1: ResourceType := RESOURCETYPE_DISK; { Принтерные ресурсы: } 2: ResourceType := RESOURCETYPE_PRINT; { Все: } else ResourceType := RESOURCETYPE_ANY; end; Screen.Cursor := crHourGlass; try { Удаляем любые старые элементы из дерева } for Loop := tvResources.Items.Count - 1 downto 0 do tvResources.Items[Loop].Delete; except end; { Начинаем перечисление: } r := WNetOpenEnum(ListType, ResourceType, 0, nil, hEnum); if r <> NO_ERROR then begin if r = ERROR_EXTENDED_ERROR then MessageDlg('Невозможно сделать обзор сети.' + #13 + 'Произошла сетевая ошибка.', mtError, [mbOK], 0) else MessageDlg('Невозможно сделать обзор сети.', mtError, [mbOK], 0); Exit; end; try { Мы получили правильный дескриптор перечисления; опрашиваем ресурсы } while (1 = 1) do begin EntryCount := 1; NetResLen := SizeOf(NetRes); r := WNetEnumResource(hEnum, EntryCount, @NetRes, NetResLen); case r of 0: begin { Это контейнер, организуем итерацию: } if NetRes[0].dwUsage = RESOURCEUSAGE_CONTAINER then DoEnumerationContainer(NetRes[0]) else { Здесь получаем подключенные и возобновляемые ресурсы } if ListType in [RESOURCE_REMEMBERED, RESOURCE_CONNECTED] then AddConnection(NetRes[0]); end; ERROR_NO_MORE_ITEMS: { Получены все ресурсы: } Break; else begin { Другие ошибки: } MessageDlg('Ошибка опроса ресурсов.', mtError, [mbOK], 0); break; end; end; end; finally Screen.Cursor := crDefault; { Закрываем дескриптор перечисления: } WNetCloseEnum(hEnum); end; end; { Перечисление заданного контейнера. Эта функция обычно вызывается рекурсивно } procedure TfrmMain.DoEnumerationContainer(NetResContainer: TNetResource); var NetRes: Array[0..10] of TNetResource; TopContainerIndex: Integer; r, hEnum, EntryCount, NetResLen: DWORD; begin { Добавляем имя контейнера к найденным сетевым ресурсам } AddContainer(NetResContainer); { Делаем этот элемент текущим корневым уровнем } TopContainerIndex := tvResources.Items.Count-1; { Начинаем перечисление: } if ListType = RESOURCE_GLOBALNET then { Перечисляем глобальные объекты сети: } r := WNetOpenEnum(ListType, ResourceType, RESOURCEUSAGE_CONTAINER, @NetResContainer, hEnum) else { Перечисляем подключаемые и возобновляемые ресурсы (другие получить здесь невозможно) } r := WNetOpenEnum(ListType, ResourceType, RESOURCEUSAGE_CONTAINER, nil, hEnum); { Невозможно перечислить ресурсы данного контейнера, выводим соответствующее предупреждение и едем дальше } if r <> NO_ERROR then begin AddShareString(TopContainerIndex, '<Не могу опросить ресурсы (Ошибка #' + IntToStr(r) + '>'); WNetCloseEnum(hEnum); Exit; end; { Мы получили правильный дескриптор перечисления; опрашиваем ресурсы } while (1 = 1) do begin EntryCount := 1; NetResLen := SizeOf(NetRes); r := WNetEnumResource(hEnum, EntryCount, @NetRes, NetResLen); case r of 0: begin { Другой контейнер для перечисления, необходим рекурсивный вызов } if (NetRes[0].dwUsage = RESOURCEUSAGE_CONTAINER) or (NetRes[0].dwUsage=10) then DoEnumerationContainer(NetRes[0]) else case NetRes[0].dwDisplayType of { Верхний уровень } RESOURCEDISPLAYTYPE_GENERIC, RESOURCEDISPLAYTYPE_DOMAIN, RESOURCEDISPLAYTYPE_SERVER: AddContainer(NetRes[0]); { Ресурсы общего доступа: } RESOURCEDISPLAYTYPE_SHARE: AddShare(TopContainerIndex, NetRes[0]); end; end; ERROR_NO_MORE_ITEMS: Break; else begin MessageDlg('Ошибка #' + IntToStr(r) + ' при перечислении ресурсов.', mtError, [mbOK], 0); Break; end; end; end; { Закрываем дескриптор перечисления } WNetCloseEnum(hEnum); end; procedure TfrmMain.FormShow(Sender: TObject); begin DoEnumeration; end; { Добавляем элементы дерева; помечаем, что это контейнер } procedure TfrmMain.AddContainer(NetRes: TNetResource); var ItemName: String; begin ItemName := Trim(String(NetRes.lpRemoteName)); if Trim(String(NetRes.lpComment))<>'' then begin if ItemName <> '' then ItemName := ItemName + ' '; ItemName := ItemName + '(' + String(NetRes.lpComment) + ')'; end; tvResources.Items.Add(tvResources.Selected,ItemName); end; { Добавляем дочерние элементы к контейнеру, обозначенному как текущий верхний уровень } procedure TfrmMain.AddShare(TopContainerIndex: Integer; NetRes: TNetResource); var ItemName: String; begin ItemName := Trim(String(NetRes.lpRemoteName)); if Trim(String(NetRes.lpComment)) <> '' then begin if ItemName <> '' then ItemName := ItemName + ' '; ItemName := ItemName + '(' + String(NetRes.lpComment) + ')'; end; tvResources.Items.AddChild(tvResources.Items[TopContainerIndex], ItemName); end; { Добавляем дочерние элементы к контейнеру, обозначенному как текущий верхний уровень; это просто добавляет строку для таких задач, как, например, перечисление контейнера. То есть некоторые контейнерные ресурсы общего доступа нам не доступны. } procedure TfrmMain.AddShareString(TopContainerIndex: Integer; ItemName: String); begin tvResources.Items.AddChild(tvResources.Items[TopContainerIndex], ItemName); end; { Добавляем соединения к дереву. По большому счету, к этому моменту все сетевые ресурсы типа возобновляемых и текущих соединений уже отображены. } procedure TfrmMain.AddConnection(NetRes: TNetResource); var ItemName: String; begin ItemName := Trim(String(NetRes.lpLocalName)); if Trim(String(NetRes.lpRemoteName)) <> '' then begin if ItemName <> '' then ItemName := ItemName + ' '; ItemName := ItemName + '-> ' + Trim(String(NetRes.lpRemoteName)); end; tvResources.Items.Add(tvResources.Selected, ItemName); end; { Раскрываем все контейнеры дерева } procedure TfrmMain.mniExpandAllClick(Sender: TObject); begin tvResources.FullExpand; end; { Схлопываем все контейнеры дерева } procedure TfrmMain.mniCollapseAllClick(Sender: TObject); begin tvResources.FullCollapse; end; { Записываем дерево в выбранный файл } procedure TfrmMain.mniSaveToFileClick(Sender: TObject); begin if dlgSave.Execute then tvResources.SaveToFile(dlgSave.FileName); end; { Загружаем дерево из выбранного файла } procedure TfrmMain.mniLoadFromFileClick(Sender: TObject); begin if dlgOpen.Execute then tvResources.LoadFromFile(dlgOpen.FileName); end; { Обновляем } procedure TfrmMain.btnOKClick(Sender: TObject); begin DoEnumeration; end; end.




Как узнать доступные сетевые ресурсы?

Решение:

type PNetResourceArray = ^TNetResourceArray; TNetResourceArray = array[0..MaxInt div SizeOf(TNetResource) - 1] of TNetResource; procedure EnumResources(LpNR: PNetResource); var NetHandle: THandle; I, BufSize, NetResult: Integer; NetResources: PNetResourceArray; NewItem: TListItem; Count, Size: DWORD; begin if WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY, 0, LpNR, NetHandle) <> NO_ERROR then Exit; try BufSize := 50 * SizeOf(TNetResource); GetMem(NetResources, BufSize); try while True do begin Count := $FFFFFFFF; Size := BufSize; NetResult := WNetEnumResource(NetHandle, Count, NetResources, Size); if NetResult = ERROR_MORE_DATA then begin BufSize := Size; ReallocMem(NetResources, BufSize); Continue; end; if NetResult <> NO_ERROR then Exit; for I := 0 to Count-1 do begin with NetResources^[I] do begin if RESOURCEUSAGE_CONTAINER = (DwUsage and RESOURCEUSAGE_CONTAINER) then EnumResources(@NetResources^[I]); if dwDisplayType = RESOURCEDISPLAYTYPE_SHARE then begin // ^^^^^^^^^^^^^^^^^^^^^^^^^ - ресурс // RESOURCEDISPLAYTYPE_SERVER - компьютер // RESOURCEDISPLAYTYPE_DOMAIN - рабочая группа // RESOURCEDISPLAYTYPE_GENERIC - сеть NewItem := Form1.ListView1.Items.Add; NewItem.Caption := LpRemoteName; end; end; end; end; finally FreeMem(NetResources, BufSize); end; finally WNetCloseEnum(NetHandle); end; end; procedure TForm1.Button1Click(Sender: TObject); var OldCursor: TCursor; begin OldCursor := Screen.Cursor; Screen.Cursor := crHourGlass; with ListView1.Items do begin BeginUpdate; Clear; EnumResources(nil); EndUpdate; end; Screen.Cursor := OldCursor; end; [Nomadic]

Примечание
Добавляем в uses модуль ComCtrls.




Список пользователей в Windows NT/2000

Решение:

unit Func; interface uses SysUtils, Classes, StdCtrls, ComCtrls, Graphics, Windows; {$EXTERNALSYM NetUserEnum} function NetUserEnum(servername: LPWSTR; level, filter: DWORD; bufptr: Pointer; prefmaxlen: DWORD; entriesread, totalentries, resume_handle: LPDWORD): DWORD; stdcall; external 'NetApi32.dll' Name 'NetUserEnum'; function NetApiBufferFree(Buffer: Pointer{LPVOID}): DWORD; stdcall; external 'NetApi32.dll' Name 'NetApiBufferFree'; procedure GetLocalUserList(ulist: TStringList); implementation // возвращает список пользователей локального хоста procedure GetLocalUserList(ulist: TStringList); const NERR_SUCCESS = 0; FILTER_TEMP_DUPLICATE_ACCOUNT = $0001; FILTER_NORMAL_ACCOUNT = $0002; FILTER_PROXY_ACCOUNT = $0004; FILTER_INTERDOMAIN_TRUST_ACCOUNT = $0008; FILTER_WORKSTATION_TRUST_ACCOUNT = $0010; FILTER_SERVER_TRUST_ACCOUNT = $0020; type TUSER_INFO_10 = record usri10_name, usri10_comment, usri10_usr_comment, usri10_full_name: PWideChar; end; PUSER_INFO_10 = ^TUSER_INFO_10; var dwERead, dwETotal, dwRes, res: DWORD; inf: PUSER_INFO_10; info: Pointer; p: PChar; i: Integer; begin if ulist = nil then Exit; ulist.Clear; info := nil; dwRes := 0; res := NetUserEnum(nil, 10, FILTER_NORMAL_ACCOUNT, @info, 65536, @dwERead, @dwETotal, @dwRes); if (res <> NERR_SUCCESS) or (info = nil) then Exit; p := PChar(info); for i:=0 to dwERead - 1 do begin inf := PUSER_INFO_10(p + i * SizeOf(TUSER_INFO_10)); ulist.Add(WideCharToString(PWideChar((inf^).usri10_name))); end; NetApiBufferFree(info); end; end. [Кондратюк Виталий]




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

В книгах Калверта, Свана и других авторов можно найти текст, смысл которого сводится примерно к следующему: «Изменить цвет кнопок Button, BitBtn нельзя, т. к. их рисует Windows». Если нельзя, но очень нужно, то можно.

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

unit ColorBtn; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons; type TColorBtn = class(TButton) private 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 procedure CreateParams(var Params: TCreateParams); override; procedure SetButtonStyle(ADefault: boolean); override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; published 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. [VS]

Примечание
Кнопку по-прежнему рисует Windows, а раскрашивает ее компонент ColorBtn.




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

Решение:

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 DisplayFormat: string read DispFormat write SetFormat; property Value: Extended read FieldValue write SetFieldValue; 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.




«Бегущая» строка

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

С помощью TLabel и TTimer.

procedure TForm1.Timer1Timer(Sender: TObject); const LengthGoString = 10; GoString = 'В конце стpоку желательно повтоpить,' + ' чтобы получить эффект кольцевого движения! В конце строки'; i: Integer = 1; begin Label1.Caption := Copy(GoString, i, LengthGoString); Inc(i); if Length(GoString) - LengthGoString < i then i := 1; end; [Nikolaev Igor]

Примечание
«Окно» просмотра задается константой LengthGoString, скорость – параметром Interval компонента TTimer.




Множественный выбор в 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; Примечание
Необходимо в Object Inspector для ListBox1 установить свойство MultiSelect в True.




Динамическое добавление пунктов меню

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

unit Istopmnu; interface uses Windows, SysUtils, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Menus; type TForm1 = class(TForm) MainMenu1: TMainMenu; procedure AClick(Sender: TObject); procedure FormCreate(Sender: TObject); end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.AClick(Sender: TObject); var TM: TMenuItem; Lev: Word; begin MessageBeep(0); TM := Sender as TMenuItem; if TM.Count > 0 then Caption := 'подменю' else Caption := 'элемет меню'; Caption := Caption + ' с именем "' + TM.Name + '"'; Lev := 0; while (TM.Parent <> NIL) and (TM.Parent is TMenuItem) do begin TM := TM.Parent; Inc(Lev); end; case Lev of 1: Caption := 'Верхний уровень ' + Caption; 2: Caption := '2-й уровень ' + Caption; 3: Caption := '3-й уровень ' + Caption; else Caption := Format('%d-й уровень %s', [Lev, Caption]); end; end; procedure TForm1.FormCreate(Sender: TObject); var TM: TMenuItem; N: Integer; begin TM := MainMenu1.Items; TM.Add(NewItem('&Элемент', 0, False, True, AClick, 0, 'MenuItem2')); for N := 2 to 5 do begin TM.Add(TMenuItem.Create(nil)); TM := TM.Items[TM.Count - 1]; TM.Caption := '&Меню'; TM.Name := 'SubMenu' + IntToStr(N); TM.OnClick := AClick; TM.Add(NewItem('&Элемент', 0, False, True, AClick, 0, 'MenuItem' + IntToStr(N + 1))); end; MainMenu1.Items.Add(NewSubMenu('Меню&2', 0, 'SM1', [NewItem('&Элемент', 0, False, True, AClick,0,'MI2'), NewSubMenu('&Меню', 0, 'SM2', [NewItem('&Элемент', 0, False, True, AClick,0,'MI3'), NewSubMenu('&Меню', 0, 'SM3', [NewItem('&Элемент', 0, False, True, AClick, 0, 'MI4'), NewSubMenu('&Меню', 0, 'SM4', [NewItem('&Элемент', 0, False, True, AClick, 0, 'MI5'), NewSubMenu('&Меню', 0, 'SM5', [NewItem('&Элемент', 0, False, True, AClick, 0, 'MI6')])])])])])); TM := MainMenu1.Items[1]; while True do begin TM.OnClick := AClick; if TM.Count < 2 then Break; TM := TM.Items[1]; end; end; end. [News Group]




Слияние MDI-меню

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

По всей видимости, для слияния меню Delphi использует функцию InsertMenu() с параметром MF_POSITION. Тем не менее, если дочернее MDI-окно максимально развернуто, всплывающее (pop-up) меню, называемое еще контекстным, добавляется к меню MDI-приложения, на одну позицию дальше, чем необходимо. Это стандартное поведение системы, поскольку системное меню активного дочернего окна включается в первую позицию панели меню MDI-окна.

Согласно WinSDK, если активное дочернее окно максимально развертывается, вставляется новое всплывающее меню. При этом к значению позиции добавляется 1 (единица).






Динамическое создание пункта всплывающего меню

Как динамически создать пункт всплывающего меню?

Для динамического создания пункта меню необходимо создать процедуру в объекте (частный метод формы), примерно так:

procedure MyClick(Sender: TObject);
Затем, при создании нового пункта меню, назначить ему собственное событие OnClick следующим образом:
NewItem := TMenuItem.Create(Self); NewItem.Caption := 'Пункт меню'; NewItem.OnClick := MyClick;




Использование шрифтов и стилей в TMemo

Кто-нибудь знает, как использовать различные шрифты и стили в TMemo?

Создайте собственный TxxxMemo. Наследуйте от стандартного TMemo и перекройте метод Paint.

Пример, изменяющий цвет каждой строки:

unit Todrmemo; interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TOwnerDrawMemo = class(TMemo) private procedure WMPaint(var Message: TWMPaint); message WM_PAINT; end; procedure Register; implementation procedure TOwnerDrawMemo.WMPaint(var Message: TWMPaint); var Buffer: Array[0..255] of Char; PS: TPaintStruct; DC: HDC; i: Integer; X, Y, Z: Word; OldColor: LongInt; begin DC := Message.DC; if DC = 0 then DC := BeginPaint(Handle, PS); try X := 1; Y := 1; SetBkColor(DC, Color); SetBkMode(DC, Transparent); OldColor := Font.Color; for i:=0 to Pred(Lines.Count) do begin if odd(i) then SetTextColor(DC, clRed) else SetTextColor(DC, OldColor); Z := Length(Lines[i]); StrPCopy(Buffer, Lines[i]); Buffer[Z] := #0; { реально не нужно } TextOut(DC, X,Y, Buffer, Z); Inc(Y, abs(Font.Height)); end; finally if Message.DC = 0 then EndPaint(Handle, PS); end; end; procedure Register; begin RegisterComponents('Controls', [TOwnerDrawMemo]); end; end. [News Group]

Примечание
Пример написан для Delphi 1, поэтому желающие могут, разобравшись в написанном, адаптировать его к более современным версиям. А я рекомендую использовать RichEdit.




Индикатор хода выполнения в строке состояния

Решение:

procedure TForm1.Button1Click(Sender: TObject); var pb: TProgressBar; begin ... pb := TProgressBar.Create(Self); with pb do begin Parent := StatusBar1; Position := 30; Top := 2; Left := 0; Height := StatusBar1.Height - Top; Width := StatusBar1.Panels[0].Width - Left; end; pb.Visible := True; ... end; [Лихолетов Алексей]

Примечание
Для работы необходимо создать хотя бы одну панель (StatusPanel) в StatusBar.




TTrackBar для эстетов

В стандартном компоненте всегда присутствует диапазон выбора (см. на рисунке нижний TTrackBar). К сожалению, в Delphi не предусмотрена возможность его отключения. Если нет необходимости в использовании диапазона выбора, то поможет небольшая доработка компонента.

unit NTrackBar; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, CommCtrl; type TNTrackBar = class(TTrackBar) private FSelRangeVisible: Boolean; procedure SetSelRangeVisible(const Value: Boolean); protected procedure CreateParams(var Params: TCreateParams); override; public constructor Create(AOwner: TComponent); override; published property SelRangeVisible: Boolean read FselRangeVisible write SetSelRangeVisible; end; procedure Register; implementation { TNTrackBar } constructor TNTrackBar.Create(AOwner: TComponent); begin inherited Create(AOwner); FSelRangeVisible := False; ThumbLength := 18; end; procedure TNTrackBar.CreateParams(var Params: TCreateParams); begin inherited CreateParams(Params); with Params do begin if not FSelRangeVisible then Style := Style xor TBS_ENABLESELRANGE else Style := Style or TBS_ENABLESELRANGE; end; end; procedure TNTrackBar.SetSelRangeVisible(const Value: Boolean); begin if FSelRangeVisible <> Value then begin FSelRangeVisible := Value; RecreateWnd; end; end; procedure Register; begin RegisterComponents('Controls', [TNTrackBar]); end; end. [VS]




Чтение текста RichEdit из базы данных

Запись RichEdit в файл и сохранение этого файла в базе данных является наиболее простым способом сохранения текста компонента в таблице, но тот же способ может быть достигнут и без использования промежуточного файла, а именно с помощью TBlobStream. Пример, приведенный ниже, демонстрирует чтение текста RTF из таблицы.

procedure ReadRichEditFromTable(Table: TTable; var RichEdit: TRichEdit); var BlobStream: TBlobStream; begin try BlobStream := TBlobStream.Create(Table.FieldByName('BODY') as TBlobField, bmRead); if (not Table.FieldByName('BLOBFieldName').IsNull) then begin RichEdit.Lines.LoadFromStream(BlobStream); end; finally BlobStream.Free; end; end; [Лагонский Сергей]




Исправление загрузки текста RTF через поток

В версии Borland Delphi 3 Client/Server обнаружено, что при загрузке текста формата RTF методом LoadFromStream в компонент TRichEdit он не интерпретируется как RTF, а отображается полностью (со всеми управляющими символами). Ниже приведен исправленный текст реализации метода TRichEditStrings.LoadFromStream (измененные строки отмечены комментарием {!}):

procedure TRichEditStrings.LoadFromStream(Stream: TStream); var EditStream: TEditStream; Position: Longint; TextType: Longint; StreamInfo: TRichEditStreamInfo; Converter: TConversion; begin StreamInfo.Stream := Stream; if FConverter <> nil then Converter := FConverter else Converter := RichEdit.DefaultConverter.Create; StreamInfo.Converter := Converter; try with EditStream do begin dwCookie := LongInt(Pointer(@StreamInfo)); pfnCallBack := @StreamLoad; dwError := 0; end; Position := Stream.Position; if PlainText then TextType := SF_TEXT else TextType := SF_RTF; SendMessage(RichEdit.Handle, EM_STREAMIN, TextType, Longint(@EditStream)); if (TextType = SF_RTF) and (EditStream.dwError <> 0) then begin Stream.Position := Position; {!} if PlainText then TextType := SF_TEXT {!} else TextType := SF_RTF; SendMessage(RichEdit.Handle, EM_STREAMIN, TextType, Longint(@EditStream)); if EditStream.dwError <> 0 then raise EOutOfResources.Create(sRichEditLoadFail); end; finally if FConverter = nil then Converter.Free; end; end; [Лагонский Сергей]




Динамическое создание компонента TTable

Решение 1
Любой компонент можно создать и без (вне) формы или любого другого дочернего компонента. Для этого в методе Create надо указать параметр nil:

FSession := TSession.Create(nil); FDatabase := TDatabase.Create(nil); FSession.SessionName := 'DBSession' FDatabase.Connected := False; FDatabase.AliasName := Database; FDatabase.DatabaseName := USER_DATABASE; FDatabase.SessionName := FSession.SessionName; FUserTBL := TTable.Create(nil); FUserTBL.DatabaseName := FDatabase.DatabaseName; FUserTBL.SessionName := FSession.SessionName; FUserTBL.TableName := USERTBL; FUserTBL.IndexName := USERSpIndex; FUserSource := TDataSource.Create(nil); FUserSource.DataSet := FUserTBL;
Решение 2
Можно использовать TTable, не размещая компонент на форме:

function TForm1.TotalPopulation: double; var Tbl: TTable; begin Result := 0.0; Tbl := TTable.Create(nil); try Tbl.DatabaseName := 'DBDEMOS'; Tbl.TableName := 'COUNTRY'; Tbl.Open; Tbl.First; while not Tbl.EOF do begin Result := Result + Tbl.FieldByName('Population').AsFloat; Tbl.Next; end; Tbl.Close; finally Tbl.Free; end; end;




Различные цвета строк в DBCtrlGrid

Как изменить цвета строк в DBCtrlGrid?

Используйте событие PaintPanel:

procedure TForm1.DBCtrlGrid1PaintPanel(DBCtrlGrid: TDBCtrlGrid; Index: Integer); begin with DataModule2.Table1 do begin if FieldByName('INDUSTRY').AsInteger > 3600 then DBText2.Font.Color := clGreen else DBText2.Font.Color := clYellow; if FieldByName('CUR_PRICE').AsInteger > 50 then DBText3.Font.Color := clBlue else DBText3.Font.Color := clAqua; if (CompareStr(FieldByName('RCMNDATION').AsString, 'BUY') = 0) then DBText4.Font.Color := clLime else if (CompareStr(FieldByName('RCMNDATION').AsString, 'HOLD') = 0) then DBText4.Font.Color := clRed else DBText4.Font.Color := clFuchsia; end; end;




Использование опции MultiSelect в DBGrid

Данный пример реализует множественный выбор записей в табличной сетке и отображение второго поля набора данных.

Метод DisableControls применяется для того, чтобы DBGrid не обновлялся во время изменения набора данных. Последняя позиция набора данных сохраняется как Bookmark.

Метод 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; [News Group]




Сортировка колонок в DBGrid

Многие профессиональные приложения отображают данные в полях табличной сетки и разрешают сортировать любую колонку, просто щелкая по ее заголовку. То, что здесь изложено – не наилучший путь для решения задачи, данная технология не что иное, как простая имитация такого поведения компонента.

Главное препятствие в решении задачи – сам DBGrid. Проблема в отсутствии событий OnClick или OnMouseDown, позволяющих реагировать на элементарные манипуляции с заголовком. Правда, существует событие OnDoubleClick, но для данной цели оно не слишком изящно. Нам нужно лишь создать заголовок, реагирующий на однократный щелчок мышью. Обратимся к компоненту THeaderControl.

Этот компонент, введенный в палитру компонентов еще в Delphi 2.0, обеспечивает необходимые нам функции. Главное достоинство – реакция компонента при щелчке по отдельным панелям. Панели также обеспечивают визуальное отображение подобно кнопке (могут вдавливаться и отжиматься). Нам необходимо «прицепить» THeaderControl к DBGrid. Вот как это сделать:

Во-первых, создайте новое приложение. Положите THeaderControl на форму. Он автоматически выровняется по верхнему краю формы. Затем поместите на форму DBGrid и присвойте свойству Align значение alClient. Затем добавьте компоненты TTable и TDataSource. В компоненте TTable присвойте свойству DatabaseName значение DBDEMOS, а свойству TableName значение EVENTS.DB. В TDataSource укажите в свойстве DataSet на компонент Table1, а в TDBGrid в свойстве DataSource на DataSource1. Если свойство Active компонента TTable равно True, выключите его (значение False).

Сделаем так, чтобы компонент THeaderControl выглядел похожим на заголовок компонента DBGrid. Произведем необходимые манипуляции в момент создания формы. Дважды щелкните на событии OnCreate формы и введите следующий код:

procedure TForm1.FormCreate(Sender: TObject); var TheCap: String; TheWidth, a: Integer; begin DBGrid1.Options := DBGrid1.Options - [dgTitles]; HeaderControl1.Sections.Add; HeaderControl1.Sections.Items[0].Width := 12; Table1.Active := False; Table1.Exclusive := True; Table1.Active := True; for a := 1 to DBGrid1.Columns.Count do begin with DBGrid1.Columns.Items[a - 1] do begin TheCap := Title.Caption; TheWidth := Width; end; with HeaderControl1.Sections do begin Add; Items[a].Text := TheCap; Items[a].Width := TheWidth + 1; Items[a].MinWidth := TheWidth + 1; Items[a].MaxWidth := TheWidth + 1; end; try Table1.AddIndex(TheCap, TheCap, []); except HeaderControl1.Sections.Items[a].AllowClick := False; end; end; Table1.Active := False; Table1.Exclusive := False; Table1.Active := True; end; После того как THeaderControl заменил стандартный заголовок DBGrid, в первую очередь мы сбрасываем (устанавливаем в False) флаг dgTitles в свойстве Options компонента DBGrid. Затем мы добавляем колонку в HeaderControl и устанавливаем ее ширину равной 12. Это будет пустой колонкой, которая имеет ту же ширину, что и левая колонка статуса в DBGrid.

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

Теперь добавляем секции в HeaderControl. Для каждой добавленной колонки мы создаем в заголовке тот же текст, что и в соответствующей колонке DBGrid. В цикле мы проходим по всем колонкам DBGrid и повторяем текст заголовка колонки и его высоту. Мы также устанавливаем для HeaderControl значения свойств MinWidth и MaxWidth равным ширине соответствующей колонки в DBGrid. Это предохранит колонки от изменения их ширины. Для изменяющих размер колонок нужно дополнительное кодирование. Этот вопрос читателю предлагается решить самостоятельно.

Теперь самое интересное. Мы собираемся создать индекс для каждой колонки в DBGrid. Имя индекса будет таким же, как и название колонки. Данный код мы должны заключить в конструкцию try..finally, поскольку существуют некоторые поля, которые не могут быть проиндексированы (например, поля Blob и Memo). При попытке индексации этих полей генерируется исключительная ситуация. Мы перехватываем это исключение и не допускаем возможности щелчка в данной колонке. Это означает, что колонки, содержащие неиндексированные поля, не будут реагировать на щелчок мышью. Создание этих индексов объясненяет, почему таблица должна быть открыта в режиме эксклюзивного (монопольного) доступа. И в заключение мы закрываем таблицу, сбрасываем флаг эксклюзивности и снова делаем таблицу активной.

Последний шаг. При щелчке на HeaderControl нам необходимо включить правильный индекс таблицы. Создадим обработчик события OnSectionClick компонента HeaderControl как показано ниже:

procedure TForm1.HeaderControl1SectionClick(HeaderControl: THeaderControl; Section: THeaderSection); begin Table1.IndexName := Section.Text; end; После щелчка в заголовке колонки значение свойства таблицы IndexName становится равным заголовку компонента HeaderControl.

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

Улучшения
Здесь приведен код, улучшенный по сравнению с предыдущей версией «Совета», он заключается в использовании в качестве имени индекса имя поля вместо заголовка.

Это улучшает гибкость.

procedure TForm1.FormCreate(Sender: TObject); var TheCap: String; TheFn: String; TheWidth: Integer; a: Integer; begin Table1.Active := True; DBGrid1.Options := DBGrid1.Options - [DGTitles]; HeaderControl1.Sections.Add; HeaderControl1.Sections.Items[0].Width := 12; for a := 1 to DBGrid1.Columns.Count do begin with DBGrid1.Columns.Items[a - 1] do begin TheFn := FieldName; TheCap := Title.Caption; TheWidth := Width; end; with Headercontrol1.Sections do begin Add; Items[a].Text := TheCap; Items[a].Width := TheWidth + 1; Items[a].MinWidth := TheWidth + 1; Items[a].MaxWidth := TheWidth + 1; end; try { Используем индексы с тем же именем, что и имя поля } { Пробуем задать имя индекса } (DataSource1.Dataset as TTable).IndexName := TheFn; except HeaderControl1.Sections.Items[a].AllowClick := False; { Индекс недоступен } end; end; end; Используем свойство FieldName компонента DBGrid для задания индекса с тем же именем, что и имя поля.

procedure TfrmDoc.HeaderControl1SectionClick(HeaderControl: THeaderControl; Section: THeaderSection); begin (DataSource1.Dataset as TTable).IndexName := DBGrid1.Columns.Items[Section.Index - 1].FieldName; end; Примечание

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




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

Есть ли какой-либо способ придать ячейке DBGrid другой цвет? Мне хотелось бы выделить отдельные ячейки строки по определенному признаку. Типа флага, который, если счет просрочен свыше 90 дней, делает строчку красной.

Решение 1
Здесь показано, как изменить цвет отдельных ячеек GBGrid без создания нового компонента.

Создайте форму, поместите на нее компонент TTable и укажите ему на таблицу EMPLOYEE.DB в базе данных DBDEMOS. Затем разместите на форме DataSource и DBGrid, «соедините» их и вы получите «живые» данные.

Для демонстрации данной технологии выбрано поле Номер служащего в таблице EMPLOYEE.DB «покрашены» ячейки с нечетными числами. То есть, если число нечетное, красим ячейку в зеленый цвет.

Единственный код расположился в обработчике события OnDrawColumnCell компонента DBGrid и выглядел он так:

procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); var holdColor: TColor; begin holdColor := DBGrid1.Canvas.Brush.Color; { сохраняем оригинальный цвет } if Column.FieldName = 'EmpNo' then { "раскрашиваем" ячейки только для поля EmpNo } if (Column.Field.AsInteger mod 2 <> 0) then begin DBGrid1.Canvas.Brush.Color := clGreen; DBGrid1.DefaultDrawColumnCell(Rect, DataCol, Column, State); DBGrid1.Canvas.Brush.Color := holdColor; end; end; В данном случае вызывается метод DefaultDrawColumnCell компонента TCustomDBGrid, являющийся родителем для TDBGrid. Он раскрашивает зеленым цветом нечетные ячейки поля EmpNo.

Решение 2
Обработайте событие OnDrawDataCell. Вот пример, который использует демонстрационную таблицу COUNTRY и выводит текст красным цветом во всех строках, содержащих страны с населением свыше 10 миллионов человек:

procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect; Field: TField; State: TGridDrawState); begin if Table1.FieldByName('Population').AsFloat > 10000000 then DBGrid1.Canvas.Font.Color := clRed; dbGrid1.DefaultDrawDataCell(Rect, Field, State); end; Примечание
Borland не рекомендует использовать в новых разработках обработчик события OnDrawDataCell, которому пришел на смену обработчик OnDrawColumnCell. Старый вызов сохранен для совместимости.




TDBGrid -– копирование в буфер обмена

Простая процедура копирования информации из DBGrid в Clipboard может существенно облегчить жизнь при реализации требований экспорта выборок данных во внешние приемники:

unit UnGridToClb; interface uses Windows, SysUtils, Classes, Dialogs, Grids, DBGrids, Db, DBTables, ClipBrd; procedure CopyGRDToClb(dbg :TDBGrid); // Копирует DBGrid в буфер обмена, после чего данные отлично переносятся // как в простой текстовый редактор, так и в Excel implementation procedure CopyGRDToClb(dbg: TDBGrid); var bm: TBookMark; pch, pch1: PChar; s, s2: string; i, j: integer; begin s := ''; for j := 0 to dbg.Columns.Count - 1 do s := s + dbg.Columns.Items[j].Title.Caption +#9 ; s := s + #13 + #10; if not dbg.DataSource.DataSet.active then begin ShowMessage('Нет выборки!!!'); Exit; end; try dbg.Visible := False; // Делаем сетку невидимой, чтобы не уходило время // на ее перерисовку при прокрутке DataSet bm := dbg.DataSource.DataSet.GetBookmark; // чтобы не потерять текущую запись dbg.DataSource.DataSet.First; while not dbg.DataSource.DataSet.EOF do begin s2 := ''; for j := 0 to dbg.Columns.Count - 1 do begin s2 := s2 + dbg.Columns.Items[j].Field.AsString + #9; end; s := s + s2 + #13 + #10; dbg.DataSource.DataSet.Next; end; // Переключаем клавиатуру "в русский режим", иначе - проблемы с кодировкой GetMem(pch, 100); GetMem(pch1, 100); GetKeyboardLayoutName(pch); StrCopy(pch1, pch); while pch <> '00000419' do begin ActivateKeyboardLayout(HKL_NEXT, 0); GetKeyboardLayoutName(pch); if StrComp(pch, pch1) = 0 then // Круг замкнулся - нет такого языка '00000419' StrCopy(pch, '00000419'); end; Clipboard.AsText := s; // Данные - в буфер!!! while strComp(pch, pch1) <> 0 do begin // Возвращаем режим клавиатуры ActivateKeyboardLayout(HKL_NEXT, 0); GetKeyboardLayoutName(pch); end; FreeMem(pch); FreeMem(pch1); dbg.DataSource.DataSet.GotoBookmark(bm); // ShowMessage('Данные успешно скопированы в буфер обмена.'); finally dbg.Visible := True; end; end; end. [Беличенко Б]




Заголовок в DBGrid

Как создать шапку в DBGrid?

Решение:

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; 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; public constructor Create(AOwner : TComponent);override; destructor Destroy; override; published property OnDrawTitle : TOnDrawTitleEvent read FOnDrawTitle write FOnDrawTitle; property RealTitleFont : TFont read FRealTitleFont write SetRealTitleFont; 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); 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; begin with DrawBitmap, ARect do begin 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 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.




Отображение логотипа при запуске приложения

Решение:

program Project1; uses Forms, Unit1 in 'Unit1.pas' {Form1}; ULogo in 'ULogo.pas' {LogoForm}; {$R *.RES} begin Application.Initialize; {до этого момента никаких изменений} with TLogoForm.Create(Application) do try Show; Update; Application.CreateForm(TForm1, Form1); { GProgress.AddProgress(1); - здесь можно двигать прогресс, если TGauge или TProgressBar лежат на TLogoForm, если есть еще формы, то Application.CreateForm(TForm2, Form2); и т.д.} finally Free; end; Application.Run; end. [Алексей]




Поддержка команд Cut, Copy, Paste

Решение 1
Предлагаем следующие процедуры. Вызывайте их при выборе соответствующих пунктов меню. Это будет работать со всеми «редактируемыми» элементами управления. Но для TTree нужно использовать специальные сообщения редактирования.

procedure TForm1.CopyClick(Sender: TObject); var Mes: TWMCopy; begin Mes.Msg := WM_COPY; Screen.ActiveControl.Dispatch(Mes); end; procedure TForm1.CutClick(Sender: TObject); var Mes: TWMCut; begin Mes.Msg := WM_CUT; Screen.ActiveControl.Dispatch(Mes); end; procedure TForm1.PasteClick(Sender: TObject); var Mes: TWMPaste; begin Mes.Msg := WM_PASTE; Screen.ActiveControl.Dispatch(Mes); end; procedure TForm1.UndoClick(Sender: TObject); var Mes: TWMUndo; begin Mes.Msg := WM_UNDO; Screen.ActiveControl.Dispatch(Mes); end; [Shejchenko Andrij]

Решение 2
Свойство формы ActiveControl позволяет получить ссылку на активный в данный момент элемент управления. Но не все элементы управления могут работать с буфером обмена. Если хотите работать только с компонентами Edit и Memo, то самый простой метод для CopyToClipboard:

ActiveControl.Perform(WM_COPY, 0, 0); Для PasteFromClipboard: ActiveControl.Perform(WM_PASTE, 0, 0); Если элемент управления «не понимает» посланных сообщений, то это никак не скажется на его работе, он просто проигнорирует их.

Другим способом является проверка типа во время выполнения приложения:

if ActiveControl is TCustomEdit then TCustomEdit(ActiveControl).CopyToClipboard; [News Group]

Решение 3
Реализация команд Cut, Copy и Paste средствами WinAPI:

SendMessage(GetFocus, WM_CUT, 0, 0); SendMessage(GetFocus, WM_COPY, 0, 0); SendMessage(GetFocus, WM_PASTE, 0, 0); [News Group]

Решение 4
Есть два шага, положенных в основу работы с буфером обмена . Во-первых, нужно знать, какие пункты меню Правка должны быть в данный момент активизированы. Во-вторых, необходимо работать с тем элементом управления, который в данный момент выбран.

procedure TForm1.Edit1Click(Sender: TObject); begin if ActiveControl is TCustomEdit then begin with TCustomEdit(ActiveControl) do begin Cut1.Enabled := SelLength > 0; Copy1.Enabled := SelLength > 0; Paste1.Enabled := ClipBoard.HasFormat(CF_TEXT); end; end else begin Cut1.Enabled := False; Copy1.Enabled := False; Paste1.Enabled := False; end; end; procedure TForm1.Cut1Click(Sender: TObject); begin if ActiveControl is TDBEdit then with TDBEdit(ActiveControl).DataSource.DataSet do Edit; TCustomEdit(ActiveControl).CutToClipboard; if ActiveControl is TDBEdit then with TDBEdit(ActiveControl).DataSource.DataSet do Post; end; procedure TForm1.Copy1Click(Sender: TObject); begin TCustomEdit(ActiveControl).CopyToClipboard; end; procedure TForm1.Paste1Click(Sender: TObject); begin if ActiveControl is TDBEdit then with TDBEdit(ActiveControl).DataSource.DataSet do Edit; TCustomEdit(ActiveControl).PasteFromClipboard; if ActiveControl is TDBEdit then with TDBEdit(ActiveControl).DataSource.DataSet do Post; end; Edit1 – меню редактирования верхнего уровня. Если по нему щелкают, то прежде чем меню «вывалится» вниз, необходимо проверить, принадлежит ли текущий активный элемент управления некоторым типам редактирования. Если это условие выполняется, активизируются пункты меню Вырезать и Копировать и, если есть текст в буфере обмена, то и пункт Вставить. Если нет, то все три пункта будут недоступны.

Для копирования содержимого элемента редактирования достаточно просто вызвать CopyToClipboard; это не проблема. Для вырезания и вставки необходимо «изменить» содержимое активного элемента редактирования – если это DBEdit, необходимо перейти в режим редактирования и после манипуляций с данными буфера обмена сохранить измененные данные.




Назад к части 3



При перепечатке любого материала с сайта, видимая ссылка на источник www.warayg.narod.ru и все имена, ссылки авторов обязательны.

© 2005
 

Hosted by uCoz