|
Раздел "Кодинг". Содержание:
Статьи:
Забавное программирование в Delphi
Перемещение Image'a по форме во время работы приложения средствами Delphi
Пособие по написанию своих компонентов на Delphi для начинающих
Раздел "Кодинг". Статьи:
 | Забавное программирование в Delphi |
|
Приведённый здесь материал можно озаглавить не иначе как "Чем заняться программисту, если нечего делать". На самом деле, Delphi настолько интересная среда, что в ней наряду с разработкой серьёзных приложений можно легко увлечься созданием абсолютно бесполезных вещей.
Итак, поехали...
Автоматически нажимающаяся кнопка
Этот компонент представляет из себя кнопку, на которую не надо нажимать, чтобы получить событие OnClick. Достаточно переместить курсор мышки на кнопку. При создании такого компонента традиционным способом, требуется довольно много времени, так как необходимо обрабатывать мышку, перехватывать её и т.д. Однако результат стоит того!
Предлагаю взглянуть на две версии данного компонента. В более простой версии обработчик перемещения мышки просто перехватывает сообщения Windows с нужным кодом и вызывает обработчик события OnClick:
type
TAutoButton1 = class(TButton)
private
procedure WmMouseMove (var Msg: TMessage);
message wm_MouseMove;
end;
procedure TAutoButton1.WmMouseMove (var Msg: TMessage);
begin
inherited;
if Assigned (OnClick) then
OnClick (self);
end;
Вторая версии имеет больше исходного кода, так как в ней я просто пытаюсь повторить событие мышки OnClick когда пользователь перемещает мышку над кнопкой либо по истечении определённого времени. Далее следует объявление класса:
type
TAutoKind = (akTime, akMovement, akBoth);
TAutoButton2 = class(TButton)
private
FAutoKind: TAutoKind;
FMovements: Integer;
FSeconds: Integer;
// really private
CurrMov: Integer;
Capture: Boolean;
MyTimer: TTimer;
procedure EndCapture;
// обработчики сообщений
procedure WmMouseMove (var Msg: TWMMouse);
message wm_MouseMove;
procedure TimerProc (Sender: TObject);
procedure WmLBUttonDown (var Msg: TMessage);
message wm_LBUttonDown;
procedure WmLButtonUp (var Msg: TMessage);
message wm_LButtonUp;
public
constructor Create (AOwner: TComponent); override;
published
property AutoKind: TAutoKind
read FAutoKind write FAutoKind default akTime;
property Movements: Integer
read FMovements write FMovements default 5;
property Seconds: Integer
read FSeconds write FSeconds default 10;
end;
Итак, когда курсор мышки попадает в область кнопки (WmMouseMove), то компонент запускает таймер либо счётчик количества сообщений о перемещении. По истечении определённого времени либо при получении нужного количества сообщений о перемещении, компонент эмулирует событие нажатия кнопкой.
procedure TAutoButton2.WmMouseMove (var Msg: TWMMouse);
begin
inherited;
if not Capture then
begin
SetCapture (Handle);
Capture := True;
CurrMov := 0;
if FAutoKind <> akMovement then
begin
MyTimer := TTimer.Create (Parent);
if FSeconds <> 0 then
MyTimer.Interval := 3000
else
MyTimer.Interval := FSeconds * 1000;
MyTimer.OnTimer := TimerProc;
MyTimer.Enabled := True;
end;
end
else // захватываем
begin
if (Msg.XPos > 0) and (Msg.XPos < Width)
and (Msg.YPos > 0) and (Msg.YPos < Height) then
begin
// если мы подсчитываем кол-во движений...
if FAutoKind <> akTime then
begin
Inc (CurrMov);
if CurrMov >= FMovements then
begin
if Assigned (OnClick) then
OnClick (self);
EndCapture;
end;
end;
end
else // за пределами... стоп!
EndCapture;
end;
end;
procedure TAutoButton2.EndCapture;
begin
Capture := False;
ReleaseCapture;
if Assigned (MyTimer) then
begin
MyTimer.Enabled := False;
MyTimer.Free;
MyTimer := nil;
end;
end;
procedure TAutoButton2.TimerProc (Sender: TObject);
begin
if Assigned (OnClick) then
OnClick (self);
EndCapture;
end;
procedure TAutoButton2.WmLBUttonDown (var Msg: TMessage);
begin
if not Capture then
inherited;
end;
procedure TAutoButton2.WmLButtonUp (var Msg: TMessage);
begin
if not Capture then
inherited;
end;
Как осуществить ввод текста в компоненте Label?
Многие программисты задавая такой вопрос получают на него стандартный ответ "используй edit box." На самом же деле этот вопрос вполне решаем, хотя лейблы и не основаны на окне и, соответственно не могут получать фокус ввода и, соответственно не могут получать символы, вводимые с клавиатуры. Давайте рассмотрим шаги, которые были предприняты мной для разработки данного компонента.
Первый шаг - это кнопка, которая может отображать вводимый текст:
type
TInputButton = class(TButton)
private
procedure WmChar (var Msg: TWMChar);
message wm_Char;
end;
procedure TInputButton.WmChar (var Msg: TWMChar);
var
Temp: String;
begin
if Char (Msg.CharCode) = #8 then
begin
Temp := Caption;
Delete (Temp, Length (Temp), 1);
Caption := Temp;
end
else
Caption := Caption + Char (Msg.CharCode);
end;
С меткой (label) дела обстоят немного сложнее, так как прийдётся создать некоторые ухищрения, чтобы обойти её внутреннюю структуру. Впринципе, проблему можно решить созданием других скрытых компонент (кстати, тот же edit box). Итак, посмотрим на объявление класса:
type
TInputLabel = class (TLabel)
private
MyEdit: TEdit;
procedure WMLButtonDown (var Msg: TMessage);
message wm_LButtonDown;
protected
procedure EditChange (Sender: TObject);
procedure EditExit (Sender: TObject);
public
constructor Create (AOwner: TComponent); override;
end;
Когда метка (label) создана, то она в свою очередь создаёт edit box и устанавливает несколько обработчиков событий для него. Фактически, если пользователь кликает по метке, то фокус перемещается на (невидимый) edit box, и мы используем его события для обновления метки. Обратите внимание на ту часть кода, которая подражает фокусу для метки (рисует прямоугольничек), основанная на API функции DrawFocusRect:
constructor TInputLabel.Create (AOwner: TComponent);
begin
inherited Create (AOwner);
MyEdit := TEdit.Create (AOwner);
MyEdit.Parent := AOwner as TForm;
MyEdit.Width := 0;
MyEdit.Height := 0;
MyEdit.TabStop := False;
MyEdit.OnChange := EditChange;
MyEdit.OnExit := EditExit;
end;
procedure TInputLabel.WMLButtonDown (var Msg: TMessage);
begin
MyEdit.SetFocus;
MyEdit.Text := Caption;
(Owner as TForm).Canvas.DrawFocusRect (BoundsRect);
end;
procedure TInputLabel.EditChange (Sender: TObject);
begin
Caption := MyEdit.Text;
Invalidate;
Update;
(Owner as TForm).Canvas.DrawFocusRect (BoundsRect);
end;
procedure TInputLabel.EditExit (Sender: TObject);
begin
(Owner as TForm).Invalidate;
end;
Кнопка со звуком
Когда Вы нажимаете на кнопку, то видите трёхмерный эффект нажатия. А как же насчёт четвёртого измерения, например звука? Ну тогда нам понадобится звук для нажатия и звук для отпускания кнопки. Если есть желание, то можно добавить даже речевую подсказку, однако не будем сильно углубляться.
Компонент звуковой кнопки имеет два новых свойства:
type
TDdhSoundButton = class(TButton)
private
FSoundUp, FSoundDown: string;
protected
procedure MouseDown(Button: TMouseButton;
Shift: TShiftState; X, Y: Integer); override;
procedure MouseUp(Button: TMouseButton;
Shift: TShiftState; X, Y: Integer); override;
published
property SoundUp: string
read FSoundUp write FSoundUp;
property SoundDown: string
read FSoundDown write FSoundDown;
end;
Звуки будут проигрываться при нажатии и отпускании кнопки:
procedure TDdhSoundButton.MouseDown(
Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
inherited;
PlaySound (PChar (FSoundDown), 0, snd_Async);
end;
procedure TDdhSoundButton.MouseUp(Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
inherited;
PlaySound (PChar (FSoundUp), 0, snd_Async);
end;
Экранный вирус
Никогда не видели экранного вируса? Представьте, что Ваш экран заболел и покрылся красными пятнами :) А если эта болезнь нападёт на какое-нибудь окно? Всё, что нам надо, это получить контекст устройства при помощи API функции GetWindowDC и рисовать, что душе угодно.
К исходному коду особых комментариев не требуется, скажу лишь только то, что основная часть кода находится в обработчике события OnTimer:
type
TScreenVirus = class(TComponent)
private
FTimer: TTimer;
FInterval: Cardinal;
FColor: TColor;
FRadius: Integer;
protected
procedure OnTimer (Sender: TObject);
procedure SetInterval (Value: Cardinal);
public
constructor Create (AOwner: TComponent); override;
procedure StartInfection;
published
property Interval: Cardinal
read FInterval write SetInterval;
property Color: TColor
read FColor write FColor default clRed;
property Radius: Integer
read FRadius write FRadius default 10;
end;
constructor TScreenVirus.Create (AOwner: TComponent);
begin
inherited Create (AOwner);
FTimer := TTimer.Create (Owner);
FInterval := FTimer.Interval;
FTimer.Enabled := False;
FTimer.OnTimer := OnTimer;
FColor := clRed;
FRadius := 10;
end;
procedure TScreenVirus.StartInfection;
begin
if Assigned (FTimer) then
FTimer.Enabled := True;
end;
procedure TScreenVirus.SetInterval (Value: Cardinal);
begin
if Value <> FInterval then
begin
FInterval := Value;
FTimer.Interval := Interval;
end;
end;
procedure TScreenVirus.OnTimer (Sender: TObject);
var
hdcDesk: THandle;
Brush: TBrush;
X, Y: Integer;
begin
hdcDesk := GetWindowDC (GetDesktopWindow);
Brush := TBrush.Create;
Brush.Color := FColor;
SelectObject (hdcDesk, Brush.Handle);
X := Random (Screen.Width);
Y := Random (Screen.Height);
Ellipse (hdcDesk, X - FRadius, Y - FRadius,
X + FRadius, Y + FRadius);
ReleaseDC (hdcDesk, GetDesktopWindow);
Brush.Free;
end;
Шутки над пользователем
Некоторых пользователей вряд ли можно будет испугать экранным вирусом, однако можно воспользоваться другими способами запугивания, например: прозрачные окошки, недоступные пункты меню с большим количеством подуровней, а так же сообщения об ошибках, которые нельзя убрать.
В приведённом ниже примере при помощи обычного диалогового окна пользователю показывается сообщение об ошибке, причём кнопка "close" накак не хочет нажиматься. У этого диалога есть зависимое окно, которое показывается, при нажатии кнопки
"details".
Поддельная форма с сообщением об ошибке имеет кнопку
"details", которая открывает вторую
часть формы. Это достигается путём добавления компонента за пределы самой формы:
object Form2: TForm2
AutoScroll = False
Caption = 'Error'
ClientHeight = 93
ClientWidth = 320
OnShow = FormShow
object Label1: TLabel
Left = 56
Top = 16
Width = 172
Height = 65
AutoSize = False
Caption =
'Программа выполнила недопустимую ' +
'операцию. Если проблема повторится, ' +
'то обратитесь к разработчику программного обеспечения.'
WordWrap = True
end
object Image1: TImage
Left = 8
Top = 16
Width = 41
Height = 41
Picture.Data = {...}
end
object Button1: TButton
Left = 240
Top = 16
Width = 75
Height = 25
Caption = 'Close'
TabOrder = 0
OnClick = Button1Click
end
object Button2: TButton
Left = 240
Top = 56
Width = 75
Height = 25
Caption = 'Details >>'
TabOrder = 1
OnClick = Button2Click
end
object Memo1: TMemo // за пределами формы!
Left = 24
Top = 104
Width = 265
Height = 89
Color = clBtnFace
Lines.Strings = (
'AX:BX 73A5:495B'
'SX:PK 676F:FFFF'
'OH:OH 7645:2347'
'Crash 3485:9874'
''
'What'#39's going on here?')
TabOrder = 2
end
end
Когда пользователь нажимает кнопку "details", то программа просто изменяет размер формы:
procedure TForm2.Button2Click(Sender: TObject);
begin
Height := 231;
end;
Вторая форма, которая наследуется от первой имеет перемещающуюся кнопку "close":
procedure TForm3.Button1Click(Sender: TObject);
begin
Button1.Left := Random (ClientWidth - Button1.Width);
Button1.Top := Random (ClientHeight - Button1.Height);
end;
В заключении, можно сделать дырку в окне, используя API функцию SetWindowRgn:
procedure TForm1.Button4Click(Sender: TObject);
var
HRegion1, Hreg2, Hreg3: THandle;
Col: TColor;
begin
ShowMessage ('Ready for a real crash?');
Col := Color;
Color := clRed;
PlaySound ('boom.wav', 0, snd_sync);
HRegion1 := CreatePolygonRgn (Pts,
sizeof (Pts) div 8,
alternate);
SetWindowRgn (
Handle, HRegion1, True);
ShowMessage ('Now, what have you done?');
Color := Col;
ShowMessage ('Вам лучше купить новый монитор');
end;
 | Перемещение Image'a по форме во время работы приложения средствами Delphi |
|
Многие,
наверно, сталкивались с проблемой
перемещения Image'a по форме. Решить ее
можно тремя способами (может есть и
больше, но я знаю только три).
Способ
первый. Его суть заключается в том, что
свойства Left и Top картинки изменяются на
разницу между начальными и конечными
координатами (нажатия и отпускания мыши соответственно).
Этот способ самый простой и надежный, но
у него есть один недостаток: left и top
изменяются по очереди, что приводит к
заметному мерцанию картинки. Тем не
менее мы этот способ рассмотрим. Не
забудьте положить на форму Image и
вставить в нее какую-нибудь картинку.
Для начала необходимо объявить
глобальные переменные (они объявляются
в разделе Implementation) x0, y0:integer - они будут
запоминать начальные координаты. И еще
нам понадобится переменная move типа boolean,
чтобы нам отличать перемещение мыши над
картинкой, от попытки ее сдвинуть. Эти
объявления делаются примерно так (участок
кода):
{------------------------}
implementation
var x0,y0:integer;
move:boolean;
{$R *.DFM}
{------------------------}
Теперь
напишем обработчик OnMouseDown для нашей
картинки:
procedure
TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if button<>mbLeft then move:=false //если нажали не
левой кнопкой, то перемещать не будем!
else begin
move:=true;
x0:=x; //запоминаем начальные координаты
y0:=y; //запоминаем начальные координаты
end;
end;
В
этом участке кода проверяется какой
кнопкой нажали на картинку. Если левой,
то запоминаем координаты, а если любой
другой, то перемещать нельзя.
Теперь
напишем обработчик OnMouseMove для нашей
картинки:
procedure
TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if move then begin
image1.Left:=image1.Left+x-x0; // Изменяем позицию
левого края
image1.Top:=image1.Top+y-y0; // Изменяем позицию
верхнего края
end;
end;
Ну и, наконец, обработчик OnMouseUp для нашей
картинки будет таким:
procedure
TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
move:=false;
end;
Здесь
все очень просто. Когда кнопка
отпускается, то переменной move
присваивается значение false, чтобы до
следующего клика по картинке ее нельзя
было сдвинуть.
Этот
способ довольно прост, как для понимания,
так и для реализации. Но такой же
алгоритм перемещения можно реализовать
немного красивее. У некоторых
компонентов, в том числе и Image, есть такая
классная процедура SetBounds(Left,Top,Width,Height),
которая может изменять сразу все четыре
параметра. Таким образом событие OnMouseMove
можно изменить так:
procedure
TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if move then begin
image1.SetBounds(image1.Left+x-x0,image1.Top+y-y0,image1.width,image1.height);
end;
end;
Но
есть еще один очень интересный выход: по
экрану можно перемещать не саму
картинку, а только ее рамку, когда
пользователь выберет место для картинки
и отпустит кнопку - она туда
переместиться. Для этого нам
понадобится еще одна глобальная
переменная: rec: TRect, которая будет хранить
параметры картинки. Теперь слегка
изменим обработчики событий для
картинки. Таким образом все в целом
будет выглядеть так:
procedure
TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if button<>mbLeft then move:=false
else begin
move:=true;
x0:=x;
y0:=y;
rec:=image1.BoundsRect; //запоминаем контур
картинки
end;
end;
procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if move then begin
Form1.Canvas.DrawFocusRect(rec); //рисуем рамку
with rec do begin
left:=Left+x-x0;
top:=Top+y-y0;
right:=right+x-x0;
bottom:=bottom+y-y0;
x0:=x;
y0:=y; // изменяем координаты
end;
Form1.Canvas.DrawFocusRect(rec); // рисуем рамку на
новом месте
end;
end;
procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
Form1.Canvas.DrawFocusRect(rec);
with image1 do begin
setbounds(rec.left+x-x0,rec.top+y-y0,width,height); //перемещаем
картинку
move:=false;
end;
end;
Поскольку
DrawFocusRect рисует рамку методом Xor, то при
повторном вызове этой функции с теми же
параметрами, рамка стирается.
Этот
очень красивый метод добавит в ваши
программы много интересного. Так что
пользуйтесь.
 | Пособие по написанию своих компонентов на Delphi для начинающих |
|
Содержание:
Теперь изучим более сложные вещи:
Почему я сел писать это
пособие
Во-первых,
потому что когда я очень хотел
написать свой первый компонент, я
прочитал две книги, и у меня ничего
интересного собственно не вышло.
Потом я прочитал еще одну книгу (в ней
хотя бы пример рабочий был), вроде
разобрался. Но там был разобран такой
простой компонент, что все более
сложное мне приходилось делать самому,
иногда методом тыка, иногда сидел
разбирался и так далее. Результат -
разобрался, чего и вам желаю и надеюсь
помочь этим пособием.
Все мои
готовые компоненты можно найти на
сайте http://delphid.dax.ru.
Для
чего нужны компоненты
Дельфи
имеет открытую архитектуру - это значит,
что каждый программист волен
усовершенствовать эту среду
разработки, как он захочет.
К стандартным наборам компонентов,
которые поставляются вместе с Дельфи
можно создать еще массу своих
интересных компонентов, которые
заметно упростят вам жизнь (это я вам
гарантирую). А еще можно зайти на какой-нибудь
крутой сайт о Дельфи и там скачать кучу
крутых компонентов, и на их основе
сделать какую-нибудь крутую прогу. Так
же компоненты освобождают вас от
написания "тысячи тонн словесной
руды". Пример: вы создали компонент -
кнопку, при щелчке на которую данные из
Memo сохранятся во временный файл. Теперь
как только вам понадобится эта функция
вы просто ставите этот компонент на
форму и наслаждаетесь результатом. И не
надо будет каждый раз прописывать это,
для ваших новых программ - просто
воспользуйтесь компонентом.
Шаг
1. Придумывание идеи
Первым
шагом нужно ответить себе на вопрос:
"Для чего мне этот компонент и что он
будет делать?". Затем необходимо в
общих чертах продумать его свойства,
события, на которые он будет
реагировать и те функции и процедуры,
которыми компонент должен обладать.
Затем очень важно выбрать "предка"
компонента, то есть наследником
какого класса он будет являться. Тут
есть два пути. Либо в качестве
наследника взять уже готовый компонент
(то есть модифицировать уже
существующий класс), либо создать
новый класс.
Для
создания нового класса можно выделить 4
случая:
1. Создание
Windows-элемента управления (TWinControl)
2. Создание
графического элемента управления (TGraphicControl)
3. Создание
нового класса или элемента управления (TCustomControl)
4. Создание
невизуального компонента (не видимого)
(TComponent)
Теперь
попробую объяснить что же такое
визуальные и невизуальные компоненты.
Визуальные компоненты видны во время
работы приложения, с ними напрямую
может взаимодействовать пользователь,
например кнопка Button - является
визуальным компонентом.
Невизуальные
компоненты видны только во время
разработки приложения (Design-Time), а во
время работы приложения (Run-Time) их не
видно, но они могут выполнять какую-нибудь
работу. Наиболее часто используемый
невизуальный компонент - это Timer.
Итак, что
бы приступить от слов к делу, попробуем
сделать какой-нибудь супер простой
компонент (только в целях ознакомления
с техникой создания компонентов), а
потом будем его усложнять.
Шаг
2. Создание пустого модуля компонента
Рассматривать
этот шаг я буду исходя из устройства
Дельфи 3, в других версиях этот процесс
не сильно отличается. Давайте
попробуем создать кнопку, у которой
будет доступна информация о количестве
кликов по ней.
Чтобы
приступить к непосредственному
написанию компонента, вам необходимо
сделать следующее:
- Закройте
проекты, которые вы разрабатывали (формы
и модули)
- В
основном меню выберите Component -> New
Component...
- Перед
вами откроется диалоговое окно с
названием "New Component"
- В
поле Ancestor Type (тип предка) выберите
класс компонента, который вы хотите
модифицировать. В нашем случае вам
надо выбрать класс TButton
- В поле
Class Name введите имя класса, который вы
хотите получить. Имя обязательно
должно начинаться с буквы "T". Мы
напишем туда, например, TCountBtn
- В поле
Palette Page укажите имя закладки на
которой этот компонент появиться
после установки. Введем туда MyComponents (теперь
у вас в Делфьи будет своя закладка с
компонентами!).
- Поле Unit
File Name заполняется автоматически, в
зависимости от выбранного имени
компонента. Это путь куда будет
сохранен ваш модуль.
- В поле
Search Path ничего изменять не нужно.
- Теперь
нажмите на кнопку Create Unit и получите
следующее:
unit CountBtn;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TCountBtn = class(TButton)
private
{ Private declarations }
protected
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('MyComponents', [TCountBtn]);
end;
end.
Шаг
3. Начинаем разбираться во всех
директивах
Что же
здесь написано? да собственно пока
ничего интересного. Здесь объявлен
новый класс TCountBtn и процедура
регистрации вашего компонента в
палитре компонентов.
Директива Private
Здесь вы будете писать все скрытые поля
которые вам понадобятся для создания
компонента. Так же в этой директиве
описываются процедуры и функции,
необходимые для работы своего
компонента, эти процедуры и функции
пользователю не доступны. Для нашего
компонент мы напишем туда следующее (запись
должна состоять из буквы "F" имени
поля: тип этого поля):
FCount:integer;
Буква "F"
должна присутсвовать обязательно.
Здесь мы создали скрытое поле Count, в
котором и будет храниться число кликов
по кнопке.
Директива
Protected. Обычно я здесь пишу различные
обработчики событий мыши и клавиатуры.
Мы напишем здесь следующую строку:
procedure Click;
override;
Это указывает на то, что мы будем
обрабатывать щелчок мыши по компоненту.
Слово "override" указывает на то, что
мы перекроем стандартное событие OnClick
для компонента предка.
В директиве
Public описываются те процедуры и
функции компонента, которые будут
доступны пользователю. (Например, в
процессе написания кода вы пишите имя
компонента, ставите точку и перед вами
список доступных функций, объявленных
в диретиве Public). Для нашего компонента,
чтобы показать принцип использования
этой директивы создадим функцию - ShowCount,
которая покажет сообщение, уведомляя
пользователя сколько раз он уже нажал
на кнопку. Для этого в директиве Public
напишем такой код:
procedure ShowCount;
Осталась
последняя директива Published. В ней
также используется объявления
доступных пользователю, свойств и
методов компонента. Для того, чтобы наш
компонент появился на форме необходимо
описать метод создания компонента (конструктор),
можно прописать и деструктор, но это не
обязательно. Следует обратить внимание
на то, что если вы хотите, чтобы какие-то
свойства вашего компонента появились в
Инспекторе Объектов (Object Inspector) вам
необходимо описать эти свойства в
директиве Published. Это делается так: property
Имя_свойства (но помните здесь букву
"F" уже не нужно писать), затем
ставиться двоеточие ":" тип
свойства, read процедура для чтения
значения, write функция для записи
значения;. Но похоже это все сильно
запутано. Посмотрите, что нужно
написать для нашего компонента и все
поймете:
constructor
Create(aowner:Tcomponent);override; //Конструктор
property Count:integer read FCount write FCount; //Свойство
Count
Итак все
объявления сделаны и мы можем
приступить к написанию
непосредственно всех объявленных
процедур.
Шаг 4. Пишем процедуры и функции.
Начнем с
написания конструктора. Это делается
примерно так:
constructor
TCountBtn.Create(aowner:Tcomponent);
begin
inherited create(Aowner);
end;
Здесь в
принципе понимать ничего не надо. Во
всех своих компонентах я писал именно
это (только класс компонента менял и
все). Также сюда можно записывать любые
действия, которые вы хотите сделать в
самом начале работы компонента, то есть
в момент установки компонента на форму.
Например можно установить начальное
значение нашего свойства Count. Но мы
этого делать не будем.
Теперь мы
напишем процедуру обработки щелчка
мышкой по кнопке:
procedure
Tcountbtn.Click;
begin
inherited click;
FCount:=FCount+1;
end;
"Inherited
click" означает, что мы повторяем
стандартные методы обработки щелчка
мышью (зачем напрягаться и делать
лишнюю работу:)).
У нас
осталась последняя процедура ShowCount. Она
может выглядеть примерно так:
procedure
TCountBtn.ShowCount;
begin
Showmessage('По кнопке '+ caption+' вы сделали: '+inttostr(FCount)+'
клик(а/ов)');
end;
Здесь
выводится сообщение в котором
показывается количество кликов по
кнопке (к тому же выводится имя этой
кнопки, ну это я добавил только с
эстетической целью).
И
если вы все поняли и сделали правильно,
то у вас должно получится следующее:
unit
CountBtn;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TCountBtn = class(TButton)
private
{ Private declarations }
FCount:integer;
protected
{ Protected declarations }
procedure Click;override;
public
{ Public declarations }
procedure ShowCount;
published
{ Published declarations }
property Count:integer read FCount write FCount;
constructor Create(aowner:Tcomponent);override;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Mihan Components', [TCountBtn]);
end;
constructor TCountBtn.Create(aowner:Tcomponent);
begin
inherited create(Aowner);
end;
procedure Tcountbtn.Click;
begin
inherited click;
FCount:=FCount+1;
end;
procedure TCountBtn.ShowCount;
begin
Showmessage('По кнопке '+ caption+' вы сделали: '+inttostr(FCount)+'
клик(а/ов)');
end;
end.
Скорее
сохраняйтесь, дабы не потерять
случайным образом байты набранного
кода:)).
Шаг 5. Устанавливаем компонент
Если вы
сумели написать и понять, все то что
здесь предложено, то установка
компонента не должна вызвать у вас
никаких проблем. Все здесь делается
очень просто. В главном меню выберите
пункт Component -> Install Component. перед вами
открылось диалоговое окно Install Component. В
нем вы увидите две закладки: Into exsisting
Package и Into new Package. Вам предоставляется
выбор установить ваш компонент в уже
существующий пакет или в новый пакет
соответственно. Мы выберем в уже
существующий пакет.
В поле Unit
File Name напишите имя вашего сохранненого
модуля (естественно необходимо еще и
указать путь к нему), а лучше
воспользуйтесь кнопкой Browse и выберите
ваш файл в открывшемся окне.
В Search Path
ничего изменять не нужно, Делфьи сама
за вас все туда добавит.
В поле Package
File Name выберите имя пакета, в который
будет установлен ваш компонент. Мы
согласимся с предложенным по умолчанию
пакетом.
Теперь
нажимаем кнопочку Ok. И тут появиться
предупреждение Package dclusr30.dpk will be rebuilt.
Continue? Дельфи спрашивает: "Пакет такой-то
будет изменен. Продолжить?". Конечно
же надо ответить "Да". И если вы все
сделали правильно, то появиться
сообщение, что ваш компонент
установлен. Что ж можно кричать Ура! Это
ваш первый компонент.
Создание свойств своего типа
Теперь
мы попробуем создать свойство
нестандартного типа. Рассмотрим это на
примере метки - TLabel. У этого компонента
есть такое свойство: Alignment. Оно может
принимать следующие значения: taLeftJustify,
taCenter, taRightJustify. Приступаем к созданию
свойства. Ничего интересного мне
придумать не удалось, но тем не менее я
вам покажу это на примере того свойства,
которое я придумал. Оно очень простое и
поможет вам разобраться. Свойство
будет называться ShowType (тип TShowTp), в нашем
компоненте оно будет отвечать за
отображение свойства Count. Если
пользователь установит свойство ShowType в
Normal, то кнопка будет работать, как и
работала. А если пользователь присвоит
этому свойтсву значение CountToCaption, то
количество кликов, будет отображаться
на самой кнопке.
Для
начале нам необходимо объявить новый
тип. Описание типа нужно добавить после
слова Type. Вот так это выглядело вначале:
type
TCountBtn = class(TButton)
Вот так это должно выглядеть:
type
TShowTp = (Normal, CountToCaption);
TCountBtn
= class(TButton)
Здесь мы объявили новый
тип TShowTp, который может принимать
только два значения. Все значения,
которые вы хотите добавить
перечисляются через запятую.
Теперь
нам понадобиться создать поле этого
типа. Это мы уже умеем и делать и
поэтому не должно вызвать никаких
сложностей. В директиву Private напишите:
FShowType:TShowTp;
Мы
создали поле ShowType, типа TShowTp.
Конечно
же необходимо добавить это свойство в
инспектор объектов:
property
ShowType: TshowTp read FshowType write FShowType;
Ну
и наконец, чтобы наш компонент
реагировал на изменение этого свойства
пользователем надо слегка изменить
обработчик события OnClick. После
небольшой модификации он может иметь
примерно такой вид:
procedure
Tcountbtn.Click;
begin
inherited click;
FCount:=Fcount+1;
if ShowType = Normal then
Caption:=Caption;
if ShowType = CountToCaption then
Caption:='Count= '+inttostr(count);
end;
Объясню
что произошло. Вначале мы увеличиваем
счетчик на единицу. Затем проверяем
какое значение имеет свойство ShowType.
Если Normal, то ничего не делаем, а если
CountToCaption, то в надпись на кнопке выводим
количество кликов. Не так уж и сложно
как это могло показаться с первого раза.
Имплантируем таймер в компонент
Очень
часто бывает, что вам необходимо
вставить в компонент, какой-нибудь
другой компонент, например, таймер. Как
обычно будем рассматривать этот
процесс на конкретном примере. Сделаем
так, что через каждые 10 секунд значение
счетчика кликов будет удваиваться. Для
этого мы встроим таймер в нашу кнопку.
Нам понадобиться сделать несколько
несложных шагов.
После
раздела uses, где описаны добавленные в
программу модули, объявите переменную
типа TTimer. Назовем ее Timer. Приведу
небольшой участок кода:
unit CountBtn;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,
StdCtrls, ExtCtrls;
var Timer: TTimer;
type
Дальше
в директиву Protected необходимо добавить
обработчик события OnTimer для нашего
таймера. Это делается так:
procedure
OnTimer(Sender: TObject);
Поскольку
наш таймер это не переменная, а
компонент, его тоже надо создать, для
этого в конструктор нашей кнопки
напишем:
constructor
TCountBtn.Create(aowner:Tcomponent);
begin
inherited create(Aowner);
Timer:=TTimer.Create(self);
Timer.Enabled:=true;
Timer.OnTimer:=OnTimer;
Timer.Interval:=10000;
end;
Здесь
создается экземпляр нашего таймера и
его свойству Iterval (измеряется в
миллисекундах) присваивается значение
10000 (то есть 10 секунд если по простому).
Собственно
осталось написать саму процедуру OnTimer.
Я сделал это так:
procedure
TCountBtn.OnTimer(Sender: TObject);
begin
FCount:=FCount*2;
end;
Вот
примерно то, что у вас должно
получиться в конце:
unit
CountBtn;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,
StdCtrls, ExtCtrls;
var Timer: TTimer;
type
TShowTp = (Normal, CountToCaption);
TCountBtn = class(TButton)
private
{ Private declarations }
FCount:integer;
FShowType:TShowTp;
protected
{ Protected declarations }
procedure OnTimer(Sender: TObject);
procedure Click;override;
public
{ Public declarations }
procedure ShowCount;
published
{ Published declarations }
property Count:integer read FCount write FCount;
constructor Create(aowner:Tcomponent);override;
property ShowType: TshowTp read FshowType write FShowType;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Mihan Components', [TCountBtn]);
end;
constructor TCountBtn.Create(aowner:Tcomponent);
begin
inherited create(Aowner);
Timer:=TTimer.Create(self);
Timer.Enabled:=false;
Timer.OnTimer:=OnTimer;
Timer.Interval:=1000;
end;
procedure Tcountbtn.Click;
begin
inherited click;
FCount:=Fcount+1;
Timer.Enabled:=true;
if ShowType = Normal then
Caption:=Caption;
if ShowType = CountToCaption then
Caption:='Count= '+inttostr(count);
end;
procedure TCountBtn.ShowCount;
begin
Showmessage('По кнопке '+ caption+' вы сделали:
'+inttostr(FCount)+' клик(а/ов)');
end;
procedure TCountBtn.OnTimer(Sender: TObject);
begin
FCount:=FCount*2;
end;
end.
Если у вас что-то не
сработало, то в начале проверьте все ли
у вас написано правильно. Затем
проверьте может у вас не хватает какого-нибудь
модуля в разделе Uses.
Переустановка компонента
Очень
часто бывает необходимо
переустановить ваш компонент.
Если вы
попробуете сделать это путем выбора
Component->Install Component, то Дельфи вас честно
предупредит о том, что пакет уже
содержит модуль с таким именем. Перед
вами открывается окно с содержимым
пакета. В нем вы должны найти имя вашего
компонента и удалить его (либо нажать
кнопочку Remove). Теперь в пакете уже нет
вашего компонента. Затем проделайте
стандартную процедуру по установке
компонента.
 | Работа с буфером обмена в Delphi |
|
Каждый юзер наверняка сталкивался с помещением информации в буфер и извлечением ее оттуда. Тот же пресловутый Ворд при нажатии кнопки "копировать" помещает выделенный текст в буфер, а при нажатии "вставить" - извлекает его. Таким же
образом помещаться в буфер могут изображения и данные разных форматов. Итак, буфер - это специальная область оперативной памяти в которую Windows помещает пользовательские данные для обмена между разными приложениями или внутри одного приложения.
Для того, чтобы работать с буфером обмена из Delphi, необходимо подключить к своему приложению модуль
clipbrd.pas, то есть пишем в uses clipbrd.pas,...
РАБОТА С ТЕКСТОМ
Самый простой способ поместить в буфер обмена какой-либо текст - это добавить следующий код:
Clipboard.asText:='ВАШ_ТЕКСТ';
или
Clipboard.SetTextBuf(PChar('ВАШ_ТЕКСТ'));
Чтобы извлечь текст из буфера, можно сделать так (поместим текст в Memo1:TMemo):
Memo1.Text:=Clipboard.asText;
РАБОТА С ИЗОБРАЖЕНИЯМИ
Если вы работаете с компонентом Image1:Timage, то самым простым способом поместить в него изображение из буфера будет присвоение хэндла данных Clipboard'a хэндлу Tbitmap:
Image1.Picture.Bitmap.Handle:=Clipboard.GetAsHandle(CF_bitmap);
КАК УЗНАТЬ ФОРМАТ ДАННЫХ БУФЕРА В ДАННЫЙ МОМЕНТ
Для этого существует специальная функция Clipboard.HasFormat(Format:
WORD):boolean;
Функция возвращает true, если в буфере именно этот формат, в противном случает возвращает false.
Описание форматов:
CF_TEXT - Обычный текст, заканчивающийся <CR><LF>
CF_BITMAP - Битмап изображение *.bmp
CF_METAFILEPICT - Метафайл изображение *.wmf
CF_PICTURE - Объект типа TPicture
CF_COMPONENT - Компонента Delphi (Tbutton, например)
CF_OBJECT - Любой объект в Delphi типа TPersistent
ОЧИЩАЕМ БУФЕР
Чтобы очистить буфер обмена от содержащихся в нем данных, используйте процедуру
Clipboard.clear;
ОТКРЫВАЕМ // ЗАКРЫВАЕМ
Если вы хотите, чтобы все приложения, кроме вашего не могли изменять содержимое буфера, используйте
Clipboard.Open;
После пользования буфером, его необходимо закрывать процедурой Clipboard.Close;
З.Ы. Многие объекты в Delphi изначально имеют методы, позволяющие помещать данные в буфер и извлекать их оттуда, примером может служить Tpicture.
 | Как писать Win32API приложения на Delphi |
|
Главная проблема, возникающая при написании WinAPI приложений - это неудобство ручного создания всех окон приложения. Требуется вызывать функцию CreateWindow для каждого (в том числе и дочернего) окна программы, а затем еще и менять шрифт в некоторых из них. Лучшим на мой взгляд выходом из этой ситуации является использование ресурсов диалоговых окон (dialog box resources) для соэдания всех окон приложения. В этой статье я расскажу как это делается в Delphi на примере простого приложения с одним главным и двумя (модальными) окнами.
Шаг 1. Создание ресурсов диалоговых окон
Для создания ресурсов я использовал редактор ресурсов из состава Borland C++
5.02. В Borland Resource Workshop 4.5 все почти аналогично. Создаем главное окно, вот его код:
500 DIALOGEX 0, 0, 240, 117
EXSTYLE WS_EX_DLGMODALFRAME | WS_EX_APPWINDOW | WS_EX_CLIENTEDGE
STYLE DS_MODALFRAME | DS_3DLOOK | DS_CENTER | WS_OVERLAPPED | WS_VISIBLE | WS_CAPTION |
WS_SYSMENU | WS_MINIMIZEBOX
CLASS "WndClass1"
CAPTION "Главное окно приложения"
MENU 300
FONT 8, "MS Sans Serif", 400, 0
LANGUAGE LANG_RUSSIAN , 0
{
CONTROL "OK", IDOK, "BUTTON", BS_DEFPUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP,
19, 94, 50, 14, WS_EX_CLIENTEDGE
CONTROL "Cancel", IDCANCEL, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP,
96, 94, 50, 14, WS_EX_CLIENTEDGE
CONTROL "Help", IDHELP, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP,
172, 94, 50, 14, WS_EX_CLIENTEDGE
CONTROL "Группа", -1, "button", BS_GROUPBOX | BS_RIGHT | WS_CHILD | WS_VISIBLE | WS_GROUP,
20, 9, 100, 76
CONTROL "Кнопка 1", 105, "button", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP,
28, 21, 60, 12
CONTROL "Кнопка 2", 106, "button", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP,
28, 37, 60, 12
CONTROL "Кнопка 3", 107, "button", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP,
28, 53, 60, 12
CONTROL "ListBox1", 108, "listbox", LBS_NOTIFY | LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_CHILD |
WS_VISIBLE | WS_BORDER | WS_TABSTOP,
132, 13, 92, 72
}
Обратите внимание на поле CLASS. В нем должно стоять то же значение, что и в поле lpszClassName записи TWndClassEx основной программы. В редакторе ресурсов значение этого поля можно изменить в окне свойств ресурса.
Два других окна создаются как обычные ресурсы, поэтому их код я здесь не привожу.
Шаг 2. Основная программа.
Текст программы в нашем случае несколько отличается от текста, например, winmin. Регистрация оконного класса:
wc.cbSize:=sizeof(wc);
wc.style:=cs_hredraw or cs_vredraw;
wc.lpfnWndProc:=@WindowProc;
wc.cbClsExtra:=0;
wc.cbWndExtra:=DLGWINDOWEXTRA;
wc.hInstance:=HInstance;
wc.hIcon:=LoadIcon(hInstance, 'MAINICON');
wc.hCursor:=LoadCursor(0,idc_arrow);
wc.hbrBackground:=COLOR_BTNFACE+1;
wc.lpszMenuName:=nil;
wc.lpszClassName:='WndClass1';
RegisterClassEx(wc);
Обратите внимание, что в поле cbWindowExtra стоит константа DLGWINDOWEXTRA, если бы её там не было, нам не удалось бы создать главное окно, основанное на ресурсе Dialog Box. Кроме того, в поле lpszClassName стоит то же значение, что и в соответствующем поле описания ресурса окна.
Итак, класс создан и зарегистрирован, теперь создаем главное окно из ресурса: MainWnd:=CreateDialog(hInstance, '#500', 0, nil);
Напоминаю, что '#500' значит имя ресурса окна. Не забудьте подключить откомпилированный файл сценария ресурса к программе при помощи директивы {$r ...}
Шаг 3. Оконная функция.
Оконная функция ничем не отличается от обычной:
function WindowProc(wnd:HWND; Msg : Integer; Wparam:Wparam; Lparam:Lparam):Lresult; stdcall;
var nCode, ctrlID, size : word;
pt : TPoint;
s : string;
Begin
case msg of
wm_command :
Begin
nCode:=hiWord(wParam);
ctrlID:=loWord(wParam);
case ctrlID of
IDHELP : begin
DialogBox(hInstance,'#501',wnd,@DialogFunc);
end;
IDOK : begin
DialogBoxParam(hInstance,'#503',wnd,@DialogFunc2, Integer(pd));
s := 'Login: '+pd^.login;
s := s + ' ' + 'Pass: '+pd^.pass;
ListBox_AddString(lb, s);
end;
IDCANCEL : begin
DestroyWindow(wnd);
end;
end;
End;
wm_destroy :
Begin
Dispose(pd);
postquitmessage(0); exit;
Result:=0;
End;
else Result:=DefWindowProc(wnd,msg,wparam,lparam);
end;
end;
Шаг 4. Цикл сбора сообщений.
Цикл сбора сообщений следует изменить следующим образом, как при использовании немодальных диалоговых окон. Можно, правда, оставить все как есть, но тогда вы не сможете использовать клавиатуру для перемещения между дочерними окнами, использования кнопок "по умолчанию" и т.д.
While GetMessage(Mesg,0,0,0) do
begin
if mainWnd<>0 then
if IsDialogMessage(mainWnd,Mesg) then continue;
TranslateMessage(Mesg);
DispatchMessage(Mesg);
end;
После нажатия кнопки "ОК" появляется еще одно окно. Если в нем ввести текст и нажать "ОК", этот текст будет добавлен в
Listbox.
|