Вопросы и ответы - Delphi - Delphi IDE, компилятор, отладчик, редактор - Библиотека программиста
Пользователь

Добро пожаловать,

Регистрация или входРегистрация или вход
Потеряли пароль?Потеряли пароль?

Ник:
Пароль:

Меню сайта




Ваше мнение
Оцените дизайн сайта

Супер
Симпатично
Пойдет
Ничего хорошего
Просто клиника


Результаты
Другие опросы

Всего голосов: 852
Комментарии: 2


Наши партнеры



Статистика




Programming books  Download software  Documentation  Scripts  Content Managment Systems(CMS)  Templates  Icon Sets  Articles  Contacts  Voting  Site Search




Вопросы и ответы - Delphi - Delphi IDE, компилятор, отладчик, редактор

                 
 
ToolsAPI: Компоненты
Создание pop-up меню своего компонента и кое-что еще о классе TComponentExpert

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



Прежде всего вам следует разделить код вашего компонента на Design-time и Run-time. Для этого перенесите ваш компонент в модуль, с названием, например, MyComponent.pas, а процедуры регистрации его в палитре компонентов (procedure Register и т.д.) в модуль, с названием, например, MyComponentReg. На такие меры приходится идти из-за того, что Borland не включила в исходные коды исходник файла Proxies.pas.



Итак, получим два файла:



MyComponent.pas:


Код
unit MyComponent;



interface



uses

SysUtils, Classes;



type

TMyComponent = class(TComponent)

private

{ Private declarations }

protected

{ Protected declarations }

public

{ Public declarations }

published

{ Published declarations }

end;





MyComponentReg.pas


Код
unit MyComponentReg;



interface

uses DesignIntf, DesignEditors, MyComponent, Classes, Dialogs;



type

TMyComponentEditor = class(TComponentEditor)

private

procedure ExecuteVerb(Index: Integer); override;

function GetVerbCount: Integer; override;

function GetVerb(Index: Integer): string; override;

procedure Edit; override;

end;



procedure Register;



implementation



procedure Register;

begin

RegisterComponents('Samples', [TMyComponent]);

RegisterComponentEditor(TMyComponent, TMyComponentEditor);

end;



{ TMyComponentEditor }



procedure TMyComponentEditor.Edit;

begin

ShowMessage('TMyComponent component v1.0 by Rastrusny Vladislav');

end;



procedure TMyComponentEditor.ExecuteVerb(Index: Integer);

begin

inherited;

case Index of

0: //Действие при выборе первого определенного пункта меню

end;

end;



function TMyComponentEditor.GetVerb(Index: Integer): string;

begin

case Index of

0: Result := 'Demo Menu Item 1'; //Название первого пункта меню

end;

end;



function TMyComponentEditor.GetVerbCount: Integer;

begin

Result := 1;

end;



end.







Рассмотрим теперь, что же тут написано. В первом файле просто определен компонент MyComponent. В нем вы определяете все свойства и методы вашего компонента. Все как обычно. Теперь - второй файл MyComponentReg. Он содержит процедуры регистрации компонента и процедуру регистрации редактора компонента (TComponentEditor). Этот редактор и будет отображать меню и прочие безобразия. Итак:



Определяем TMyComponentEditor как потомка TComponentEditor. Сам по себе этот класс является "воплотителем" интерфейса IComponentEditor, хотя нам все равно. Для того, чтобы все это заработало нам нужно будет переопределить стандартные методы класса TComponentEditor. Рассмотрим его:


Код
type

TComponentEditor = class(TBaseComponentEditor, IComponentEditor)

private

FComponent: TComponent;

FDesigner: IDesigner;

public

constructor Create(AComponent: TComponent; ADesigner: IDesigner); override;

procedure Edit; virtual;

function GetVerbCount: Integer; virtual;

function GetVerb(Index: Integer): string; virtual;

procedure ExecuteVerb(Index: Integer); virtual;

procedure Copy; virtual;

procedure PrepareItem(Index: Integer; const AItem: IMenuItem); virtual;

property Component: TComponent;

property Designer: IDesigner;

end;







Конструктор нам переопределять не нужно. Поэтому начнем с описания метода Edit.



Метод Edit вызывается при двойном щелчке по компоненту. Вот так просто! При двойном щелчке на компоненте! Если метод не определен, то при двойном щелчке будет выполнен первый пункт меню, которое вы определили.



Метод GetVerbCount: Integer должен возвращать количество определенных вами пунктов меню.



Метод GetVerb(Index: Integer): string должен возвращать название пункта меню № Index.



Метод ExecuteVerb(Index: Integer) вызывается при щелчке на пункте меню, определенном вами. Index - номер меню из метода GetVerb. В нем вы определяете действия, которые будут происходить при нажатии на ваш пункт меню.



Метод Copy вызывается при копировании вашего компонента в буфер обмена



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



Метод PrepareItem(Index: Integer; const AItem: IMenuItem) вызывается для каждого определенного вами пункта меню № Index и через параметр AItem передает сам пункт меню для настройки. Для работы нам нужно будет рассмотреть саму реализацию интерфейса IMenuItem. Он определен в модуле DesignMenus.pas и является потомком интерфейса IMenuItems.


Код
IMenuItems = interface

['{C9CC6C38-C96A-4514-8D6F-1D121727BFAF}']



// public

function SameAs(const AItem: IUnknown): Boolean;

function Find(const ACaption: WideString): IMenuItem;

function FindByName(const AName: string): IMenuItem;

function Count: Integer;

property Items[Index: Integer]: IMenuItem read GetItem;

procedure Clear;



function AddItem(const ACaption: WideString; AShortCut: TShortCut;

AChecked, AEnabled: Boolean; AOnClick: TNotifyEvent = nil;

hCtx: THelpContext = 0; const AName: string = ''): IMenuItem; overload;



function AddItem(AAction: TBasicAction;

const AName: string = ''): IMenuItem; overload;



function InsertItem(const ACaption: WideString;

AShortCut: TShortCut; AChecked, AEnabled: Boolean; AOnClick: TNotifyEvent = nil;

hCtx: THelpContext = 0; const AName: string = ''): IMenuItem; overload;

function InsertItem(Index: Integer; const ACaption: WideString;

AShortCut: TShortCut; AChecked, AEnabled: Boolean; AOnClick: TNotifyEvent = nil;

hCtx: THelpContext = 0; const AName: string = ''): IMenuItem; overload;



function InsertItem(AAction: TBasicAction;

const AName: string = ''): IMenuItem; overload;

function InsertItem(Index: Integer; AAction: TBasicAction;

const AName: string = ''): IMenuItem; overload;



function AddLine(const AName: string = ''): IMenuItem;



function InsertLine(const AName: string = ''): IMenuItem; overload;

