Советы начинающим

         

Как "быструю подсказку" (Hints) сделать многострочной?


Необходимо создать соответствующую компоненту которая показывает "быструю подсказку" (Hints) с более чем одной строкой. Компонента наследуется от TComponent и называется TMHint. Hint-текст можно задавать следующим образом: "Строка 1@Строка 2@Строка 3". Символ '@' используется как разделитель строк. Если Вам нравится другой символ - измените свойство Separator. Свойство Active указывает на активность (TRUE) или неактивность (FALSE) "многострочности".

unit MHint;

interface

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

type TMHint = class(TComponent) private ScreenSize: Integer; FActive: Boolean; FSeparator: Char; FOnShowHint: TShowHintEvent; protected procedure SetActive(Value: Boolean); procedure SetSeparator(Value: char); procedure NewHintInfo(var HintStr: string; var CanShow: Boolean; var HintInfo: THintInfo); public constructor Create(AOwner: TComponent); override; published property Active: Boolean read FActive write SetActive; property Separator: Char read FSeparator write SetSeparator; end;

procedure Register;

implementation

constructor TMHint.Create(AOwner: TComponent);

begin inherited Create(AOwner); FActive := True; FSeparator := '@'; Application.OnShowHint := NewHintInfo; ScreenSize := GetSystemMetrics(SM_CYSCREEN); end;

procedure TMHint.SetActive(Value: Boolean);

begin FActive := Value; end;

procedure TMHint.SetSeparator(Value: Char);

begin FSeparator := Value; end;

procedure TMHint.NewHintInfo(var HintStr: string; var CanShow: Boolean; var HintInfo: THintInfo);

var I: Byte;

begin if FActive then begin I := Pos(FSeparator, HintStr); while I > 0 do begin HintStr[I] := #13; I := Pos(FSeparator, HintStr); end; if HintInfo.HintPos.Y+10 > ScreenSize then HintInfo.HintPos.Y := ScreenSize-11; end; end;

procedure Register;

begin RegisterComponents('MyComponents', [TMHint]); end;

end.





Или вот ещё .



Как через индекс обратиться к нескольким компонентам?


Чтобы найти и сделать видимыми, например, компоненты с именами от "Label1" и до "Label5" можно использовать следующий вариант:

for t := 1 to 5 do

FindComponent('Label' + IntToStr(t)).Visible := TRUE;





Как эффективно организовать объявление глобальных переменных?


Один из эффективных способов организации глобальных переменных - создать отдельный Unit и в Interface-части объявить все необходимые переменные (и, естесственно, константы ежели таковые имеются). Если теперь в других модулях записать этот Unit в uses раздел, то все глобальные переменные можно использовать в рамках целого проекта (или проектов).



Как экспортировать таблицу базы данных в ASCII-файл?


procedure TMyTable.ExportToASCII;

var

I: Integer; Dlg: TSaveDialog; ASCIIFile: TextFile; Res: Boolean;

begin

if Active then

if (FieldCount > 0) and (RecordCount > 0) then

begin

Dlg := TSaveDialog.Create(Application); Dlg.FileName := FASCIIFileName; Dlg.Filter := 'ASCII-Fiels (*.asc)|*.asc'; Dlg.Options := Dlg.Options+[ofPathMustExist, ofOverwritePrompt, ofHideReadOnly]; Dlg.Title := 'Экспоритровать данные в ASCII-файл'; try

Res := Dlg.Execute; if Res then

FASCIIFileName := Dlg.FileName; finally

Dlg.Free; end; if Res then

begin

AssignFile(ASCIIFile, FASCIIFileName); Rewrite(ASCIIFile); First; if FASCIIFieldNames then

begin

for I := 0 to FieldCount-1 do

begin

Write(ASCIIFile, Fields[I].FieldName); if I <> FieldCount-1 then

