Валентин Озеров - Советы по Delphi. Версия 1.0.6
inherited Destroy;
end;
procedure TIDSLabel.Paint;
var
wXPos, wYPos : Word;
begin
{Рисуем рамку}
inherited Paint;
{Назначаем шрифт}
Canvas.Font.Assign(Font);
{Вычисляем вертикальную позицию}
wYPos:= (Height – Canvas.TextHeight(Caption)) div 2;
{Вычисляем горизонтальную позицию}
wXPos:= Offset;
case alignment of
taRightJustify: wXPos:= Width – Canvas.TextWidth(Caption) – Offset;
taCenter: wXPos := (Width – Canvas.TextWidth(Caption)) div 2;
end;
Canvas.Brush:= Parent.Brush;
Canvas.TextOut(wXPos,wYPos,Caption);
end;
procedure TIDSLabel.SetAlignment;
begin
FAlignment:= taIn;
Invalidate;
end;
procedure TIDSLabel.SetCaption;
begin
FCaption:= strIn;
if Assigned(FOnChange) then FOnChange(Self);
Invalidate;
end;
procedure TIDSLabel.SetFont;
begin
FFont.Assign(fntNew);
Invalidate;
end;
procedure TIDSLabel.SetOffset;
begin
FOffset:= bOffNew;
Invalidate;
end;
end.
ScrollBox
Синхронизация двух компонентов Scrollbox
Решить задачу помогут обработчики событий OnScroll (в данном примере два компонента ScrollBox (ScrollBar1 и ScrollBar2) расположены на форме TMainForm):
procedure TMainForm.ScrollBar1Scroll(Sender: TObject; ScrollCode: TScrollCode; var ScrollPos: Integer);
begin
ScrollBar2.Position:= ScrollPos;
end;
procedure TMainForm.ScrollBar1Scroll(Sender: TObject; ScrollCode: TScrollCode; var ScrollPos: Integer);
begin
ScrollBar1.Position:= ScrollPos;
end;
Splitter
Конструирование Splitter
У меня есть форма с расположенными на ней компонентами TreeView и Memo. Значение свойства align обоих компонентов позволяет им занимать всю форму. Я хотел бы расположить между ними движок типа Splitter, пропорционально меняющий их размеры (один шире, другой меньше и наоборот), но к сожалению я обладаю лишь дистрибутивом Delphi2 (Splitter вошел в палитру только в Delphi3). Какой компонент мог бы сымитировать поведение Splitter и как это реализовать?
Предположим, Ваш TreeView расположен в левой, а Memo в правой части формы. Вам нужно сделать следующее:
• Установите свойство Align компонента TreeView на alLeft.
• Вырежьте (Ctrl-X) компонент TMemo из вашей формы.
• Добавьте компонент Panel и присвойте его свойству Align значение alClient.
• Внутри панели разместите другой компонент Panel.
• Установите его ширину, равной 8 пикселам, свойству Align присвойте значение alLeft.
• Скопируйте вырезанный компонент TMemo в панель Panel1 и присвойте свойству Align значение alClient.
Panel2 – движок: теперь вам необходимо добавить процедуры, приведенные ниже. Ваш код будет выглядеть приблизительно так:
type TForm1 = class(tform)
TreeView1: TTreeview;
Panel1: TPanel;
Panel2: TPanel;
Memo1: TMemo;
procedure Panel1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure Panel1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure Panel1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
private
Resizing: Boolean;
public
…
end;
procedure TForm1.Panel2MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
Resizing:=true;
end;
procedure TForm1.Panel2MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
Resizing:= false;
end;
procedure TForm1.Panel2MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
if Resizing then begin
TreeView1.Width:=TreeView1.Width+X;
// Предохранение от странных ошибок перерисовки при изменении размеров:
Panel1.Invalidate;
end;
end;
Код может быть модифицирован для получения горизонтального движка – идея, надеюсь, понятна…
StatusBar
Обработчик события OwnerDraw в компоненте StatusBar
Обработчик должен выглядеть примерно так:
procedure TForm1.StatusBar1DrawPanel(StatusBar: TStatusBar; Panel: TStatusPanel; const Rect: TRect);
begin
with statusbar1.Canvas do begin
Brush.Color:= clRed;
FillRect(Rect);
TextOut(Rect.Left, Rect.Top, 'Панель '+IntToStr(Panel.Index));
end;
end;
StringGrid
Установка атрибутов –=Только для чтения=– у столбцов компонента StringGrid
Манипулирование вышеуказанным атрибутом возможно в обработчике события OnSelectCell:
if Col mod 2 = 0 then grd.Options:= grd.Options + [goEditing]
else grd.Options:= grd.Options – [goEditing];
Помещение изображения в ячейку StringGrid
Возможно ли поместить изображение в одну из ячеек компонента StringGrid?
Такое позволяет обработчик события OnDrawCell. Приводим скелет кода, демонстрирующий принцип вывода изображения в ячейке компонента:
with StringGrid1.Canvas do begin
{…}
Draw(Rect.Left, Rect.Top, Image1.Picture.Graphic);
{…}
end;
Достичь цели позволяют методы Draw() и StretchDraw() объекта TCanvas. В приведенном примере переменная Image1 класса TImage содержит заранее загруженное изображение.
Сохранение и чтение Tstringgrid
Как мне сохранить целый Stringgrid со всеми ячейками в файле?
Procedure SaveGrid;
var f:textfile;
x,y: integer;
begin
assignfile(f,'Filename');
rewrite(f);
writeln(f,stringgrid.colcount);
writeln(f,stringgrid.rowcount);
For x:= 0 to stringgrid.colcount-1 do For y:= 0 to stringgrid.rowcount-1 do writeln(F, stringgrid.cells[x,y]);
closefile(f);
end;
Procedure LoadGrid;
var f:textfile;
temp,x,y:integer;
tempstr:string;
begin
assignfile(f,'Filename');
reset(f);
readln(f,temp);
stringgrid.colcount:= temp;
readln(f,temp);
stringgrid.rowcount:= temp;
For x:=0 to stringgrid.colcount-1 do For y:=0 to stringgrid.rowcount-1 do begin
readln(F, tempstr);
stringgrid.cells[x,y]:= tempstr;
end;
closefile(f);
end;
TabbedNotebook
Добавление элементов управления в TTabbedNotebook и TNotebook
Я несколько раз видел в конференциях вопросы типа "как мне добавить элементы управления в TTabbedNotebook или TNotebook во время выполнения программы?". Теперь, когда у меня выдалось несколько свободных минут, я попытаюсь осветить этот вопрос как можно подробнее:
TTabbedNotebookДобавление элементов управления в TTabbedNotebook во время проектирования – красивая и простая задача. Все, что Вам нужно – это установить свойство PageIndex или ActivePage на необходимую страницу и начать заполнять ее элементами управления.
Добавление элементов управление во время выполнения приложения также очень просто. Тем не менее, в прилагаемой документации по Delphi вы не найдете рецептов типа Что-и-Как. Видимо для того, чтобы окончательно запутать начинающих программистов, фирма-изготовитель даже не удосужилась включить исходный код TTabbedNotebook в VCL-библиотеку. Таким образом, TTabbedNotebook остается для некоторых тайной за семью печатями. К счастью, я имею некоторый опыт, коим и хочу поделиться.
Первым шагом к раскрытию тайны послужит просмотр файла DELPHIDOCTABNOTBK.INT, интерфейсной секции модуля TABNOTBK.PAS, в котором определен класс TTabbedNotebook. Беглый просмотр позволяет обнаружить класс TTabPage, описанный как хранилище элементов управления отдельной страницы TTabbedNotebook.
Вторым шагом в исследовании TTabbedNotebook может стать факт наличия свойством Pages типа TStrings. В связи с этим отметим, что Delphi-классы TStrings и TStringList соорганизуются с двумя свойствами: Strings и Objects. Другими словами, для каждой строки в TStrings есть указатель на соответствующий Objects. Во многих случаях этот дополнительный указатель игнорируется, нам же он очень пригодится.
После небольшого эксперимента выясняем, что свойство Objects указывает на нашу копию TTabPage и ссылается на имя страницы в свойстве Strings. Блестяще! Всегда полезно знать что ищешь. Теперь посмотрим что мы можем сделать:
{ Данная процедура добавляет кнопку в случайной позиции на }
{ текущей странице данного TTabbedNotebook. }
procedure AddButton(tabNotebook : TTabbedNotebook);
var
tabpage: TTabPage;
button: TButton;
begin
with tabNotebook do tabpage:= TTabPage(Pages.Objects[PageIndex]);
button:= TButton.Create(tabpage);
try
with button do begin
Parent:= tabpage;
Left:= Random(tabpage.ClientWidth – Width);
Top:= Random(tabpage.ClientHeight – Height);
end;
except
button.Free;
end;
end;