function InsertLine(Index: Integer; const AName: string = ''): IMenuItem; overload;

end;





IMenuItem = interface(IMenuItems)

['{DAF029E1-9592-4B07-A450-A10056A2B9B5}']



// public

function Name: TComponentName;

function MenuIndex: Integer;

function Parent: IMenuItem;

function HasParent: Boolean;

function IsLine: Boolean;



property Caption: WideString;

property Checked: Boolean;

property Enabled: Boolean;

property GroupIndex: Byte;

property HelpContext: THelpContext;

property Hint: string;

property RadioItem: Boolean;

property ShortCut: TShortCut;

property Tag: LongInt;

property Visible: Boolean;

end;







Начнем с конца. Т.е. с IMenuItem. Как видно, почти все члены интерфейса соответствуют членам класса TMenuItem. Т.е. обратившись в методе PrepareItem к AItem.Enabled:=false мы запретим выбор этого элемента меню. Что же касается класса TMenuItems, то они, видимо, предназначены для манипулирования элементом меню в качестве родительского для нескольких других. Думаю, в них опытным путем разобраться тоже не составит труда.



Что же касается процедуры RegisterComponentEditor, то она принимает два параметра: первый - класс компонента, для которого создается редактор свойств и второй - собственно сам класс редактора свойств.



Создание редакторов свойств

Для создания редактора свойств нужно написать класс, унаследованный от TBasePropertyEditor. Но мы рассмотрим более функционального его потомка TPropertyEditor


Код
TPropertyEditor = class(TBasePropertyEditor, IProperty, IProperty70)

protected

procedure SetPropEntry(Index: Integer; AInstance: TPersistent;

APropInfo: PPropInfo); override;

protected

function GetFloatValue: Extended;

function GetFloatValueAt(Index: Integer): Extended;

function GetInt64Value: Int64;

function GetInt64ValueAt(Index: Integer): Int64;

function GetMethodValue: TMethod;

function GetMethodValueAt(Index: Integer): TMethod;

function GetOrdValue: Longint;

function GetOrdValueAt(Index: Integer): Longint;

function GetStrValue: string;

function GetStrValueAt(Index: Integer): string;

function GetVarValue: Variant;

function GetVarValueAt(Index: Integer): Variant;

function GetIntfValue: IInterface;

function GetIntfValueAt(Index: Integer): IInterface;

procedure Modified;

procedure SetFloatValue(Value: Extended);

procedure SetMethodValue(const Value: TMethod);

procedure SetInt64Value(Value: Int64);

procedure SetOrdValue(Value: Longint);

procedure SetStrValue(const Value: string);

procedure SetVarValue(const Value: Variant);

procedure SetIntfValue(const Value: IInterface);

protected

{ IProperty }

function GetEditValue(out Value: string): Boolean;

function HasInstance(Instance: TPersistent): Boolean;

{ IProperty70 }

function GetIsDefault: Boolean; virtual;

public

constructor Create(const ADesigner: IDesigner; APropCount: Integer); override;

destructor Destroy; override;

procedure Activate; virtual;

function AllEqual: Boolean; virtual;

function AutoFill: Boolean; virtual;

procedure Edit; virtual;

function GetAttributes: TpropertyAttributes; virtual;

function GetComponent(Index: Integer): TPersistent;

function GetEditLimit: Integer; virtual;

function GetName: string; virtual;

procedure GetProperties(Proc: TGetPropProc); virtual;

function GetPropInfo: PPropInfo; virtual;

function GetPropType: PTypeInfo;

function GetValue: string; virtual;

function GetVisualValue: string;

procedure GetValues(Proc: TGetStrProc); virtual;

procedure Initialize; override;

procedure Revert;

procedure SetValue(const Value: string); virtual;

function ValueAvailable: Boolean;

property Designer: IDesigner read FDesigner;

property PrivateDirectory: string read GetPrivateDirectory;

property PropCount: Integer read FPropCount;

property Value: string read GetValue write SetValue;

end;





Предположим, нам нужно создать редактор для текстового свойства, при нажатии кнопки "…" в Object Inspector.



Объявим специальный тип этого свойства TMyComponentStringProperty = string;



Далее, в компоненте укажем свойство данного типа property MyProperty: TMyComponentStringProperty, далее в Run-time части компонента (MyComponentReg.pas) объявим класс TMyCSPEditor (в переводе: TMyComponentStringPropertyEditor), унаследовав его от класса TStringProperty, который в свою очередь является потомком рассматриваемого класса TPropertyEditor: type TMyCSPEditor = class(TStringProperty) . Переопределим в нем несколько методов таким образом (фрагменты файла):

Код
type

TVRSIDBListViewExcludeColumnsPropertyEditor = class(TStringProperty)

function GetAttributes: TPropertyAttributes; override;

procedure Edit; override;

end;





procedure TVRSIDBListViewExcludeColumnsPropertyEditor.Edit;

var Text: string;

begin

if InputQuery('Введите строковое значение',Text)=False then Exit;

Self.SetValue(Text);

end;



function TVRSIDBListViewExcludeColumnsPropertyEditor.GetAttributes: TPropertyAttributes;

begin

Result:=[paDialog];

end;







Итак, приступаем к рассмотрению методов класса TPropertyEditor. Начнем с тех, которые мы уже использовали.



Метод Edit. Просто вызывается при щелчке на кнопке "…" в Object Inspector. В TStringProperty не переопределен.



Метод SetValue(Text: string). Должен устанавливать значение свойства в переданную строку. В TStringProperty переопределен. Этот метод вызывается самим Object Inspector, когда пользователь вводит значение поля. Вы можете переопределить этот метод для установки вашего свойства в зависимости от значения, введенного пользователем. Если вы обнаруживаете ошибку в переданном параметре - вызовите исключение.



Метод GetAttributes: TPropertyAttributes. Задает параметры свойства. Рассмотрим их по порядку.



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

paSortList - указывает, что список, возвращенный GetValues нужно сортировать

paSubProperties - указывает, что у свойства имеются подсвойства (типа подсвойства Name у свойства Font класса TFont). Подсвойства, если этот флаг установлен, должны возвращаться методом GetProperties.

paDialog - указывает, что рядом со свойством должна быть кнопка "…", по нажатию которой вызывается метод Edit для редактирования значения свойства. Что мы и указали в нашем примере.

paMultiSelect - Разрешает отображать свойство в Object Inspector, даже если выделено более одного объекта

paAutoUpdate - указывает, что метод SetValue нужно вызывать при каждом изменении значения свойства, а не после нажатия Enter или выхода из Object Inspector (Пример: свойство Caption у формы изменяется одновременно с набором на клавиатуре)

paReadOnly - указывает, что значение через Object Inspector изменить нельзя. Оно устанавливается в классе TClassProperty, от которого унаследованы все классовые редакторы свойств типа TStrings, TFont и т.п. При установке рядом со значением свойства отображается строка, возвращенная методом GetValue и значение это изменить нельзя.