Write(ASCIIFile, FASCIISeparator); end; Write(ASCIIFile, #13#10); end; while not EOF do

begin

for I := 0 to FieldCount-1 do

begin

Write(ASCIIFile, Fields[I].Text); if I <> FieldCount-1 then

Write(ASCIIFile, FASCIISeparator); end; Next; if not EOF then

Write(ASCIIFile, #13#10); end; CloseFile(ASCIIFile); if IOResult <> 0 then

MessageDlg('Ошибка при создании или переписывании '+ 'в ASCII-файл', mtError, [mbOK], 0); end; end

else

MessageDlg('Нет данных для экспортирования.', mtInformation, [mbOK], 0) else

MessageDlg('Таблица должна быть открытой, чтобы данные '+ 'можно было экспортировать в ASCII-формат.', mtError, [mbOK], 0); end;





Как копировать и вставлять Bitmap через буфер обмена?


Некоторые функции для копирования и вставки Bitmap-объектов через буфер обмена.

function CopyClipToBuf(DC: HDC; Left, Top, Width, Height: Integer; Rop: LongInt; var CopyDC: HDC; var CopyBitmap: HBitmap): Boolean;

var

TempBitmap: HBitmap;

begin

Result := False; CopyDC := 0; CopyBitmap := 0; if DC <> 0 then

begin

CopyDC := CreateCompatibleDC(DC); if CopyDC <> 0 then

begin

CopyBitmap := CreateCompatibleBitmap(DC, Width, Height); if CopyBitmap <> 0 then

begin

TempBitmap := CopyBitmap; CopyBitmap := SelectObject(CopyDC, CopyBitmap); Result := BitBlt(CopyDC, 0, 0, Width, Height, DC, Left, Top, Rop); CopyBitmap := TempBitmap; end; end; end; end;

function CopyBufToClip(DC: HDC; var CopyDC: HDC; var CopyBitmap: HBitmap; Left, Top, Width, Height: Integer; Rop: LongInt; DeleteObjects: Boolean): Boolean;

var

TempBitmap: HBitmap;

begin

Result := False; if (DC <> 0) and

(CopyDC <> 0) and

(CopyBitmap <> 0) then

begin

TempBitmap := CopyBitmap; CopyBitmap := SelectObject(DC, CopyBitmap); Result := BitBlt(DC, Left, Top, Width, Height, CopyDC, 0, 0, Rop); CopyBitmap := TempBitmap; if DeleteObjects then

begin

DeleteDC(CopyDC); DeleteObject(CopyBitmap); end; end; end;





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


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

Variable := GetFreeSystemResources(GFSR_SYSTEMRESOURCES);

Другие функции опроса параметров системы (описаны в системе помощи): GetWinFlags, SystemParametersInfo, GetDeviceCaps и GetFreeSpace.



Для 32-bit систем необходимо вызывать GetSystemMetrics, GetSystemInfo и SystemParametersInfo.



Как перейти к указанной записи в БД?


Демонстрация перехода к указанной записи через задание номера записи.

function TBDEDirect.GoToRecord(RecNo: LongInt): Boolean; var RecCount: LongInt; Bookmark: TBookmark; Res: DBIResult; begin Result := False; if CheckDatabase then begin if RecNo < 1 then RecNo := 1; RecCount := GetRecordCount; if RecNo > RecCount then RecNo := RecCount; Res := DbiSetToRecordNo(FDataLink.DataSource.DataSet.Handle, RecNo); if Res = 0 then begin Bookmark := StrAlloc(GetBookmarkSize); DbiGetBookmark(FDataLink.DataSource.DataSet.Handle, Bookmark); FDataLink.DataSource.DataSet.GoToBookmark(Bookmark); FDataLink.DataSource.DataSet.FreeBookmark(Bookmark); Result := True; end else Check(Res); end; end;





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


Следующая функция проверяет доступ к базе данных и выдает возможные причины, если доступ не удается осуществить. Функция возвращает значение True в случае успешной операции и False в противном случае.

function TBDEDirect.CheckDatabase: Boolean; var

DS: TDataSource; begin

Result := False; DS := GetDataSource; if DS = nil then

begin

MessageDlg('Не установлена связь с элементом-источником данных.'+ 'Проверьте установку свойства DataSource.', mtError, [mbOK], 0); Exit; end; if DS.DataSet = nil then

begin

MessageDlg('Доступ к базе данных невозможен.', mtError, [mbOK], 0); Exit; end; if TDBDataSet(DS.DataSet).Database = nil then

begin

MessageDlg('Доступ к базе данных невозможен.', mtError, [mbOK], 0); Exit; end; if TDBDataSet(DS.DataSet).Database.Handle = nil then

begin

MessageDlg('Дескриптор (Handle) БД недоступен.', mtError, [mbOK], 0); Exit; end; if DS.DataSet.Handle = nil then

begin

MessageDlg('Дескриптор курсора (Cursor-Handle) недоступен.', mtError, [mbOK], 0); Exit; end; Result := True; end;





Как различаются между собой Paint-события: Invalidate, Update и Refresh?








Как сделать отступ в Memo?


С помощью API-функции SendMessage можно задать поля в Memo-компоненте. Если необходимо, например, сделать отступ в 20 пикселей слева то можно это сделать следующим образом:

var Rect: TRect; begin SendMessage( Memo1.Handle, EM_GETRECT, 0, LongInt(@Rect)); Rect.Left:= 20; SendMessage(Memo1.Handle, EM_SETRECT, 0, LongInt(@Rect)); Memo1.Refresh; end;





Как сделать возможным передвижение


В следующем примере показано как можно передвигать форму если пользователь "захватил" Client-пространство. Наиболее простое решение - "обмануть" Windows и Client-пространство выдать за заголовок окна.

unit DragMain;

interface

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

type TForm1 = class(TForm) Button1: TButton; procedure ButtonClick(Sender: TObject); private procedure WMNCHitTest(var M: TWMNCHitTest); message wm_NCCHitTest; end;

var Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1. WMNCHitTest(var M: TWMNCHitTest);

begin inherited; if M.Result = htClient then M.Result := htCaption; end;

procedure TForm1.Button1Click(Sender: TObject);

begin Close; end;

end.





Как скрыть форму при старте приложения?


К сожалению при создании окна приложения ни в одном из первых событий (OnCreate, OnShow, OnActivate) нет доступа к свойству Visible. Использование функции ShowWindow с параметром SW_HIDE в OnActivate-событии решает проблему (но при этом не избежать кратковременного мерцания окна перед "исчезновением"):

procedure TForm1.OnActivate(Sender: TObject); begin

ShowWindow(Handle, SW_HIDE); end;

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

ShowWindow(Handle, SW_SHOW);

Если Вам лень пользовать API-функции или неприятно видеть мерцание, то есть другой, весьма интересный способ сокрыть окно приложения от любопытствующих глаз:

procedure TForm1.OnCreate(Sender: TObject); begin

Left := Screen.Width; end;

Окно создается видимым, но находится за пределами экрана!





Как скрыть свойства объекта?


В иерархии VCL в большинстве случаев существует уровень объектов-"предшественников" (TCustomXXXX), в которых многие свойства скрыты. Для унаследованных от таких "предшественников" объектов можно "открывать" на выбор те или иные свойства. А как можно сокрыть свойства, которые объявлены в published-области от Object Inspector'а, но при этом оставить возможность доступа во время работы программы? Решение состоит в объявлении свойства "по новой" в public-области. В примере скрытым будет у объекта TMyControl свойство Height.

TMyControl = class(TWinControl) protected procedure SetHeight(Value: Integer); function GetHeight: Integer; public property Height: Integer read GetHeight write SetHeight; end;

procedure TMyControl.SetHeight(Value: Integer); begin inherited Height := Value; end;

function TMyControl.GetHeight; begin Result := inherited Height; end;





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


Для этого необходимо создать новый курсор(ы) в подходящем для этого редакторе ресурсов (например борландовский Resource Workshop). При этом надо обратить внимание на то что имена в редакторе ресурсов (особенно в том, который поставляется с Delphi) надо писать заглавными буквами. После этого "перед внутренним употреблением" (лучше всего в процедуре обработки события OnCreate главной формы) необходимо загрузить курсор(ы) из res-файла как указано ниже:

{$I CURSOR.RES}

Screen.Cursors[1] := LoadCursor(hInstance, 'CURSOR_1'); Button1.Cursor := 1;

Обратите внимание на то, что системные курсоры в Screen.Cursors начинаются с нуля и идут в минусовом направлении. Поэтому при создании новых курсоров лучше выбирать положительные числа (лучше не слишком большие :-)).

Более удобный вариант - это объявить постоянную (равную например 12):

const

CUR_HAND = 12;

...

Screen.Cursors[CUR_HAND] := LoadCursor(hInstance, 'CURSOR_HAND'); Button1.Cursor := CUR_HAND;





Как создать и вызвать модальный формуляр?


Модальные формуляры довольно часто выгоднее (с точки зрения использования памяти) временно создавать, и после того, как формуляр был вызван, отработан и закрыт, - освободить его из памяти. В противном случае Delphi-приложение может быть настоящим "пожирателем памяти". Примерно так может выглядеть вызов такого формуляра:

ModalForm := TModalForm.Create(Self); try

ModalForm.ShowModal; finally

ModalForm.Free; end;





Как создать Ini-файл в директории программы?




По умолчанию ini-файл создается в Windows-директории (например: TIniFile.Create('FOO.INI' )), что приводит к "захламлению" оной. Более (эко-)логично (за исключением случаев, когда программа делается для CD-ROM) если ini-файл создается в той же директории что и главная программа. Это достигается с помощью одной строки:

IniFile := TIniFile.Create(ChangeFileExt(ParamStr(0),'.INI'));





Как сравнить bookmarks в таблице?


function TBDEDirect.CompareBookmarks(Bookmark1, Bookmark2: TBookmark): Boolean;

var

Res: DBIResult; CompareRes: Word;

begin

Result := False; if CheckDatabase then

begin

Res := DbiCompareBookmarks(FDataLink.DataSource.DataSet.Handle, Bookmark1, Bookmark2, CompareRes); if Res = 0 then

if CompareRes = 0 then

Result := True else

else

Check(Res); end; end;





Как управлять табуляцией в Memo-компоненте?


Для этого необходимо поставить свойство "WantTabs" в "True". Теперь для управления штртной табуляции необходимо вызвать API-функцию SendMessage. В последнем параметре функция ожидает указатель на массив с элементами типа Word, в котором указаны позиции в единицах экрана. Для примера вызов функции с указанием двух позиций табуляции:

procedure TForm1.FormCreate(Sender:TObject); const Tabs: array[0..1] of Word = (4, 8); begin SendMessage(Memo1.Handle, EM_SetTabStops, 2, LongInt(@Tabs)); end;





Как ускорить навигацию по исходному тексту?


Весьма удобно при работе с текстами больше двух-трех страниц использовать метки (bookmarks).

Чтобы установить подобную метку - необходимо нажать компбинацию клавиш: CTRL+SHIFT+Число, где "Число" находится в пределах от 0 до 9. После этого можно с любого места "прыгнуть" на эту метку. Для этого необходимо нажать комбинацию CTRL+Число.

Примечание: Чтобы убрать метку - необходимо второй раз нажать CTRL+SHIFT+Число, а чтобы передвинуть метку на другое место - необходимо нажать ту же комбинацию в необходимом месте.





Как установить минимальные размеры формуляра?


Если свойство BorderStyle установленно в значение bsSizeable, то размер формуляра можно уменьшать или увеличивать. Иногда, чтобы показывать все необходимые элементы на форме, необходимо установить минимальные размеры формуляра? Следующая процедура позволяет ограничить размеры формуляра.

type TForm1 = class(TForm) procedure wmGetMinMaxInfo(var Msg : TMessage); message wm_GetMinMaxInfo;

procedure TForm1.wmGetMinMaxInfo(var Msg : TMessage);

begin PMinMaxInfo(Msg.lParam)^.ptMinTrackSize.X := 600; PMinMaxInfo(Msg.lParam)^.ptMinTrackSize.Y := 350; end;





Как узнать, находится ли дискета в дисководе?


type

TDriveState(DS_NO_DISK, DS_UNFORMATTED_DISK, DS_EMPTY_DISK, DS_DISK_WITH_FILES);

function DriveState(DrvLetter: Char): TDriveState;

var

Mask: String[6]; SearchRec: TSearchRec; oldMode: Cardinal; ReturnCode: Integer;

begin

oldMode: = SetErrorMode(SEM_FAILCRITICALERRORS); Mask:= '?:\*.*'; Mask[1] := DrvLetter; {$I-} { отключить обработку исключительных ситуаций }

ReturnCode := FindFirst(Mask, faAnyfile, SearchRec); FindClose(SearchRec);

{$I+}

case ReturnCode of

{ как минимум один файл был найден }

0: Result := DS_DISK_WITH_FILES; { файлов не найдено и дискета в порядке }

-18: Result := DS_EMPTY_DISK; { DS_NO_DISK для DOS, ERROR_NOT_READY для WinNT, ERROR_PATH_NOT_FOUND для Win 3.1 }

-21, -3: Result := DS_NO_DISK; else

{ дискета лежит в дисководе но она не форматировнная }

Result := DS_UNFORMATTED_DISK; end; SetErrorMode(oldMode); end; { DriveState }





Как узнать содержание активной записи в БД?


Следующая функция возвращает в виде указателя на строку содержание активной записи в БД.

function TBDEDirect.GetCurRecord(Lock: DBILockType): PChar;

var

Res: DBIResult; RecSize: Word; RecBuf: PChar; Bookmark: TBookmark;

begin

Result := StrNew(''); if CheckDatabase then

begin

RecSize := GetPhysicalRecSize; RecBuf := StrAlloc(RecSize+1); FillChar(RecBuf^, RecSize+1, #0); Bookmark := FDataLink.DataSource.DataSet.GetBookmark; DbiSetToBookmark(FDataLink.DataSource.DataSet.Handle, Bookmark); FDataLink.DataSource.DataSet.FreeBookmark(Bookmark); Res := DbiGetRecord(FDataLink.DataSource.DataSet.Handle, Lock, RecBuf, nil); if Res = 0 then

Result := RecBuf else

Check(Res); end; end;





Как выдать текст под наклоном?


Чтобы вывести под любым углом текст необходимо использовать TrueType Fonts (например "Arial"). Например:

var LogFont : TLogFont;

... GetObject(Canvas.Font.Handle, SizeOf(TLogFont), @LogFont); { Вывести текст 1/10 градуса против часовой стрелки }

LogFont.lfEscapement := Angle*10; Canvas.Font.Handle := CreateFontIndirect(LogFont);



(3кб)



Как выделить окошко DBGrid другим цветом?




Необходимо обработать событие "OnDrawCellData". Например для того, чтобы пометить выбранное окошко красным фоном, необходимо сделать следующее:

procedure TForm1.DBGridDrawDataCell(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;





Как выяснить дату последнего изменения файла?


Для выяснения даты последнего изменения файла можно воспользоваться следующей функцией:

function GetFileDate(FileName: string): string;

var

FHandle: Integer;

begin

FHandle := FileOpen(FileName, 0); try

Result := DateTimeToStr(FileDateToDateTime(FileGetDate(FHandle))); finally

FileClose(FHandle); end; end;





Как выяснить положение курсора в МЕМО?


Необходимо вызвать дважды API-функцию "SendMessage":

var xChr, xRow, xCol: LongInt; ...

xRow := SendMessage(Memo1.Handle, EM_LINEFROMCHAR, Memo1.SelStart, 0); xChr := SendMessage(Memo1.Handle, EM_LINEINDEX, Zeile, 0); xCol := Memo1.SelStart - xChr + 1;





Как выяснить размер BLOB-поля?


Следующая функция поможет определить размер BLOB-поля.

Function GetBlobSize(Field: TBlobField): LongInt;

begin

with TBlobStream.Create(Field, bmRead) do try

Result := Seek(0, 2); finally

Free; end; end;





Как вывести главное окно справочной системы?


В 16-битных версиях справочной системы необходимо было вызывать начальное (главное) окно помощи с параметром HELP_CONTENTS в комманде HelpCommand. В 32-битном варианте это осуществляется следующим образом:

Application.HelpCommand(HELP_FINDER, 0);

Примечание: Для того, чтобы показывались "книжки" (или главные темы) - необходимо создать .CNT-файл.



Как вывести окно перед другими окнами не выводя наверх родительское окно?


Если это не мешает общему ходу программы, то одно из самых простых решений - использование рабочего стола (Desktop) как родительское окно. Для этого необходимо перекрыть CreateParams подчиненного окна следующим образом:

...

private

procedure CreateParams(var Params: TCreateParams); override;

...

procedure TForm2.CreateParams(var Params: TCreateParams);

begin

inherited CreateParams(Params); Params.WndParent := GetDesktopWindow; end;





Как выводить формуляр в центр экрана?


Часто встречающийся вопрос начинающего программиста. Чтобы вывести формуляр в центре экрана необходимо (в самом простом случае) изменить свойство формы Position. Значение должно быть установленно в poScreenCenter. Причем это можно делать как в Object Inspector, так и во время работы программы.



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


В Delphi-приложении можно вызвать помощь в пользовании системой помощи следующим образом:

Application.HelpCommand(Help_HelpOnHelp, 0);





Как закрыть окно подсказки если пользователь закончил приложение?


В следующем примере показано как закрыть окно помощи если пользователь закончил приложение.

procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);

begin

Winhelp(Handle, 'WINHELP.HLP', HELP_QUIT, 0); Action := caFree; end;





Как "заствавить" формуляр не разворачиваться из иконки?


Для этого необходимо перехватить сообщение "WM_QUERYOPEN". Для этого необходимо объявить соответствующую процедуру в private-области.

{ объявление процедуры в классе TMainForm }

procedure WMQueryOpen(var Msg: TWMQueryOpen); message WM_QUERYOPEN;

{ ... и ее реализация }

procedure TMainForm.WMQueryOpen(var Msg: TWMQueryOpen); begin Msg.Result := 0; end;











Invalidate      весь объект перерисовывается заново; обычно перерисовывается только часть бывшая перед этим закрытой
Update незамедлительная перерисовка
Refresh Invalidate + Update