end;
TNotebook
Операция по заполнению элементами управления компонента TNotebook почти такая же, как и в TTabbedNotebook – разница лишь в типе класса – TPage вместо TTabPage. Тем не менее, если вы заглянете в DELPHIDOCEXTCTRLS.INT, декларацию класса TPage вы там не найдете. По неизвестной причине Borland не включил определение TPage и в DOC-файлы, поставляемые с Delphi. Декларация TPage в EXTCTRLS.PAS (можно найти в библиотеке VCL-исходников), правда, расположена в интерфейсной части модуля. Мы восполним пропущенную информацию о классе TPage:
TPage = class(TCustomControl)
private
procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
protected
procedure ReadState(Reader: TReader); override;
procedure Paint; override;
public
constructor Create(AOwner: TComponent); override;
published
property Caption;
property Height stored False;
property TabOrder stored False;
property Visible stored False;
property Width stored False;
end;
Теперь, по аналогии с вышеприведенной процедурой, попробуем добавить кнопку на TNotebook. Все, что мы должны сделать – заменить "TTabbedNotebook" на "TNotebook" и "TTabPage" на "TPage". Вот что должно получиться:
{ Данная процедура добавляет кнопку в случайной позиции на }
{ текущей странице данного TNotebook. }
procedure AddButton(Notebook1: TNotebook);
var
page: TPage;
button: TButton;
begin
with Notebook1 do page:= TPage(Pages.Objects[PageIndex]);
button:= TButton.Create(page);
try
with button do begin
Parent:= page;
Left:= Random(page.ClientWidth – Width);
Top:= Random(page.ClientHeight – Height);
end;
except
button.Free;
end;
end;
Остальное не менее просто!
Недоступная закладка в компоненте Tabbednotebook
Есть ли возможность в компоненте Tabbednotebook сделать какую-либо страницу недоступной? То есть не позволять пользователю щелкать на ней и видеть ее содержимое?
Да, такая возможность существует. Самый простой путь – удалить страницу, например так:
with TabbedNotebook do Pages.Delete(PageIndex);
и снова включить ее (при необходимости), перегрузив форму.
Блокировка (а не удаление) немного мудренее, поскольку необходима организация цикла в процедуре создания формы, присваивающая имена закладкам компонента TabbedNotebook. Например так:
J:= 0;
with TabbedNotebook do for I:= 0 to ComponentCount - 1 do if Components[I].ClassName = 'TTabButton' then begin
Components[I].Name:= ValidIdentifier(TTabbedNotebook(Components[I].Owner).Pages[J]) + 'Tab';
Inc(J);
end;
где ValidIdentifier ValidIdentifier – функция, которая возвращает правильный Pascal-идентификатор, производный от строки 'Tab':
function ValidIdentifier(theString: str63): str63;
{--------------------------------------------------------}
{ Конвертирует строку в правильный Pascal-идентификатор, }
{ удаляя все неправильные символы и добавляя символ '_', }
{ если первый символ – цифра }
{--------------------------------------------------------}
var
I, Len: Integer;
begin
Len:= Length(theString);
for I:= Len downto 1 do if not (theString[I] in LettersUnderscoreAndDigits) then Delete(theString, I, 1);
if not (theString[1] in LettersAndUnderscore) then theString:= '_' + theString;
ValidIdentifier:= theString;
end; {ValidIdentifier}
Затем мы можем сделать закладку компонента TabbedNotebook недоступной:
with TabbedNotebook do begin
TabIdent:= ValidIdentifier(Pages[PageIndex]) + 'Tab';
TControl(FindComponent(TabIdent)).Enabled:= False;
{ Переключаемся на первую доступную страницу: }
for I:= 0 to Pages.Count – 1 do begin
TabIdent:= ValidIdentifier(Pages[I]) + 'Tab';
if TControl(FindComponent(TabIdent)).Enabled then begin
PageIndex:= I;
Exit;
end;
end; {for}
end; {with TabbedNotebook}
следующий код восстанавливает доступность страницы:
with TabbedNotebook do for I:= 0 to Pages.Count - 1 do begin
TabIdent:= ValidIdentifier(Pages[I]) + 'Tab';
if not TControl(FindComponent(TabIdent)).Enabled:= True;
end; {for}
Создание компонента TTable без формы
Решение 1
Действительно, любой компонент можно создать и без (вне) формы или любого другого дочернего компонента. Для этого я использую параметр nil:
FSession:= TSession.Create(nil);
FDatabase:= TDatabase.Create(nil);
FSession.SessionName:= 'DBSession'
FDatabase.Connected:= False;
FDatabase.AliasName:= Database;
FDatabase.DatabaseName:= USER_DATABASE;
FDatabase.SessionName:= FSession.SessionName;
FUserTBL:= TTable.Create(nil);
FUserTBL.DatabaseName:= FDatabase.DatabaseName;
FUserTBL.SessionName:= FSession.SessionName;
FUserTBL.TableName:= USERTBL;
FUserTBL.IndexName:= USERSpIndex;
FUserSource:= TDataSource.Create(nil);
FUserSource.DataSet:= FUserTBL;
Решение 2
Я привожу некоторый код, касающийся описываемой проблемы: он работал, когда я использовал его в большом приложении. Я не знаю специфического метода создания компонента TTable вне родителей, поэтому я пошел путем создания своего класса от TTable во время инициализации модуля. Удобство такого подхода объясняется наличием под рукой всегда готового к работе экземпляра класса, стоит всего-лишь добавить модуль к вашему приложению. Конечно, новый класс не должен иметь одиноко выглядящую процедуру со странной технологией фильтрации данных :=))), да и не помешала бы публикация нескольких событий, но этот пример призван все-го лишь продемонстрировать иной подход к решаемой задаче.
unit Unit2;
interface
uses db, DBTables, dialogs;
type fake = class(Ttable)
procedure fakeFilterRecord(DataSet: TDataSet; var Accept: Boolean);
end;
var
MyTable: fake;
implementation
procedure fake.fakeFilterRecord(DataSet: TDataSet; var Accept: Boolean);
begin
showmessage('Здравствуй, Вася');
end;
Initialization
MyTable:= fake.create(nil);
With Mytable do begin
DataBaseName:= 'dbdemos';
TableName:= 'biolife';
OnFilterRecord:= MyTable.fakeFilterRecord;
Filtered:= true;
active:= true;
end;
{проверка получением неких данных…}
showmessage(MyTable.fields[1].asstring);
Finalization
{Важно! MyTable не имеет родителя, – уничтожаем объект сами, иначе память не высвобождается…}
MyTable.free;
end.
Ускорение работы TreeView
Представляем вашему вниманию немного переработанный компонент TreeView, работающий быстрее своего собрата из стандартной поставки Delphi. Кроме того, была добавлена возможность вывода текста узлов и пунктов в жирном начертании (были использованы методы TreeView, хотя, по идее, необходимы были свойства TreeNode. Мне показалось, что это будет удобнее).
Для сравнения:
TreeView:
128 сек. для загрузки 1000 элементов (без сортировки)*
270 сек. для сохранения 1000 элементов (4.5 минуты!!!)
HETreeView:
1.5 сек. для загрузки 1000 элементов – ускорение около 850%!!! (2.3 секунды без сортировки = stText)*
0.7 сек. для сохранения 1000 элементов – ускорение около 3850%!!!
Примечание:
• Все операции выполнялись на медленной машине 486SX 33 Mгц, 20 Mб RAM.
• Если TreeView пуст, загрузка происходит за 1.5 секунды, плюс 1.5 секунды на стирание 1000 элементов (общее время загрузки составило 3 секунды). В этих условиях стандартный компонент TTreeView показал общее время 129.5 секунд. Очистка компонента осуществлялась вызовом функции SendMessage(hwnd, TVM_DELETEITEM, 0, Longint(TVI_ROOT)).
Проведите несколько приятных минут, развлекаясь с компонентом.
unit HETreeView;
{$R-}
// Описание: Реактивный TreeView
(*
TREEVIEW:
128 сек. для загрузки 1000 элементов (без сортировки)*
270 сек. для сохранения 1000 элементов (4.5 минуты!!!)
HETREEVIEW:
1.5 сек. для загрузки 1000 элементов – ускорение около 850%!!! (2.3 секунды без сортировки = stText)*
0.7 сек. для сохранения 1000 элементов – ускорение около 3850%!!!
NOTES:
– Все операции выполнялись на медленной машине 486SX 33 Mгц, 20 Mб RAM.
– * Если TTreeView пуст, загрузка происходит за 1.5 секунды,
плюс 1.5 секунды на стирание 1000 элементов (общее время загрузки составило 3 секунды).
В этих условиях стандартный компонент TreeView показал общее время 129.5 секунд.
Очистка компонента осуществлялась вызовом функции
SendMessage(hwnd, TVM_DELETEITEM, 0, Longint(TVI_ROOT)).
*)
interface
uses SysUtils, Windows, Messages, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, CommCtrl, tree2vw;
type THETreeView = class(TTreeView)