paRevertable - указывает, изменение значения свойства можно отменить. Это не касается вложенных подсвойств.

paFullWidthName - указывает Object Inspector, что прорисовка значения свойства не требуется и можно занять под имя свойства всю длину панели.

paVolatileSubProperties - установка этого значения указывает, что при любом изменении свойства нужно повторить сборку подсвойств (GetProperties)

paVCL - ???

paReference - указывает, что свойство является указателем на что-либо. Используется вместе с paSubProperties для указания отображения объекта, на которое ссылается в качестве подсвойств (TFont).

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

Методы GetXXXValue и SetXXXValue. Используются для внутренней установки реального значения свойства. Как правило, используются методом GetValue и SetValue. В принципе, все эти методы уже определены в классе TPropertyEditor, и переопределять их не нужно.



Метод Modified вызывается для указания того факта, что значение свойства изменено. Это метод уже определен в TPropertyEditor и переопределять его не требуется.



Метод GetEditValue возвращает true, если значение можно редактировать



Метод GetIsDefault возвращает true, если значение свойства в текущий момент является значением свойства по умолчанию. Т.е. метод должен возвращать true, если НЕ нужно сохранять значение свойства в .dfm файле.



Метод Activate вызывается при выборе свойства в Object Inspector. При использовании переопределения этого метода для отображения значения свойства исключительно в момент активизации нужно быть осторожным, если указаны параметры свойства paSubProperties и paMultiSelect.



Метод AllEqual вызывается всякий раз, когда выделяется более одного компонента. Если этот метод вернет true, будет вызван метод GetValue, в противоположном случае будет отображена пустая строка. Вызывается только, если указано свойство paMultiSelect. Очевидно, метод должен проверять совпадение свойств у все выбранных компонентов путем опроса метода GetComponent.



Метод AutoFill вызывается для определения, могут ли элементы списка быть выбраны по возрастанию. Указывается, только если указан параметр paValueList.



Метод GetComponent возвращает компонент с заданным индексом из выбранных компонентов.



Метод GetEditLimit возвращает максимальное количество символов, которые можно ввести в текстовое значение свойства. По умолчанию 255.



Метод GetName возвращает имя свойства, в котором знаки подчеркивания заменены на пробелы. Свойство метод должен переопределяться только, если свойство не предназначено для отображения в Object Inspector



Метод GetComponentValue возвращает значение свойства типа TComponent в том и только в том случае, если свойство унаследовано от TComponent. Этот метод переопределяется в классе TComponentEditor



Метод GetProperties вызывается для каждого подсвойства, которое редактируется. В метод передается параметр типа TGetPropertyProc. Это указатель на процедуру для обработки каждого свойства. Например, TClassProperty вызывает процедуру TGetPropertyProc для каждого published элемента класса, а TSetProperty - для каждого элемента множества. Т.е. при использовании подсвойств вы должны определить процедуру TGetPropertyProc, чтобы она определяла каждое подсвойство.



Метод GetPropType возвращает указатель на информацию о типе редактируемого свойства (TypeInfo (Type))



Метод GetValue возвращает значение свойства в виде текстовой строки. Например, в TClassProperty этот метод переопределен для возвращения в качестве результата имени типа класса (TStrings и т.п.).



Метод ValueAvailable возвращает true, если можно получить доступ к значению свойства, не вызывая исключения.



Описания для остальных методов и свойств, к сожалению, найти не удалось, поэтому исследовать их можно только опытным путем.



По завершении создания редактора свойств не забудьте зарегистрировать его внутри метода register вызовом



RegisterPropertyEditor(TypeInfo(<тип свойства>), <тип компонента>, <имя свойства>, <тип редактора свойства>);

RegisterPropertyEditor(TypeInfo(TMyComponentsStringProperty), TMyComponent, '', TMCSPEditor);



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



Вот, собственно, и все. Пишите свой редактор свойств, переопределяйте нужные методы и вперед!



Delphi 7 ToolsAPI: Эксперты. Написание простейшего эксперта

Какой же код нужно написать для создания простейшего эксперта? Для этого нужно написать класс, унаследованный от IOTAWizard (определен в файле ToolsAPI.pas) или одного из его потомков, расположить в модуле процедуру Register, как мы это делали с компонентами, и вызвать внутри ее процедуру



RegisterPackageWizard (const Wizard: IOTAWizard);



например:



Code:

RegisterPackageWizard (TMyExpert.Create as IOTAWizard);




передав ей в качестве параметра экземпляр заранее созданного эксперта.

Рассмотрим класс IOTAWizard.

Код

IOTAWizard = interface(IOTANotifier)

['{B75C0CE0-EEA6-11D1-9504-00608CCBF153}']

{ Expert UI strings }

function GetIDString: string;

function GetName: string;

function GetState: TWizardState;



{ Launch the AddIn }

procedure Execute;

end;







Интерфейс IOTANotifier нам не понадобится, поэтому давайте рассмотрим методы IOTAWizard: Метод GetIDString должен возвращать уникальный идентификатор эксперта. Например: MyCompany.MyExpert

Метод GetName должен возвращать название эксперта

Метод GetState должен возвращать [wsEnabled], если эксперт функционирует, wsChecked если выбран.

Метод Execute вызывается при запуске эксперта из среды IDE.



Итак, если вы хотите сами программировать действия вашего эксперта, включая добавление в меню IDE и прочее и прочее, унаследуйте его от IOTAWizard.



Если вы хотите, чтобы ваш эксперт отображался в репозитории Delphi на произвольной странице и по щелчку по его иконке вызывался его метод Execute - унаследуйте его от IOTARepositoryWizard


Код

IOTARepositoryWizard = interface(IOTAWizard)

['{B75C0CE1-EEA6-11D1-9504-00608CCBF153}']

function GetAuthor: string;

function GetComment: string;

function GetPage: string;

function GetGlyph: Cardinal;

end;






Метод GetAuthor должен возвращать имя автора,

Метод GetComment - комментарий,

Метод GetPage - страницу на которой будет расположена иконка эксперта

Метод GetGlyph - дескриптор иконки



Если вы хотите, чтобы эксперт появлялся на странице форм в репозитории - унаследуйте его от IOTAFormWizard. Он имеет все те же методы и свойства, что и IOTARepositoryWizard, если на странице проектов - от IOTAProjectWizard. Он тоже аналогичен IOTARepositoryWizard.



Если же вы хотите, чтобы пункт меню для вызова метода вашего эксперта Execute помещался в мень Help главного меню IDE, унаследуйте вашего эксперта от IOTAMenuWizard:

Код

IOTAMenuWizard = interface(IOTAWizard)

['{B75C0CE2-EEA6-11D1-9504-00608CCBF153}']

function GetMenuText: string;

end;





Метод GetMenuText должен возвращать имя пункта меню для отображения, а метод GetState возвращает стиль элемента меню (Enabled, Checked)

Вот так все просто, оказывается!



Расположение эксперта внутри DLL библиотеки


Если вы хотите расположить вашего эксперта не в пакете, а в DLL библиотеке, библиотека должна экспортировать функцию INITWIZARD0001 следующего формата:


Код

type TWizardRegisterProc = function(const Wizard: IOTAWizard): Boolean;

type TWizardTerminateProc = procedure;

function INITWIZARD0001(const BorlandIDEServices: IBorlandIDEServices; RegisterProc: TWizardRegisterProc; var Terminate: TWizardTerminateProc): Boolean stdcall;




Для регистрации вашего эксперта вызовите внутри этой функции RegisterProc и передайте ей экземпляр заранее созданного класса вашего эксперта. BorlandIDEServices - указатель на основной интерфейс для работы со всей IDE. Отдельные части его мы рассмотрим далее. По окончании работы IDE или при принудительной выгрузке вашего эксперта будет вызвана функция Terminate, которую вы должны передать среде.



Поместите полный путь к DLL в ключ реестра

HKEY_CURRENT_USERSoftwareBorlandDelphi7.0Experts

или

HKEY_LOCAL_MACHINESOFTWAREBorlandDelphi7.0Experts

Именем ключа может быть произвольная строка.

Эксперт будет запущен только при перезапуске среды, если она выполнялась. Вуаля!

Активизация и использование в IDE окна CPU
Предупреждение: Окно CPU еще до конца не оттестировано и может иногда приводить к ошибкам. Если у вас есть проблемы с отладчиком, или при запуске вашей программы вы не можете им воспользоваться, окно CPU может помочь решить ваши проблемы. Обычно его не требуется включать, если только у вас не "особый случай".



В Delphi 2 эта характеристика встроена, но по умолчанию выключена, называется это окно CPU window, или DisassemblyView. Она легка в использовании, может быть полезной в отладке и сравнении кода при его оптимизации.



Для активизации этой характеристики, запустите REGEDIT и отредактируйте регистры описанным ниже образом. Найдите ключ HKEY_CURRENT_USERSoftwareBorlandDelphi2.0Debugging. Создайте по этому пути строковый ключ с именем "ENABLECPU". Значение нового ключа должно быть строкой "1". Это все. Теперь в Delphi IDE появился новый пункт меню View|CPUWindow. При его активизации выводится новое окно.



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



Создайте 2 одинаковых обработчика события. В каждом обработчике события разместите приведенный ниже код. Установите точку прерывания на первой строчке каждого обработчика. Запустите приложение и активизируйте события. Сравните ассемблерный код обоих методов. Один короче? В этом случае он будет исполняться быстрее.



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



Хорошим примером, где различный код выполняет одну и ту же работу, но делает это с разной скоростью, является использование конструкции "with object do". Исходный код с многократным использованием конструкции "with object do" будет длиннее, но ассемблерный код короче. Вспомните, сколько раз вы устанавливали свойства для динамически создаваемых объектов?

Код:


Код
with TObject.create do

begin

property1 := ;

property2 := ;

property3 := ;

end;




будет выполняться быстрее, чем


Код

MyObj := TObject.create;

MyObj.Property1 := ;

MyObj.Property2 := ;

MyObj.Property3 := ;


Как заставить стартовать Дельфи без заставки?
Командной строкой:

Код
Delphi32.EXE -ns

Как заставить стартовать Дельфи без проекта?
Командной строкой:

Код
Delphi32.exe -np

Как мне избавиться от выскакивающего окна CPU при ошибках?
HKEY_CURRENT_USERSoftwareBorlandDelphi4.0Debugging

ViewCPUOnException=0

Как найти все установленные компоненты?
Код

uses ToolsApi;

{....}

var

a, i: Integer;

begin

with (BorlandIDEServices as IOTAPackageServices) do

begin

for a := 0 to GetPackageCount - 1 do

begin

for i := 0 to GetComponentCount(a) - 1 do

begin

{get each component name with GetComponentName(a, i);}

end;

end;

end;

end;

Как показать содержимое TMemo-поля в TDBGrid?
Поумолчанию, DBGrid не может отображать memo-поля. Однако, проблему можно решить при помощи события OnDrawDataCell в DBGrid.

Код
procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const
Rect: TRect; Field: TField; State:
TGridDrawState);

var
P: array [0..50] of char; {размер массива, это количество необходимых символов}
bs: TBlobStream; {из memo-поля}
hStr: String;

begin
if Field is TMemoField then
begin
with (Sender as TDBGrid).Canvas do
begin {Table1Notes это TMemoField}
bs := TBlobStream.Create(Table1Notes, bmRead);
FillChar(P,SizeOf(P),#0); {строка завершается нулём}
bs.Read(P, 50); {читаем 50 символов из memo в blobStream}
bs.Free;
hStr := StrPas(P);
while Pos(#13, hStr) > 0 do {удаляем переносы каретки и}
hStr[Pos(#13, hStr)] := ' ';
while Pos(#10, hStr) > 0 do {отступы строк}
S[Pos(#10, hStr)] := ' ';

FillRect(Rect); {очищаем ячейку}
TextOut(Rect.Left, Rect.Top, hStr); {заполняем ячейку данными из memo}
end;
end;
end;


Замечание: перед тем, запустить пример, создайте объект TMemoField для memo-поля двойным кликом по компоненту TTable и добавлением memo-поля.

Способ 2:

В обработчик события GetText TMemoField поместите следующую строку:
Код

Text := GrabMemoAsString(TMemoField(Sender));


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

Код
function GrabMemoAsString(TheField: TMemoField): string;
begin
if TheField.IsNull then
Result := ''
else
with TBlobStream.Create(TheField, bmRead) do
begin
if Size >= 255 then
begin
Read(Result[1], 255);
Result[0] := #255;
end
else
begin
Read(Result[1], Size);
Result[0] := Chr(Size);
end;
Free;
while Pos(#10, Result) > 0 do
Result[Pos(#10, Result)] := ' ';
while Pos(#13, Result) > 0 do
Result[Pos(#13, Result)] := ' ';
end;
end;

Как сделать свои собственные сообщения при компилляции?
Формат команды:

Код
{$MESSAGE HINT|WARN|ERROR|FATAL 'text string' }




Например, добавление следующих строк приведёт к появлению:


Код
{$MESSAGE 'Появился новый hint!'}

{$MESSAGE Hint 'И это тоже hint!'}

{$MESSAGE Warn 'А это уже Warning'}

{$MESSAGE Error 'Эта строка вызовет ошибку компиляции!'}

{$MESSAGE Fatal 'А это фатальная ошибка компиляции!'}





Пример:



Код

destructor TumbSelectionTempTable.Destroy;

begin

// Clear the temp tables.

{$MESSAGE Warn ' - remember to free all allocated objects'}

ClearAllOuterWorldFold;

if FSubjectsTempTableCreated then

DropTempTable(FTableName);



FOuterWorldsFolded.Free;

FQuery.Free;

inherited;

end;


Как создать простейший эксперт?
Код

{

This unit can be compiled into a package and will then appear in the Delphi

Help menu.

}

unit SDCSimpleExpert;



interface



uses ToolsApi;



type

TSDCSimpleExpert = class(TNotifierObject, IOTAMenuWizard, IOTAWizard)

public

function GetIDString: string;

function GetName: string;

function GetState: TWizardState;

procedure Execute;

function GetMenuText: string;

end;



procedure Register;



implementation



uses Dialogs;



procedure Register;

begin

{register expert}

RegisterPackageWizard(TSDCSimpleExpert.Create);

end;



{ TSDCSimpleExpert }



procedure TSDCSimpleExpert.Execute;

begin

{code to execute when menu item is clicked}

ShowMessage('Hello SwissDelphiCenter Simple Expert.');

end;



function TSDCSimpleExpert.GetIDString: string;

begin

{unique expert identifier}

Result := 'SwissDelphiCenter.SimpleExpert';

end;



function TSDCSimpleExpert.GetMenuText: string;

begin

{caption of menu item in help menu}

Result := 'SwissDelphiCenter Simple Expert';

end;



function TSDCSimpleExpert.GetName: string;

begin

{name of the expert}

Result := 'SwissDelphiCenter Simple Expert';

end;



function TSDCSimpleExpert.GetState: TWizardState;

begin

Result := [wsEnabled];

end;



end.

Как создать свой пункт меню в Дельфи IDE?
Код
{....}



uses ToolsApi, Menus;



{....}



var

item: TMenuItem;

begin

{get reference to delphi's mainmenu. You can handle it like a common TMainMenu}

with (BorlandIDEServices as INTAServices).GetMainMenu do

begin

item := TMenuItem.Create(nil);

item.Caption := 'A Mewn caption';

Items.Add(item);

end;

end;
Как узнать версию компилятора?
Иногда надо выполнить разный код в зависимости от версии Дельфи, особенно актуально это при разработки компонентов и модулей, которые используются в разных приложениях.



В Дельфи предопределены специальные константы компиляции для этого:



Ver80 - Дельфи 1

Ver90 - Дельфи 2

Ver93 - С Buider 1

Ver100 - Дельфи 3

Ver110 - С Buider 3

Ver120 - Дельфи 4

Ver125 - С Buider 4

Ver130 - Дельфи 5

Ver140 - Дельфи 6

Ver150 - Дельфи 7



Пример использования:


Код
procedure TForm1.Button2Click(Sender: TObject);



{©Drkb v.3(2007): www.drkb.ru,

®Vit (Vitaly Nevzorov) - nevzorov@yahoo.com}

const Version=

{$Ifdef Ver80}'Дельфи 1';{$EndIf}

{$Ifdef Ver90}'Дельфи 2';{$EndIf}

{$Ifdef Ver100}'Дельфи 3';{$EndIf}

{$Ifdef Ver120}'Дельфи 4';{$EndIf}

{$Ifdef Ver130}'Дельфи 5 ';{$EndIf}

{$Ifdef Ver140}'Дельфи 6';{$EndIf}

{$Ifdef Ver150}'Дельфи 7';{$EndIf}

begin

ShowMessage('Для компиляции этой программы был использован '+Version);

end;


Какие есть директивы компилятора?
{$I+} и {$I-} - директивы контроля ввода/вывода

{$M} и {$S} - директивы, определяющие размер стека



{$M+} и {$M-} - директивы информации времени выполнения о типах

{$Q+} и {$Q-} - директивы проверки переполнения целочисленных операций

{$R} - директива связывания ресурсов


{$R+} и {$R-} - директивы проверки диапазона


{$APPTYPE CONSOLE} - директива создания консольного приложения

1) Директивы компилятора, разрешающие или запрещающие проверку утверждений.

По умолчанию {$C+} или {$ASSERTIONS ON}

Область действия локальная



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

директива {$C+} и процедура Assert генерирует исключение EAssertionFailed, если проверяемое утверждение ложно.

Так как эти проверки используются только в процессе отладки программы, то перед ее окончательной компиляцией следует указать директиву {$C-}. При этом работа процедур Assert будет блокировано и генерация исключений EassertionFailed производиться не будет.

Директивы действуют на весь файл исходного кода независимо от того, в каком месте файла они расположены.



2) Директивы компилятора, включающие и выключающие контроль файлового ввода-вывода.



По умолчанию {$I+} или {$IOCHECKS ON}

Область действия локальная



Директивы компилятора $I включают или выключают автоматический контроль результата вызова процедур ввода-вывода Object Pascal. Если действует директива {$I+}, то при возвращении процедурой ввода-вывода ненулевого значения генерируется исключение EInOutError и в его свойство errorcode заносится код ошибки. Таким образом, при действующей директиве {$I+} операции ввода-вывода располагаются в блоке try...except, имеющем обработчик исключения EInOutError. Если такого блока нет, то обработка производится методом TApplication.HandleException.

Если действует директива {$I-}, то исключение не генерируется. В этом случае проверить, была ли ошибка, или ее не было, можно, обратившись к функции IOResult. Эта функция очищает ошибку и возвращает ее код, который затем можно анализировать. Типичное применение директивы {$I-} и функции IOResult демонстрирует следующий пример:





Код
{$I-}
AssignFile(F,s);

Rewrite(F);
{$I+}
i:=IOResult;
if i<>0 then
case i of

2: ..........

3: ..........

end;





В этом примере на время открытия файла отключается проверка ошибок ввода вывода, затем она опять включается, переменной i присваивается значение, возвращаемое функцией IOResult и, если это значение не равно нулю (есть ошибка), то предпринимаются какие-то действия в зависимости от кода ошибки. Подобный стиль программирования был типичен до введения в Object Pascal механизма обработки исключений. Однако сейчас, по-видимому, подобный стиль устарел и применение директив $I потеряло былое значение.



3) Директивы компилятора, определяющие размер стека



По умолчанию {$M 16384,1048576}

Область действия глобальная



Локальные переменные в процедурах и функциях размещаются в стеке приложения. При каждом вызове процедуры или функции ее локальные переменные помещаются в стек. При выходе из процедуры или функции эти локальные процедуры удаляются из стека.

Директивы компилятора $M задают параметры стека приложения: его минимальный и максимальный размеры. Приложение всегда гарантировано имеет размер стека, равный его минимальной величине. Если при запуске приложения Windows обнаруживает, что не может выделить этот минимальный объем памяти, то выдается сообщение об этой ошибке.

Если во время работы выясняется, что минимального размера стека не хватает, то размер увеличивается на 4 K, но не более, чем до установленного директивой максимального размера. Если увеличение размера стека невозможно из-за нехватки памяти или из-за достижения его максимальной величины, генерируется исключение EStackOverflow. Минимальный размер стека по умолчанию равен 16384 (16K). Этот размер может изменяться параметром minstacksize директивы {$M} или параметром number директивы {$MINSTACKSIZE}.

Максимальный размер стека по умолчанию равен 1,048,576 (1M). Этот размер может изменяться параметром maxstacksize директивы {$M} или параметром number директивы {$MAXSTACKSIZE number}. Значение минимального размера стека может задаваться целым числом в диапазоне между1024 и 2147483647. Значение максимального размера стека должно быть не менее минимального размера и не более 2147483647. Директивы задания размера стека могут включаться только в программу и не должны использоваться в библиотеках и модулях.


В Delphi 1 имеется процедура компилятора {$S}, осуществляющая переключение контроля переполнения стека. Теперь этот процесс полностью автоматизирован и директива {$S} оставлена только для обратной совместимости.


4) Директивы компилятора, включающие и выключающие генерацию информации времени выполнения о типах (runtime type information - RTTI).


По умолчанию {$M-} или {$ TYPEINFO OFF}

Область действия локальная


Директивы компилятора $M включают или выключают генерацию информации времени выполнения о типах (runtime type information - RTTI). Если класс объявляется в состоянии {$M+} или является производным от класса объявленного в этом состоянии, то компилятор генерирует RTTI о его полях, методах и свойствах, объявленных в разделе published. В противном случае раздел published в классе не допускается. Класс TPersistent, являющийся предшественником большинства классов Delphi и все классов компонентов, объявлен в модуле Classes в состоянии {$M+}. Так что для всех классов, производных от него, заботиться о директиве {$M+}не приходится.


5) Директивы компилятора, включающие и выключающие проверку переполнения при целочисленных операциях



По умолчанию {$Q-} или {$OVERFLOWCHECKS OFF}

Область действия локальная



Директивы компилятора $Q включают или выключают проверку переполнения при целочисленных операциях. Под переполнением понимается получение результата, который не может сохраняться в регистре компьютера. При включенной директиве {$Q+} проверяется переполнение при целочисленных операциях +, -, *, Abs, Sqr, Succ, Pred, Inc и Dec. После каждой из этих операций размещается код, осуществляющий соответствующую проверку. Если обнаружено переполнение, то генерируется исключение EIntOverflow. Если это исключение не может быть обработано, выполнение программы завершается.

Директивы $Q проверяют только результат арифметических операций. Обычно они используются совместно с директивами {$R}, проверяющими диапазон значений при присваивании.

Директива {$Q+} замедляет выполнение программы и увеличивает ее размер. Поэтому обычно она используется только во время отладки программы. Однако, надо отдавать себе отчет, что отключение этой директивы приведет к появлению ошибочных результатов расчета в случаях, если переполнение действительно произойдет во время выполнении программы. Причем сообщений о подобных ошибках не будет.



6) Директивы компилятора, включающие и выключающие проверку диапазона целочисленных значений и индексов



По умолчанию {$R} или {$RANGECHECKS OFF}

Область действия локальная



Директивы компилятора $R включают или выключают проверку диапазона целочисленных значений и индексов. Если включена директива {$R+}, то все индексы массивов и строк и все присваивания скалярным переменным и переменным с ограниченным диапазоном значений проверяются на соответствие значения допустимому диапазону. Если требования диапазона нарушены или присваиваемое значение слишком велико, генерируется исключение ERangeError. Если оно не может быть перехвачено, выполнение программы завершается.

Проверка диапазона длинных строк типа Long strings не производится.

Директива {$R+} замедляет работу приложения и увеличивает его размер. Поэтому она обычно используется только во время отладки.



6) Директива компилятора, связывающая с выполняемым модулем файлы ресурсов


Область действия локальная


Директива компилятора {$R} указывает файлы ресурсов (.DFM, .RES), которые должны быть включены в выполняемый модуль или в библиотеку. Указанный файл должен быть файлом ресурсов Windows. По умолчанию расширение файлов ресурсов - .RES.

В процессе компоновки компилированной программы или библиотеки файлы, указанные в директивах {$R}, копируются в выполняемый модуль. Компоновщик Delphi ищет эти файлы сначала в том каталоге, в котором расположен модуль, содержащий директиву {$R}, а затем в каталогах, указанных при выполнении команды главного меню Project | Options на странице Directories/Conditionals диалогового окна в опции Search path или в опции /R командной строки DCC32.

При генерации кода модуля, содержащего форму, Delphi автоматически включает в файл .pas директиву {$R *.DFM}, обеспечивающую компоновку файлов ресурсов форм. Эту директиву нельзя удалять из текста модуля, так как в противном случае загрузочный модуль не будет создан и генерируется исключение EResNotFound.

Код определения свойств
Итак вам опять нужно "немного" кода. Вот небольшой примерчик компонента лично для вас и остальных моих читателей. Установите этот компонент в палитру Delphi, бросьте экземпляр на форму, закройте ее и модуль и откройте форму как файл формы, используя в диалоге открытия тип *.dfm. Вы увидите дополнительные свойства 'StringThing' и 'Thing'. Первое - свойство строки, второе - бинарное свойство, фактически запись. Если вы имеете HexEdit (шестнадцатиричный редактор) или что-то аналогичное, взгляните на ваш dfm-файл и вы увидите тэги ваших новых свойств вместе с их именами.



Если TReader/TWriter имеет специфические методы для чтения/записи свойств и вы хотите добавить, например, строку, целое, символ или что-то еще (проверьте описание соответствующих методов TReader в файлах помощи), то в этом случае используйте DefineProperty. В случае сложного объекта используйте DefineBinaryProperty и ваши методы чтения и записи получат TStream вместо TReader/TWriter.



Код
unit PropDemo;



interface



uses

SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,

Forms, Dialogs;



type

TDemoProps = class(TComponent)

private

{ Private declarations }

FStringThing: string;

FThing: record

i, j, k: integer;

x, y: real;

ch: char;

end;

procedure ReadStringThing(Reader: TReader);

procedure WriteStringThing(Writer: TWriter);

procedure ReadThing(Stream: TStream);

procedure WriteThing(Stream: TStream);

protected

{ Protected declarations }

procedure DefineProperties(Filer: TFiler); override;

public

{ Public declarations }

constructor Create(AOwner: TComponent); override;

published

{ Published declarations }

end;



procedure Register;



implementation



constructor TDemoProps.Create(AOwner: TComponent);



begin

inherited Create(AOwner);

{ создайте любые данные, чтобы было что передать в поток}

FStringThing := 'Всем привет!';

with FThing do

begin

i := 1;

j := 2;

k := 3;

x := PI;

y := 180 / PI;

ch := '?';

end;

end;



procedure TDemoProps.ReadStringThing(Reader: TReader);

begin

FStringThing := Reader.ReadString;

end;



procedure TDemoProps.WriteStringThing(Writer: TWriter);

begin

Writer.WriteString(FStringThing);

end;



procedure TDemoProps.ReadThing(Stream: TStream);

begin

Stream.ReadBuffer(FThing, sizeof(FThing));

end;



procedure TDemoProps.WriteThing(Stream: TStream);

begin

Stream.WriteBuffer(FThing, sizeof(FThing));

end;



procedure TDemoProps.DefineProperties(Filer: TFiler);

begin

inherited DefineProperties(Filer);

Filer.DefineProperty('StringThing', ReadStringThing, WriteStringThing,

FStringThing <> '');

Filer.DefineBinaryProperty('Thing', ReadThing, WriteThing, true);

end;



procedure Register;

begin

RegisterComponents('Samples', [TDemoProps]);

end;



end.


Показ свойств во время выполнения программы
Я написал компонент-отладчик, выводящий в дереве все компоненты. Попробуйте этот код. Вызывайте функцию DisplayProperties как показано ниже:


Код

DisplayProperties(Form1, {Вы можете использовать любой компонент}

Outline1.Lines, {Допускается любой TStrings-объект}

0); {0 - "стартовый", корневой уровень}



DisplayProperties(AObj: TObject; AList: TStrings; iIndentLevel: Integer);

var

Indent: string;

ATypeInfo: PTypeInfo;

ATypeData: PTypeData;

APropTypeData: PTypeData;

APropInfo: PPropInfo;

APropList: PPropList;

iProp: Integer;

iCnt: Integer;

iCntProperties: SmallInt;

ASecondObj: TObject;



procedure AddLine(sLine: string);

begin

AList.Add(Indent + #160 + IntToStr(iProp) + ': ' + APropInfo^.Name

+ ' (' + APropInfo^.PropType^.Name + ')' + sLine);

end;



begin



try

Indent := GetIndentSpace(iIndentLevel);



ATypeInfo := AObj.ClassInfo;

ATypeData := GetTypeData(ATypeInfo);

iCntProperties := ATypeData^.PropCount;

GetMem(APropList, SizeOf(TPropInfo) * iCntProperties);

GetPropInfos(ATypeInfo, APropList);



for iProp := 0 to ATypeData^.PropCount - 1 do

begin

APropInfo := APropList^[iProp];

case APropInfo^.PropType^.Kind of

tkInteger:

AddLine(' := ' + IntToStr(GetOrdProp(AObj, APropInfo)));

tkChar:

AddLine(' := ' + chr(GetOrdProp(AObj, APropInfo)));

tkEnumeration:

begin

APropTypeData := GetTypeData(APropInfo^.PropType);

if APropTypeData^.BaseType^.Name <> APropInfo^.PropType^.Name then

AddLine(' := ' + IntToStr(GetOrdProp(AObj, APropInfo)))

else

AddLine(' := ' + APropTypeData^.NameList);

end;

tkFloat:

AddLine(' := ' + FloatToStr(GetFloatProp(AObj, APropInfo)));

tkString:

AddLine(' := "' + GetStrProp(AObj, APropInfo) + '"');

tkSet:

begin

AddLine(' := ' + IntToStr(GetOrdProp(AObj, APropInfo)));

end;

tkClass:

begin

ASecondObj := TObject(GetOrdProp(AObj, APropInfo));

if ASecondObj = nil then

AddLine(' := NIL')

else

begin

AddLine('');

DisplayProperties(ASecondObj, AList, iIndentLevel + 1);

end;

end;

tkMethod:

begin

AddLine('');

end;

else

AddLine(' := >>НЕИЗВЕСТНО<<');

end;

end;

except {Выводим исключение и продолжаем дальше}

on e: Exception do ShowMessage(e.Message);

end;



FreeMem(APropList, SizeOf(TPropInfo) * iCntProperties);

end;



function GetIndentSpace(iIndentLevel: Integer): string;

var iCnt: Integer;

begin

Result := '';

for iCnt := 0 to iIndentLevel - 1 do

Result := Result + #9;

end;

Работа с IDE из программы
Код

function LaunchedFromDelphiIDE: Boolean;

{----------------------------------------------------------------}

{ Осуществляем проверку запущенности приложения из-под Delphi }

{ IDE. Идея взята из сообщения в Delphi-Talk от Ed Salgado }

{ из Eminent Domain Software. }

{----------------------------------------------------------------}



begin

LaunchedFromDelphiIDE := Bool(PrefixSeg) {т.е. не DLL} and

Bool(PWordArray(MemL[DSeg: 36])^[8]);

end; {LaunchedFromDelphiIDE}



function DelphiLoaded: Boolean;

{----------------------------------------------------------------}

{ Проверяем, загружена ли Delphi. Дает правильные результаты }

{ - если вызывающее приложение запущено отдельно, или из-под IDE}

{ - если Delphi имеет открытый проект }

{ - если Delphi минимизирована. }

{ Автор идеи Wade Tatman (wtatman@onramp.net). }

{----------------------------------------------------------------}



begin

DelphiLoaded := false;

if WindowExists('TPropertyInspector', 'Object Inspector') then

if WindowExists('TMenuBuilder', 'Menu Designer') then

if WindowExists('TAppBuilder', '(AnyName)') then

if WindowExists('TApplication', 'Delphi') then

if WindowExists('TAlignPalette', 'Align') then

DelphiLoaded := true;

end; {DelphiLoaded}



function DelphiInstalled: Boolean;

{----------------------------------------------------------------}

{ Проверяем наличие Delphi.ini, ищем в нем путь к Библиотеке }

{ Компонентов, после чего проверяем ее наличие по этому пути. }

{----------------------------------------------------------------}



var

IniFile: string;

begin

DelphiInstalled := false;

IniFile := WindowsDirectory + 'Delphi.ini';

if FileExists(IniFile) then

if FileExists(GetIni(IniFile, 'Library', 'ComponentLibrary')) then

DelphiInstalled := true;

end; {DelphiInstalled}



Я уверен, что один из приведенных выше методов вам поможет.Последние две

подпрограммы используют некоторые другие инкапсуляции Windows API и классов

Delphi, и они определены следующим образом:



function WindowExists(WindowClass, WindowName: string): Boolean;

{----------------------------------------------------------------}

{ С помощью паскалевских строк проверяем наличие определенного }

{ окна. Для поиска только имени окна (WindowName), используем }

{ WindowClass '(AnyClass)'; для поиска только класса окна }

{ (WindowClass), используем WindowName '(AnyName)'. }

{----------------------------------------------------------------}



var

PWindowClass, PWindowName: PChar;

AWindowClass, AWindowName: array[0..63] of Char;

begin

if WindowClass = '(AnyClass)' then

PWindowClass := nil

else

PWindowClass := StrPCopy(PChar(@AWindowClass), WindowClass);



if WindowName = '(AnyName)' then

PWindowName := nil

else

PWindowName := StrPCopy(PChar(@AWindowName), WindowName);



if FindWindow(PWindowClass, PWindowName) <> 0 then

WindowExists := true

else

WindowExists := false;

end; {WindowExists}



function WindowsDirectory: string;

{----------------------------------------------------------------}

{ Возвращаем путь к каталогу Windows (без обратной косой черты) }

{----------------------------------------------------------------}



const

BufferSize = 144;

var

ABuffer: array[0..BufferSize] of Char;

begin

if GetWindowsDirectory(PChar(@ABuffer), BufferSize) = 0 then

WindowsDirectory := ''

else

WindowsDirectory := StrPas(PChar(@ABuffer));

end; {WindowsDirectory}



function GetIni(const IniFile, Section, Entry: string): string;

{----------------------------------------------------------------}

{ Получаем инициализационную 'profile' строку из определенного }

{ пункта (Entry) определенной секции [Section] определенного }

{ INI-файла (дополняем '.ini', если отсутствует). Возвращаем }

{ нулевую строку, если IniFile, Section или Entry не найден. }

{----------------------------------------------------------------}



var

IniFileVar: string;

IniFileObj: TIniFile;

begin

if StrEndsWith(IniFile, '.ini') then

IniFileVar := IniFile

else

IniFileVar := IniFile + '.ini';

IniFileObj := TIniFile.Create(IniFileVar);

GetIni := IniFileObj.ReadString(Section, Entry, '');

IniFileObj.Free;

end; {GetIni}

Регистрация редактора свойства
Скажем, вы имеете компонент TContainer, содержащий TContainedClass в свойстве с именем 'Contained' - попробуйте добавить следующую строку к процедуре Register вашего компонента:


Код
RegisterPropertyEditor(TypeInfo(TContainedClass),

TContainer,

'Contained',

TClassProperty);



Не забудьте добавить TypInfo и DsgnIntf в список используемых модулей.


Все это задокументировано в справке помощи. Первым параметром всегда идет TypeInfo() с "коллекционируемым" классом в качестве параметра, затем контейнерный класс или NIL, если он должен работать для всех экземпляров контейнерного класса с заданным свойством, затем идет имя контейнерного свойства или '', если редактор должен работать для всех свойств, и завершает славную четверку параметров класс TClassProperty, расширяющий классовое свойство, т.е. "создающий" знак "+" в Инспекторе Объектов, позволяющий редактировать вложенные свойства (щелчок на плюсике раскрывает список вложенных свойств описываемого контейнерного класса).

Смена свойств приложения, открываемого, по умолчанию в среде при её запуске
Большинство стандартных темплейтов зашиты в delphide70.bpl (70 - версия), остальные - в каталоге Objrepos. Описаны же последние в файле bindelphi32.dro. Т.о:


1. Добавляем в "delphi32.dro" строки:



Код
[C:Program FilesLangDelphi7ObjReposMyAppMyApp]

Type=ProjectTemplate

Page=Projects

Name=My Application

Description=This is my application template

Author=Eugene

Icon=C:Program FilesLangDelphi7ObjReposMyAppMyApp.ico

DefaultProject=1

Designer=dfm



(для темплейтов формы Type=FormTemplate, DefaultMainForm=0/1, DefaultNewForm=0/1)


2. Размещаем нашу темплейт-прогу в каталоге "C:Program FilesLangDelphi7ObjReposMyApp" и называем её "MyApp.dpr".


3. Жмём "File/New/Application" (т.к. у нас DefaultProject=1), либо заходим во вкладку "Projects", а затем кликаем два раза по "My Application".


4. Радуемся!

Создание редактора свойства
Если вы присвоили свойству имя TableName, то полный цикл создания редактора свойств включает следующие шаги:


Опишите класс редактора свойства:

Код
type

TTableNameProperty = class(TStringProperty)

function GetAttributes: TPropertyAttributes; override;

procedure GetValues(Proc: TGetStrProc); override;

end;

implementation

{ TTableNameProperty }

function TTableNameProperty.GetAttributes: TPropertyAttributes;

begin

Result := [paValueList];

end;



procedure TTableNameProperty.GetValues(Proc: TGetStrProc);

var

TableName: String;

I: Integer;

begin

{ здесь вы должны добавить свой код, ?тобы с помощью цикла обойти имена всех

таблиц, включенных в список }

for I := 0 to ???? do

begin

TableName := ????[I];

Proc(TableName);

end;

end;


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

Код
RegisterPropertyEditor(TypeInfo(string), TcsNotebook, 'TableName', TTableNameProperty);


Текущий модуль и проект
Компонент во время проектирования может знать имена текущих модулей и имя проекта. Все это можно получить с помощью ToolServices (см. файл TOOLINTF.PAS)


Имя текущего проекта можно получить с помощью вызова GetProjectName, список модулей/форм - с помощью функции GetUnitCount, которая возвратит количество модулей и затем с помощью GetUnitName(i) мы можем получить имя каждого модуля (также и с формами).


Вот примерный образец кода (получение и запись имен всех модулей/форм в StringGrid и имени проекта в Label):


Код

procedure TInformationFrm.FormActivate(Sender: TObject);

{ необходимо: StringGrid1 (2 колонки, масса строк), Label1, Label2 }

var

i, j: Integer;

Tmp: string;

begin

StringGrid1.Cells[0, 0] := 'модулей:';

StringGrid1.Cells[1, 0] := 'форм:';

if ToolServices <> nil then

with ToolServices do

begin

Label1.Caption := ExtractFileName(GetProjectName); { простое имя }

Label2.Caption := GetProjectName; { полное правильное имя пути }

for i := 0 to GetUnitCount do

begin

Tmp := ExtractFileName(GetUnitName(i));

StringGrid1.Cells[0, i + 1] := Tmp;

Tmp := ChangeFileExt(Tmp, '.DFM');

for j := 0 to GetFormCount do

if ExtractFileName(GetFormName(j)) = Tmp then

StringGrid1.Cells[1, i + 1] := Tmp

end;

end;

end;




Печать страницы
Печать страницы


Внимание! Если у вас не получилось найти нужную информацию, используйте рубрикатор или воспользуйтесь поиском


.



книги по программированию исходники компоненты шаблоны сайтов C++ PHP Delphi скачать