Отправляет email-рассылки с помощью сервиса Sendsay

Программирование на Delphi

  Все выпуски  

Программирование на Delphi #21


Информационный Канал Subscribe.Ru

Программирование на Delphi. Выпуск №21: 20.02.05.


Добрый день , уважаемые читатели!

Начну с того, что уже давно хотел сказать. Почему никто не посещает форум? Уже порядка двух недель ни одного нового сообщения и ни одной новой темы. Хотелось бы узнать, почему так? Жду ваших писем. Не стесняйтесь и говорите, что вам конкретно не нравится... У меня одно подозрение, но хотелось бы услышать именно вас.
Также скажу всем, кто ещё не в курсе (об этом я сообщал на форуме, но поскольку вы туда не ходите, сообщу здесь) - готовится новый вариант сайта, в котором полностью всё изменено, начиная от дизайна и заканчивая структурой расположения файлов на сервере. Пока точных сроков дать не могу, но планирую до конца февраля всё успеть.
Также извиняюсь, что совсем забыл про новый конкурс... Почему-то из головы вылетело и всё :( Итак, вот он...


Предлагаю вам принять участие вот в таком конкурсе. Ниже вы видите 10 вопросов, на каждый из которых есть варианты ответов. Ответьте на все вопросы, просто указывая букву(ы) и пришлите их по ЭТОЙ ссылке. Внимание! Письмо должно соответствовать следующему образцу:

--------------------------------------------------------

1-A
2-B
3-C
...
10-C,D
===
Имя, Фамилия
E-mail

--------------------------------------------------------

Строки "--------------------------------------------------------" - просто разделители. Таким образом, письмо не должно иметь вступления (приветствия). Сразу должны идти ответы на вопросы: номер вопроса-ответ. Пробелов перед и после "-", а также после "," между двумя вариантами ответа (10-й вопрос) быть не должно. Строка "===" является разделителем между ответами и информацией о пользователе и является обязательной. Рекомендую вам просто скопировать текст из поля ниже и вставить его полностью в текст письма:

Не забудьте изменить варианты ответа на правильные :))

Хочу отдельно сказать, почему такой строгий шаблон писем: обрабатывать эти письма буду не я, а специально-написанная мною программа (на Delphi конечно :) ), поэтому соблюдайте все вышеописанные правила. А вот и сами вопросы:

1. Разработчиком Delphi является:

A. Microsoft.
B. Borland.
C. Discreet.
D. Norton.

2. На Delphi можно писать...

A. Всё, что угодно.
B. Только приложения, работающие с БД.
C. Только web-приложения.
D. Только драйвера.
E. Всё, кроме БД-приложений.
F. Всё, кроме Web-приложений.
G. Всё, кроме драйверов.

3. Среда Delphi разработана в Delphi:

A. Да
B. Нет

4. Программу можно скомпилировать, даже если в коде есть ошибки, но тогда эти ошибки проявятся при выполнении программы...

A. Да
B. Нет

5. На Delphi можно создавать приложения для Linux:

A. Нет
B. Да

6. Есть в Delphi поддержка RTL-библиотек (RunTime Libraries), как в Visual Basic?

A. Есть
B. Нет

7. Выполнение кода:

var List: TStrings;
i: integer;
...
for i:=0 to List.Count do
Memo1.Lines.Add(List[i]);

приведёт к:

A. Выводу всех строк List в Memo1.
B. Очистке List.
C. Ошибке.
D. Закрытию программы.

8. Какой из перечисленных типов включает в себя наибольший диапазон значений?

A. Single.
B. Double.
C. Extended.

9. Можно ли запустить программу из Delphi с командными параметрами или только через Windows?

A. Можно.
B. Нельзя.

10. Укажите ДВЕ известные программы, написанные на Delphi:

A. Microsoft Word.
B. The Bat.
C. Windows Commander.
D. 3D Studio MAX.

За каждый правильный ответ вы получите 2 балла. Таким образом, ответив правильно на все вопросы, вы можете получить 20 баллов. Имена лучших будут опубликованы.

Также поступающие вопросы и ответы начиная с текущего времени и до обновления на сайте публиковаться НЕ БУДУТ по некоторым причинам. И вообще, сайт будет всё это время обновляться крайне мало. Учтите это. Но это вовсе не значит, что не работает и форум! Наоборот, он ждёт всех вас!

Чуть не забыл... Когда вам начисляются баллы за ответы, то ваша "учётка" привязана к вашему e-mail. Поэтому, пожалуйста, все письма в ящик нашего проекта шлите с одного мыла, иначе счёт может обнулиться.

Количество подписчиков: XXXX.

Не забывайте посещать наш форум.

Top-10 Readers:

Место
Имя
Кол-во баллов
Место
Имя
Кол-во баллов
1.
167 баллов
6.
50 баллов
2.
140 баллов
7.
43 балла
3.
137 баллов
8.

Den

36 баллов
4.
127 баллов
9.
35 баллов
5.
67 баллов
10.
34 балла

Правила нашей рассылки:
1. Не присылайте ответов на вопросы вроде "да я не знаю" или "да/нет". Такие ответы не публикуются.
2. Вопросы, не касающиеся Delphi, не принимаются (для этого существуют другие рассылки).
3. Запрещено присылать вложенные файлы, размером более 100 Кб, без предварительной связи с администратором.
4. Не изменяйте тем присылаемых писем. Письма с "неправильными" темами не публикуются! Используйте текстовый формат писем.


Новые вопросы.

74. У меня такой вопрос, бьюсь над ним уже две недели: вывод суммарной температуры за день и за месяц. Как в StringGrid сделать подсчет, чтобы было так: В первый столбец вводится дата, во второй температура - в третьем столбце выводится суммарная температура за день, в четвертом суммарная температура за месяц:
| 02.01.2005 | 25 | 71 | 241 |
| 02.01.2005 | 24 | 71 | 241 |
| 02.01.2005 | 22 | 71 | 241 |
| 03.01.2005 | 27 | 53 | 241 |
| 03.01.2005 | 26 | 53 | 241 |
| 04.01.2005 | 21 | 21 | 241 |
| 05.01.2005 | 23 | 96 | 241 |
| 05.01.2005 | 22 | 96 | 241 |
| 05.01.2005 | 26 | 96 | 241 |
| 05.01.2005 | 25 | 96 | 241 |
| 12.02.2005 | 22 | 46 | 216 |
| 12.02.2005 | 24 | 46 | 216 |
| 13.02.2005 | 22 | 72 | 216 |
| 13.02.2005 | 24 | 72 | 216 |
| 13.02.2005 | 26 | 72 | 216 |
| 14.02.2005 | 25 | 44 | 216 |
| 14.02.2005 | 19 | 44 | 216 |
| 15.02.2005 | 18 | 54 | 216 |
| 15.02.2005 | 19 | 54 | 216 |
| 15.02.2005 | 17 | 54 | 216 | [Ответить].

75. Как убрать мою программу из диспетчера задач WINDOWS? [Ответить].

76. Народ подскажите как получить список IP адресов компьютера без WinSock? [Ответить].

77. У меня проблема.
Пишу так:

Var reg:TRegistry;
. . .
reg:=TRegistry.Create;
If reg.OpenKey(‘Program’,true) then …

Здесь все работает. Подскажите, пожалуйста, почему выдает ошибку следующее:

Var reg:Tregistry;
. . .
reg:=TRegistry.Create;
reg.RootKey:=HKEY_LOCAL_MACHINE;
If reg.OpenKey(‘Program’,true) then …

[Ответить].



Ответы на вопросы.

"Огромное спасибо за помощь Iron Monkу ! YaricZ (YaricZ@mail.ru)" - я так думаю, это за ответ на 60-ый вопрос.

71. (Отправка SMS). [Отвечает: Igor]: Отправить СМС можно, например, с помощью e-mail, если это поддерживает мобильный оператор. Нужно отправлять e-mail на спец. адрес, в который входит номер телефона, что-то типа 380670000000@2sms.kyivstar.net для абонентов Киевстар. Позвоните своему оператору и узнайте, как с простого e-mail послать СМС на телефон.

[Отвечает: Садовников Владимир]: Есть хорошая программа для посылки смс - SMSSender. Но! Теперь крупные компании (типа МегаФона) стали использовать такие методы, как визуальное подтверждение кода (т.е. ты должен распознать число, нарисованное на картинке). Так что такой трюк уже не катит... Так как: программы делают следующим образом: соединяются с сервером посылки смс для данного оператора, после чего выполняют ту же работу, что и твой броузер - передают скриптам на странице определённые параметры (типа текста сообщения, номера получателя и т.д.). Когда возвращается удачный результат, соединение разрывается (чтобы понизить трафик). Вот и весь, вроде, принцип их работы - без оператора не покатит, универсальности здесь никакой, и надо подстраиваться под сети и сервисы тех операторов, которые тебе нужны.

[Отвечает: Ершов Денис]: Отправка SMS через интернет в основном производится через e-mail или http. Для этого существуют специальные сервера сотовых операторов или специальные бесплатные службы, например, Infan.ru.

С почтовыми сервисами все проще, необходим всего лишь почтовый клиент, отправляющий SMS в виде текста письма, но я о подобных сервисах только слышал. HTTP более популярная вещь, хотя и тоже крайне редкая. В этом случае нужно либо вежливо связаться с сапортом службы (может они дадут формат запроса), либо отправить для примера одну SMS с помощью браузера и с помощью анализаторов трафика самому его подсмотреть. Далее работа с http-клиентом.

[Отвечает: Dron]: Все SMS отправляются обычным e-mail сообщением, только для каждого оператора они разные. Иногда ставят защиту вроде "введите код, который вы видите на картинке". Написать универсальную программу практически невозможно.

69. (Реализация шахмат). [Отвечает: Садовников Владимир]: Вот тебе пример модуля программы. Здесь есть только передвижение. Ни о каких поеданиях и речи быть пока не может (хотя исправить эту ситуацию несложно) - просто лениво было уже писать. Я думаю, ты и сам справишься. Думаю, что концепция ООП тебе понятна с наследующими классами. Кстати, в TQueen.Move я сжульничал - лень было код переписывать. Дерзай, может что-то вроде ChessMaster 9000 смастеришь или лучше :).

unit Chess;

interface

const
COORDX_A = 0;
COORDX_B = 1;
COORDX_C = 2;
COORDX_D = 3;
COORDX_E = 4;
COORDX_F = 5;
COORDX_G = 6;
COORDX_H = 7;

COORDY_1 = 0;
COORDY_2 = 1;
COORDY_3 = 2;
COORDY_4 = 3;
COORDY_5 = 4;
COORDY_6 = 5;
COORDY_7 = 6;
COORDY_8 = 7;

COLOR_WHITE = 0;
COLOR_BLACK = 1;

type

TFigure=class;

TChessBoard=class
public
constructor Create;
destructor Destroy;override;

function IsFreeCell(X,Y:Integer):Boolean;
private
BlackFigures:array[0..15] of TFigure;
WhiteFigures:array[0..15] of TFigure;
function GetBlack(Index: Integer): TFigure;
function GetWhite(Index: Integer): TFigure;
public
property White[Index:Integer]:TFigure read GetWhite;
property Black[Index:Integer]:TFigure read GetBlack;
end;

TFigure=class
private
XPos,YPos:Integer;
Board:TChessBoard;
Col:Integer;
public
constructor Create(ChessBoard:TChessBoard;Color:Integer;PosX,PosY:Integer);
destructor Destroy;override;

function Move(NewX,NewY:Integer):Boolean;virtual;

property GetX:Integer read XPos;
property GetY:Integer read YPos;
end;

THorse=class(TFigure)
public
function Move(NewX,NewY:Integer):Boolean;override;
end;

TElefant=class(TFigure)
public
function Move(NewX,NewY:Integer):Boolean;override;
end;

TCastle=class(TFigure)
public
function Move(NewX,NewY:Integer):Boolean;override;
end;

TKing=class(TFigure)
public
function Move(NewX,NewY:Integer):Boolean;override;
end;

TQueen=class(TFigure)
public
function Move(NewX,NewY:Integer):Boolean;override;
end;

TPawn=class(TFigure)
public
function Move(NewX,NewY:Integer):Boolean;override;
end;

implementation

{ TChessBoard }

constructor TChessBoard.Create;
var
I:Integer;
begin
for I:=0 to 7 do
WhiteFigures[I]:=TPawn.Create(Self,COLOR_WHITE,I,COORDY_2);
WhiteFigures[8]:=TCastle.Create(Self,COLOR_WHITE,COORDX_A,COORDY_1);
WhiteFigures[9]:=THorse.Create(Self,COLOR_WHITE,COORDX_B,COORDY_1);
WhiteFigures[10]:=TElefant.Create(Self,COLOR_WHITE,COORDX_C,COORDY_1);
WhiteFigures[11]:=TQueen.Create(Self,COLOR_WHITE,COORDX_D,COORDY_1);
WhiteFigures[12]:=TKing.Create(Self,COLOR_WHITE,COORDX_E,COORDY_1);
WhiteFigures[13]:=TElefant.Create(Self,COLOR_WHITE,COORDX_F,COORDY_1);
WhiteFigures[14]:=THorse.Create(Self,COLOR_WHITE,COORDX_G,COORDY_1);
WhiteFigures[15]:=TCastle.Create(Self,COLOR_WHITE,COORDX_H,COORDY_1);

for I:=0 to 7 do
BlackFigures[I]:=TPawn.Create(Self,COLOR_BLACK,I,6);
BlackFigures[8]:=TCastle.Create(Self,COLOR_BLACK,COORDX_A,COORDY_8);
BlackFigures[9]:=THorse.Create(Self,COLOR_BLACK,COORDX_B,COORDY_8);
BlackFigures[10]:=TElefant.Create(Self,COLOR_BLACK,COORDX_C,COORDY_8);
BlackFigures[11]:=TQueen.Create(Self,COLOR_BLACK,COORDX_D,COORDY_8);
BlackFigures[12]:=TKing.Create(Self,COLOR_BLACK,COORDX_E,COORDY_8);
BlackFigures[13]:=TElefant.Create(Self,COLOR_BLACK,COORDX_F,COORDY_8);
BlackFigures[14]:=THorse.Create(Self,COLOR_BLACK,COORDX_G,COORDY_8);
BlackFigures[15]:=TCastle.Create(Self,COLOR_BLACK,COORDX_H,COORDY_8);
end;

destructor TChessBoard.Destroy;
var
I:Integer;
begin
for I:=0 to 15 do
begin
BlackFigures[I].Destroy;
WhiteFigures[I].Destroy;
end;
inherited Destroy;
end;

function TChessBoard.GetBlack(Index: Integer): TFigure;
begin
Result:=BlackFigures[Index];
end;

function TChessBoard.GetWhite(Index: Integer): TFigure;
begin
Result:=WhiteFigures[Index];
end;

function TChessBoard.IsFreeCell(X, Y: Integer): Boolean;
var
I:Integer;
begin
Result:=True;
if (X<0) or (X>7) then
Result:=False;
if (Y<0) or (Y>7) then
Result:=False;

if (Result) then
for I:=0 to 15 do
begin
if (BlackFigures[I].GetX=X) and (BlackFigures[I].GetY=Y) then
begin
Result:=False;
Exit;
end;
if (WhiteFigures[I].GetX=X) and (WhiteFigures[I].GetY=Y) then
begin
Result:=False;
Exit;
end;
end;
end;

{ TFigure }

constructor TFigure.Create(ChessBoard: TChessBoard; Color:Integer; PosX, PosY: Integer);
begin
Board:=ChessBoard;
XPos:=PosX;
YPos:=PosY;
end;

destructor TFigure.Destroy;
begin
inherited Destroy;
end;

function TFigure.Move(NewX, NewY: Integer): Boolean;
begin
Result:=True;
if (NewX<0) or (NewX>7) then
Result:=False;
if (NewY<0) or (NewY>7) then
Result:=False;
if (NewX=XPos) and (NewY=YPos) then
Result:=False;
end;

{ THorse }

function THorse.Move(NewX, NewY: Integer): Boolean;
var
DeltaX,DeltaY:Integer;
begin
Result:=inherited Move(NewX,NewY);
if (Result) then
begin
DeltaX:=NewX-XPos;
DeltaY:=NewY-YPos;

Result:=(Abs(DeltaX)=2) or (Abs(DeltaX)=1);
if (not Result) then
Exit;
Result:=(Abs(DeltaY)=2) or (Abs(DeltaY)=1);
if (not Result) then
Exit;
Result:=Abs(DeltaX)<>Abs(DeltaY);
if (not Result) then
Exit;
Result:=Board.IsFreeCell(NewX,NewY);
if (not Result) then
Exit;

XPos:=NewX;
YPos:=NewY;
end;
end;

{ TElefant }

function TElefant.Move(NewX, NewY: Integer): Boolean;
var
DeltaX,DeltaY:Integer;
XIncrement:Integer;
YIncrement:Integer;
XX,YY:Integer;
begin
Result:=inherited Move(NewX,NewY);
if (Result) then
begin
DeltaX:=NewX-XPos;
DeltaY:=NewY-YPos;

Result:=Abs(DeltaX)=Abs(DeltaY);
if (not Result) then
Exit;

if DeltaX>0 then
XIncrement:=-1
else
XIncrement:=1;
if DeltaY>0 then
YIncrement:=-1
else
YIncrement:=1;

XX:=NewX;
YY:=NewY;
while (XX<>XPos) do
begin
Result:=Board.IsFreeCell(XX,YY);
if (not Result) then
Exit;
XX:=XX+XIncrement;
YY:=YY+YIncrement;
end;

XPos:=NewX;
YPos:=NewY;
end;
end;

{ TCastle }

function TCastle.Move(NewX, NewY: Integer): Boolean;
var
DeltaX,DeltaY:Integer;
XIncrement:Integer;
YIncrement:Integer;
XX,YY:Integer;
begin
Result:=inherited Move(NewX,NewY);
if (Result) then
begin
DeltaX:=NewX-XPos;
DeltaY:=NewY-YPos;

Result:=(DeltaX=0) or (DeltaY=0);
if (not Result) then
Exit;

if (DeltaX<>0) then
begin
if DeltaX>0 then
XIncrement:=-1
else
XIncrement:=1;
YIncrement:=0;
end
else
begin
if DeltaY>0 then
YIncrement:=-1
else
YIncrement:=1;
XIncrement:=0;
end;

XX:=NewX;
YY:=NewY;
while (XX<>XPos) and (YY<>YPos) do
begin
Result:=Board.IsFreeCell(XX,YY);
if (not Result) then
Exit;
XX:=XX+XIncrement;
YY:=YY+YIncrement;
end;

XPos:=NewX;
YPos:=NewY;
end;
end;

{ TKing }

function TKing.Move(NewX, NewY: Integer): Boolean;
var
DeltaX,DeltaY:Integer;
begin
Result:=inherited Move(NewX,NewY);
if (Result) then
begin
DeltaX:=NewX-XPos;
DeltaY:=NewY-YPos;

Result:=(Abs(DeltaX)<2) and (Abs(DeltaY)<2);
if (not Result) then
Exit;

Result:=Board.IsFreeCell(NewX,NewY);
if (not Result) then
Exit;

XPos:=NewX;
YPos:=NewY;
end;
end;

{ TQueen }

function TQueen.Move(NewX, NewY: Integer): Boolean;
var
DeltaX,DeltaY:Integer;
Figure:TFigure;
begin
DeltaX:=NewX-XPos;
DeltaY:=NewY-YPos;

if (DeltaX=0) or (DeltaY=0) then
Figure:=TCastle.Create(Board,Col,XPos,YPos)
else
Figure:=TElefant.Create(Board,Col,XPos,YPos);
Result:=Figure.Move(NewX,NewY);
if (Result) then
begin
XPos:=Figure.GetX;
YPos:=Figure.GetY;
end;
Figure.Destroy;
end;

{ TPawn }

function TPawn.Move(NewX, NewY: Integer): Boolean;
var
DeltaX,DeltaY:Integer;
Limit:Integer;
YIncrement:Integer;
YY:Integer;
begin
Result:=inherited Move(NewX,NewY);
if (Result) then
begin
DeltaX:=NewX-XPos;
DeltaY:=NewY-YPos;

if (Col=COLOR_WHITE) then
Limit:=1+Ord(YPos=1)
else
Limit:=1+Ord(YPos=6);

Result:=(Abs(DeltaY)<Limit) and (DeltaX=0);
if (not Result) then
Exit;

YY:=NewX;
if (DeltaY>0) then
YIncrement:=-1
else
YIncrement:=1;

while (YY<>YPos) do
begin
Result:=Board.IsFreeCell(NewX,YY);
if (not Result) then
Exit;
YY:=YY+YIncrement;
end;

XPos:=NewX;
YPos:=NewY;
end;
end;

end.

[Отвечает: Iron Monk]: Всем привет! Это шахматная доска:

var
Form1: TForm1;
i:integer=30;// размер клетки
step:integer;
implementation

{$R *.dfm}

procedure TForm1.CreatePole;
var
c, d:integer;
begin
Canvas.Brush.Color := clBlack;
c:=0;
d:=1;
//---
for step:=1 to 4 do
begin
Canvas.Rectangle(i,i+i*c,i*2,i*2*d);
Canvas.Rectangle(i*3,i+i*c,i*4,i*2*d);
Canvas.Rectangle(i*5,i+i*c,i*6,i*2*d);
Canvas.Rectangle(i*7,i+i*c,i*8,i*2*d);
//---
Canvas.Rectangle(i*2,i*2*d,i*3,i*3+i*c);
Canvas.Rectangle(i*4,i*2*d,i*5,i*3+i*c);
Canvas.Rectangle(i*6,i*2*d,i*7,i*3+i*c);
Canvas.Rectangle(i*8,i*2*d,i*9,i*3+i*c);
c:=c+2;
Inc(d);
//---
end;
end;

//Создаём шахматное поле
procedure TForm1.Button1Click(Sender: TObject);
begin
for step:=1 to 9 do
begin
Image1.Canvas.MoveTo(i*step,i);
Image1.Canvas.LineTo(i*step,i*9);
Image1.Canvas.MoveTo(i,i*step);
Image1.Canvas.LineTo(i*9,i*step);
end;
Application.ProcessMessages;
CreatePole;
end;
end.

А фигуры перемещать можно многими способами, от программного задания координат, до перетаскивания мышью, главное не забывать после каждой такой операции перерисовывать поле. Если в этом есть сложность, то проще использовать графическую доску TBitmap или TJPEGImage.

[Отвечает: Сергей Загородних]: Используй класс TStringGrid, его методы и события, а также приёмы drag&drop.

[Отвечает: alexlazer]: Здесь пример шахмат на Delphi. Как они устроены, не знаю, но можешь спросить у автора - Василенко И.П., i_vasilenco@yahoo.com. [Шахматы лежат здесь (238 Кб)].

[Отвечает: Dron]: Думаю, поле нарисовать совсем просто. Через Canvas, например. Затем нарисуй фигуры. Можешь их также рисовать на Canvas, но тогда придётся часто перерисовывать. Лично я бы использовал DelphiX. Каждая фигура - отдельный спрайт. Тогда вообще всё просто. А ещё круче - с помощью OpenGL сделать 3D-шахматы. В природе таких очень мало, но придётся самому писать AI для компьютера, а это непосильно одному человеку. Либо можно попробовать найти "готовую" логику.

35. (База данных в каталоге программы). [Отвечает: pirog]: Думаю эту проблему можно решить так: В инспекторе для Table1 ставим DatabaseName - ..\тут-Name\тут-baza. И делаем защищенный блок:

procedure TForm1.FormCreate(Sender: TObject); {создание формы}
begin
Try //начало защиты блока
Table1.Active:= True; {соединение с базой данных }
Except //Конец защиты блока
ShowMessage('Вы переименовали один из'+#13+
' каталогов программы'+#13+' ЭТО НЕ ДОПУСТИМО!');
end;
end;

73. (Добавление компонентов на форму во время работы приложения). [Отвечает: Igor]: Да, можно. Вот пример добавления на форму кнопок во время работы приложения.

procedure TForm1.Button1Click(Sender: TObject);
var NewButton:TButton; LastTop:integer;
begin
NewButton := TButton.Create(Application);
NewButton.Parent := Self; //обязательно укажите для вновь созданного компонента его родителя.
NewButton.Left := 15; //и задайте ему необходимые свойства.
NewButton.Top := LastTop;
LastTop := LastTop + 25;
end;

[Отвечает: Iron Monk]: Можно, можно даже создавать нестандартные решения.
В данном примере при нажатии кнопки програмно создаётся TButton и расположенный на нём TSpinEdit:

uses Spin ;
var
Btn:TButton;
Spin:TSpinEdit;

procedure TForm1.Button1Click(Sender: TObject);
begin
Btn:=TButton.Create(Nil);
Btn.Parent:=Form1;
Btn.Left:=30;
Btn.Top:=50;
Btn.Height:=50;
Btn.Width:=200;
Btn.Caption:='Выбор номера';
//---
Spin:=TSpinEdit.Create(Nil);
Spin.Parent:=Btn;
Spin.Left:=20;
Spin.Top:=15;
end;
end.

[Отвечает: Сергей Загородних]: Если ты имешь в виду создание класса и обращение к нему в дальнейшем, то:

1. Нужно объявить переменную данного класса(который ты хочешь создать) в разделе объявления переменных(локально или глобально решать тебе в зависимости будешь ли ты с других процедур или функций обращаться к созданному тобой классу).

2. В теле метода(процедуры или ф-ции) создать класс через его конструктор(чаще всего имя метода констуктора называется Create).

3. Указать для класса его владельца(owner) или поставить nil и родителя(parernt)-[если класс визуальный] - о свойствах owner и parent для классов - можно прочитать в help по delphi.

4. Следить за разрушением класса(освобождением памяти, которую он занимает) - когда класс не используется и тебе не надо к нему обращаться.

5. Класс, который ты хочешь испоьзовать должен быть объявлен либо в твоём модуле, либо подключить в раздел uses модуль, в котором описан класс, который ты хочешь использовать [в дезайн-time'e При добавлении визуального компонента на форму delphi ide сам добавляет в uses модуль, в котором описан данный класс, добавляет переменную (объект) данного класса, к примеру caption1 : TCaption, а в .dfm-файле описывает св-ва для для добавленного объекта].

[Пример] - программно создать Label на button1.OnClick:
(описане класса Tlabel находится в модуле StdCtrls)

procedure Tform1.Button1Click(Sender: TObject);
var myLabel : TLabel;
begin
myCapt : TCaption.Create(Self); // owner = Tform1
with myLabel do
begin
parent := form1;
caption := 'Метка создана';
left := 40; // разместить caption на форме
top := 40;
end;
end;

[Отвечает: VeroLom]: Вот пример добавления кнопки:

type
TForm1 = class(TForm)
{...}
procedure FormCreate(Sender: TObject);
private
Procedure MyButtonOnClickHandler(Sender: TObject);
end;

Implementation

procedure TForm1.MyButtonOnClickHandler(Sender: TObject);
begin
ShowMessage('Voila!');
end;

procedure TForm1.FormCreate(Sender: TObject);
Var
MyButton: TButton;
begin
MyButton := TButton.Create(Self); //Создание кнопки. Form1 - Owner т.е. кнопка уничтожится при закрытии формы.
MyButton.Parent := Self;
MyButton.Caption := 'Click Me!';
MyButton.OnClick := MyButtonOnClickHandler; //обработчик нажатия на кнопку
end;

[Отвечает: Четвертных В.В.]: В принципе, это очень легко. компоненты, потомки TObject (А это - почти все стандартные компоненты Delphi) имеют конструктор Create. После создания компонента, если он визуальный, нужно указать его положение свойства: Top, Left, Width и Height (а можно использовать метод SetBounds(Top, Left, Width, Height:integer)), при необходимости делаем свойство Visible:=true. А вот теперь, можно делать с ним что хочешь...

[Отвечает: alexlazer]: Если требуется, скажем, создать программно 5 кнопок. Очень просто. Помещаешь на форму тот объект, который надо программно создать (например, кнопку Button). В глобальном разделе var пишешь:

var
Form1: TForm1;
p: array[1..5] of TButton;

Щелкаешь по кнопке Button на форме, создаёшь, например, процедуру OnClick:
procedure TForm1.Button1Click(Sender: TObject);
(для этого эта кнопка, собственно, и помещается на форму, а также чтобы при компиляции программы она нашла класс TButton). Пишешь, что кнопка должна делать по щелчку мыши.

Создаёшь двойным щелчком мыши по форме процедуру FormCreate:

procedure TForm1.FormCreate(Sender: TObject);
var i:integer; Size: integer;
begin
Size:=50;
for i:= 1 to 5 do begin
P[i]:= TButton.Create(Self);
P[i].Left :=i*(Size+10);
P[i].Top := Size;
P[i].Width := Size;
P[i].Height := Size;
P[i].Parent := Self;
p[i].Tag:=i; {команда не обязательна}
P[i].Font.Size:=30; {команда не обязательна}
P[i].Caption:=inttostr(p[i].Tag); {команда не обязательна}
P[i].OnClick := Button1Click;
end;
end;

Компилируешь программу, удаляешь с формы первоначальную кнопку и запускаешь. Если требуется только одна кнопка, то:

procedure TForm1.FormCreate(Sender: TObject);
var i:integer; Size: integer;
begin
Size:=50;
i:=1;
P[i]:= TButton.Create(Self);
P[i].Left :=i*(Size+10);
P[i].Top := Size;
P[i].Width := Size;
P[i].Height := Size;
P[i].Parent := Self;
P[i].OnClick := Button1Click;
end;

Вариации по вкусу.

[Отвечает: Андрей Лучников]: Можно, причем легко:

{например TLabel на форме Form1}

with TLabel.Create(Form1) do
Begin
Left:=1;
Top:=1;
Caption:='Новая TLabel';
{ну и так далее, можно еще прикрутить обработчики}
onClick:=label1Click; {обработчик от Label1}


{а теперь ее прикрутим к форме}
Parent:=Form1;

end;

[Отвечает: Dron]: Конечно можно! Вот простой пример создания кнопки:

Var B: TButton;
Begin
B:=TButton.Create(Form1);
B.Parent:=Form1;
B.Left:=100;
B.Top:=100;
B.Width:=100;
B.Height:=50;
B.Caption:='Exit';
B.OnClick=Form1Close;
End;

65. (События CM_MouseWheel). [Отвечает: Ершов Денис]: Не согласен с утверждением Ramon'а о бесполезности параметров MousePos: TPoint - координаты курсора, Handled: Boolean и Shift: TShiftState. С помощью ShiftState можно задать альтернативное действие. Например, в MS Visio удобно сделано: при зажатом Shift экран скроллируется по горизонтали, при Ctrl - масштабирование экрана.

70. (Работа с цветами изображения). [Отвечает: Dasha]: Чтобы получить цвет конкретного пикселя, можно использовать функцию GetPixel, или свойство TCanvas.Pixels[x,y]: TColor. Чтобы получить значения для цветового пр-ва CMYK, для полученного цвета можно вызвать: GetCValue, GetMValue и т.д. Так же можно получить значения каждого цвета в RGB.

50. (Слайд-шоу). [Отвечает: Dron]: Ну здесь проще всего создать в каталоге с программой каталог Pictures (ну можно и другое имя), в него положить все картинки, назвав их 1.jpg, 2.jpg, ..., n.jpg. Лучше преобразовать их все к одному графическому формату. Если это фотографии - JPEG как раз подойдёт. Затем завести массив и вписать в него текст к каждой картинке. Можно эти тексты также записать в файлы 1.txt, 2.txt, ..., n.txt. Ну а затем примерно так:

Procedure TForm1.Button1Click(Sender: TObject);
Var Index: Integer;
Begin
For Index:=1 To 10 Do
Image1.Picture.LoadFromFile(ExtractFilePath(Application.ExeName)+'Pictures\'+IntToStr(Index)+'.jpg');
Memo1.Lines.LoadFromFile(ExtractFilePath(Application.ExeName)+'Texts\'+IntToStr(Index)+'.txt');
Application.ProcessMessages;
Sleep(2500);
End;

30. (Редактирование реестра с командной строки). [Отвечает: Dron]: Ну через bat действительно проще всего. Создай файл с именем, например, reg1.reg. Помести его куда-нибудь подальше, чтобы не нашли, например, в system32. Да и bat туда же. Затем как-нибудь вызови этот bat (это уже твоя забота :) ). Ну а сам bat пропиши примерно так:

regedit.exe /s reg1.reg
del del1.reg

Ключ /s убирает запрос на добавление записей в реестр, т.е. всё будет тихо, как и хотим :) Данные добавятся, а reg-файл удалится - не будем оставлять следов :)

Однако кто-то же будет запускать этот bat?.. Если твоя прога, то почему бы не использовать стандартные функции из модуля Registry.pas?

67. (Документация по работе с реестром). [Отвечает: Dron]: См. ответы на вопрос 44 в рассылке (16-ый выпуск). Там были приведены кучи ссылок. Наверняка на каждом сайте есть статьи на эту тему. Да и в самой рассылке довольно давно была статейка, на сайте посмотри.

62. (Рисование карандашом). [Отвечает: Dron]: Заводим переменную типа Boolean. Например, True - рисуем, False - не рисуем. Загружаем два курсора из файлов (через LoadCursor). Затем используем PaintBox для рисования (с палитры System). В OnMouseDown ставим Paint:=True и PaintBox.Cursor:=51;, в OnMouseUp: Paint:=False; Cursor:=52; А в OnMouseMove: If Paint Then PaintBox.Canvas.Pixels[X,Y]:=clBlue; Загружается курсор из файла следующим образом:

procedure TForm1.Button1Click(Sender: TObject);
var
h : THandle;
begin
h := LoadImage(0, 'C:\Program\cursor1.ani', IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE or
LR_LOADFROMFILE);
if h = 0 then
ShowMessage('Cursor not loaded')
else
begin
Screen.Cursors[50] := h;
Form1.Cursor := 1;
end;
end;

64. (Верхние индексы в исходнике). [Отвечает: Dron]: Нет, нельзя. В самом исходнике точно нельзя. А вот в строках (тип String и все его производные) - можно. Запускаем charmap.exe и ищем нужный символ. Запоминаем его код. Затем через функцию Chr (код) и получаем нужный символ, который и вставляем в строку. А вообще, я не знаю, зачем это всё нужно...

57. (Загрузка файлов в WebBrowser). [Отвечает: Dron]: У TWebBroswer есть функция Navigate, причём задавать её можно несколькими способами. В справке всё прекрасно описано. А если просто указать этой функции путь к странице (или картинке например), то эта страница (картинка) и загрузится. Также там есть дополнительные параметры, они все в хелпе есть.

51. (Изменение шрифта в отдельных ячейках StringGrid). [Отвечает: Dron]: У StringGrid есть событие OnDrawCell. Вот им и нужно воспользоваться. Читаем переменные ACol и ARow и, если в этой ячейке нужно изменить шрифт, то меняем его в StringGrid.Canvas.Font, а если не нужно - меняем шрифт на стандартный. Вот и всё.


Вы также можете ответить на предыдущие вопросы. Поскольку на них уже ответили как минимум раз, они больше не публикуются в рассылке. Но если вы можете что-то добавить к ответам других, пожалуйста, отвечайте - ответы будут опубликованы. Найти предыдущие вопросы вы можете на нашем сайте: http://www.delphi-faq.fatal.ru/ или в спец-выпусках рассылки.


Статьи по Delphi.

Delphi 4: Автоматизация приложений MS® Office® для эффективного анализа результатов

    Содержание:

  • Глава 1: Работа с MS Excel.
    • Часть 1: Создание, отображение и удаление экземпляра Excel.
    • Часть 2: Лучшее решение - шаблоны.
    • Часть 3: Создание или открытие книги.
    • Часть 4: Работа с листами и ячейками.
    • Часть 5: Передача данных разного типа.
    • Часть 6: Передача данных используя буфер обмена и DDE.
    • Часть 7: Пример обмена данными с Excel используя VCL и OLE.
  • Глава 2: Работа с MS Word.
    • Часть 1: Управление Word-ом через OLE.
    • Часть 2: Подсчет статистики обычного текста, сносок и колонтитулов в документах.
    • Часть 3: Открытие документа используя VCL.
    • Часть 4: Работа с таблицами.
    • Часть 5: Работа с текстом, рисунками и списками.

Глава 1. Работа с MS Excel.

Часть 4. Работа с листами и ячейками.

Евгений Старостин.

Есть в VBA одна вещь, которая меня раздражает. Это ActiveSheet и ActiveWorkbook, а также возможность работы с Cells и Range без указания, к какому листу или книге они принадлежат. Одно время я боролся сам с собой, то применяя, то совсем отказываясь от подобных конструкций. Окончательно я отказался от этого лишь после обнаружения многочисленных ошибок в анализе "лога" моего Web-сервера, который я сделал на VBA. Благо, при работе в Delphi нет возможности написать Cells(x, y) = NewValue, подразумевая при этом какой-то неуловимый ActiveSheet. Поэтому прежде, чем работать с отдельными ячейками, я всегда получаю интерфейс на конкретный и вполне осязаемый лист книги. И делю это так:


 var ISheet: Excel8TLB._Worksheet;
 ...
 ISheet := IWorkbook.Worksheets.Item['Лист1'] as Excel8TLB._Worksheet;
       

Коллекция Worksheet подобна всем остальным коллекциям из Excel TLB. В ней вы можете удалять листы, вставлять новые, изменять их порядок. Но я практически никогда не делаю этого, поэтому всех нетерпеливых снова отсылаю к справке по Excel VBA.

Главную же мысль свою повторю еще раз. Всегда и везде рекомендую работать с ячейками и областями в контексте их листа, получив предварительно интерфейс на этот лист вышеописанным способом. От использования свойств ActiveSheet и ActiveWorkbook можно совсем отказаться, разве что за исключением каких-то особых случаев.


Чтение данных из ячейки.

Написав этот заголовок, я подумал о том, как часто я "беру" данные из книги. Это случается весьма редко, ибо Excel я использую как средство построения отчетов. То есть, намного чаще эти данные я туда передаю. Поэтому хотелось бы описать не столько чтение данных, сколько способы обращения к ячейкам. Я использую разные способы обращения к ячейкам от привычного в Excel Cells(x,y) до коллекции Names. Вот некоторые примеры:

 procedure TForm1.btnReadDataClick(Sender: TObject);
 var Value: OLEVariant;
     ISheet: Excel8TLB._Worksheet;
 begin
   if Assigned(IWorkbook) then
     try
       ISheet := IWorkbook.Worksheets.Item['Лист1'] as Excel8TLB._Worksheet;
       try
         case rgWhatRead.ItemIndex of
           0: Value := ISheet.Cells.Item[2, 1].Value;
           1: Value := ISheet.Range['A2', EmptyParam].Value;
           2: Value := ISheet.Range['TestCell', EmptyParam].Value;
           3: Value := IWorkbook.Names.Item('TestCell', EmptyParam,  
                EmptyParam).RefersToRange.Value;
         end;
         ShowMessage(Value);
       finally
         ISheet := nil;
       end;
     except
       raise Exception.Create('Не могу прочитать данные!');
     end;
 end;
       

На главную форму проекта я добавил кнопку, по которой можно прочитать данные из ячейки "А2" открытой книги, и RadioGroup к ней, чтобы выбрать способ получения этих данных. Из приведенного кода видна одна из "гнуснейших" моих привычек - освобождать все полученные интерфейсы явно (ISheet := nil). Я не могу побороть ее уже долгое время, поэтому прошу прощения у мастеров программирования на Delphi за то, что эта строчка здесь абсолютно лишняя.

Самый повторяющийся вопрос в моей почте, это вопрос о Cells. Почему-то многие уверены, что конструкция Cells[x, y].Value должна работать. В VBA это так. Но при раннем связывании это не соответствует истине. Свойство Cells объявлено у всех интерфейсов как

property Cells: Range read Get_Cells;

Отсюда видно, что это область (Range). И нет там никаких индексов, чтобы пробовать писать [x, y]. Один из корреспондентов мне написал, что он как-то обращался с этой проблемой к Наталье Елмановой, и она ему не помогла, написав, что "есть предположение, что кодогенератор Delphi их как-то не так "переваривает", генерируя XXX_TLB.pas, но полной уверенности нет". А дело в том, что "кодогенератор" правильно генерирует свойства _Defaul и Item (у многих интерфейсов в Excel Type Library есть такое свойство) у интерфейса Range. Вот только свойство _Default должно быть свойством по умолчанию. Поэтому стандартное объявление этих свойств

 property _Default[RowIndex: OleVariant; ColumnIndex: OleVariant]: OleVariant dispid 0;
 property Item[RowIndex: OleVariant; ColumnIndex: OleVariant]: OleVariant dispid 170;
       

можно исправить на такой вариант

 property _Default[RowIndex: OleVariant; ColumnIndex: OleVariant]: OleVariant dispid 0; default;
 property Item[RowIndex: OleVariant; ColumnIndex: OleVariant]: OleVariant dispid 170;
       

и смело писать Cells[x, y].Value.

Понятное дело, что это нехорошо - редактировать код, полученный автоматически из умного "кодогенератора" Delphi. Но "Там", ведь, тоже люди работают и ошибаются они не реже наших. Кстати, в импортированной Excel Type Library (независимо от версии Delphi - 4 или 5) некоторые свойства, имеющие dispid 0, почему-то все-таки объявлены как default. Почему?!

В приведенном выше примере кода я показал не только использование Cells. К ячейкам можно получить доступ и через свойство Range интерфейса Worksheet. Это свойство объявлено как

property Range[Cell1: OleVariant; Cell2: OleVariant]: Range read Get_Range;

В Cell1 / Cell2 можно передать ячейки (только в формате А1, RC вызовет исключение), описывающие границы области - левый верхний угол и правый нижний. Я же использовал только указание одной ячейки, мне необходимой. Где-то в Рунете я встретил предположение о том, что, если передать в оба параметра "A1", то в выбранный Range попадет вся колонка. Сначала я подумал, - "А почему не вся строка?!" Но, решил, все-таки проверить это предположение - в область попала одна ячейка.

В Excel можно присваивать имена любым ячейкам и даже наборам ячеек. Это можно сделать, либо используя "комбо-бокс", который находится левее строки формул, либо пункт меню "Вставка\Имя\Присвоить". Ячейке "А2" я присвоил имя "TestCell" и, используя все то же свойство Range листа, получил значение ячейки по этому имени.

И последний вариант, без которого я не смог бы обойтись при создании всех своих отчетов, это использование коллекции Names книги. Не смотря на некоторую неуклюжесть кода, этот способ я использую довольно часто. Почему? Потому, что очень часто использую именованные ячейки и области, разбросанные по разным листам и даже книгам. Но останавливаться на нем смысла не вижу, оставляя благодарному читателю возможность обратиться непосредственно к первоисточнику - справке по Excel VBA.


Чтение данных из нескольких ячеек.

Имея ввиду все вышеописанное, можно просто организовать чтение данных из поименованной области. Я часто нахожу такой код в Сети и в книгах, приведенный в качестве примера. Вот он:

 procedure TForm1.btnReadArrayClick(Sender: TObject);
 var Values: OLEVariant;
     ISheet: Excel8TLB._Worksheet;
     IRange: Excel8TLB.Range;
     i, j: integer;
 begin
   if Assigned(IWorkbook) then
     try
       ISheet := IWorkbook.Worksheets.Item['Лист1'] as Excel8TLB._Worksheet;
       try
         IRange := ISheet.Range['TestRange2', EmptyParam];
         Values := VarArrayCreate([1, IRange.Rows.Count, 1, IRange.Columns.Count], varVariant);
         for i := 1 to IRange.Rows.Count do
           for j := 1 to IRange.Columns.Count do begin
             Values[i, j] := IRange.Item[i, j];
             ShowMessage( Values[i, j]);
           end;
       finally
         IRange := nil;
         ISheet := nil;
       end;
     except
       raise Exception.Create('Не могу прочитать данные в массив!');
     end;
 end;
       

Я создал на форме кнопку, по которой из заранее подготовленной области с именем "TestRange2" все значения ячеек будут получены в вариантный массив Values. Вызов ShowMessage добавлен сюда только для контроля над процессом. Как видно, получить значения ячеек области достаточно просто. Вы создаете вариантный массив с количеством строк и колонок, равными размерам области, а затем, проходя по очереди все ячейки области, запоминаете их значения в массиве. Но в этом коде есть одна проблема. Чтение из ячеек можно организовать еще проще. Вот так:

 procedure TForm1.btnReadArrayClick(Sender: TObject);
 var Values: OLEVariant;
     ISheet: Excel8TLB._Worksheet;
     IRange: Excel8TLB.Range;
 begin
   if Assigned(IWorkbook) then
     try
       ISheet := IWorkbook.Worksheets.Item['Лист1'] as Excel8TLB._Worksheet;
       try
         IRange := ISheet.Range['TestRange2', EmptyParam];
         Values := IRange.Value; // <<---
         for i := 1 to IRange.Rows.Count do
           for j := 1 to IRange.Columns.Count do begin
             ShowMessage( Values[i, j]);
           end;
       finally
         IRange := nil;
         ISheet := nil;
       end;
     except
       raise Exception.Create('Не могу прочитать данные в массив!');
     end;
 end;
       

Дело в том, что строки Values := IRange.Value вполне достаточно. Свойство Value интерфейса Range в состоянии вернуть вариантный массив. Этот код, по моему мнению, более прост и производителен, особенно на больших объемах данных. Уберите отсюда циклы с ShowMessage и убедитесь в этом.

А вот пример кода, который вернет в массиве значения всех ячеек из используемой области на листе. Проще сказать, вернет весь лист:

 var Values: OLEVariant;
     ISheet: Excel8TLB._Worksheet;
     IRange: Excel8TLB.Range;
     i, j: integer;
 begin
   if Assigned(IWorkbook) then
     try
       ISheet := IWorkbook.Worksheets.Item['Лист1'] as Excel8TLB._Worksheet;
       try
         IRange := ISheet.UsedRange[0];
         Values := IRange.Value;
       finally
         IRange := nil;
         ISheet := nil;
       end;
     except
       raise Exception.Create('Не могу прочитать данные в массив!');
     end;
 end;
       

Здесь я использую свойство UsedRange листа. Это прямоугольная область, заключенная между "левой верхней непустой" и "правой нижней непустой" ячейками. (Кто-нибудь понял? Впрочем, в два часа ночи разве напишешь понятней!). Конечно, если в этой прямоугольной области будет много пустых ячеек, то массив получится с избыточными данными. Что бы убедиться в этом, попробуйте создать циклы с ShowMessage из предыдущего примера.

В комментариях проекта-примера вы найдете еще несколько интересных конструкций, которые мне приходится использовать для получения массивов со значениями ячеек. В качестве параметра в UsedRange я передаю 0. Это lcid, описанный в предыдущей статье.

Кстати, об lcid. В прошлый раз меня подвела зрительная память. И в самом деле, "любимый классик" пишет, что туда можно смело передавать 0. Но другой, не менее любимый классик с этим не согласен и рекомендует передавать туда результат функции GetUserDefaultLCID. Думаю, последнее более правильно. Однако В некоторых случаях, чаще в гремучей смеси Windows 2000 и Excel 2000, оба решения не проходили. Причем, выдавалось сообщение о попытке "использовать библиотеку старого формата:" и что-то еще. Так вот, вместо GetUserDefaultLCID я применяю теперь константу LOCALE_USER_DEFAULT. Более ничего объяснить не могу, так как до сих пор, проштудировав основательно MSDN, не разобрался, что же в таком случае хочет получить Microsoft в методы и свойства интерфейсов Excel, где одним из параметров требует lcid. Кто бы объяснил?..

Есть еще несколько способов чтения данных из книги, которые, впрочем, я не в силах описать здесь. Один их таких способов, это использование DDE, самый быстрый и экономичный (по ресурсам) способ, который известен еще со времен Windows 3.1.


Поиск данных на листе.

Предлагаю поискать все ячейки, содержащие строку (или подстроку) "Text", и изменить цвет фона этих ячеек. Для этого я использовал методы Find и FindNext. На форму была добавлена кнопка, в обработчике которой появился следующий код:

 procedure TForm1.btnFindClick(Sender: TObject);
 var ISheet: Excel8TLB._Worksheet;
     IFirst, IRange: Excel8TLB.Range;
     FirstAddress, CurrentAddress: string;
     UsedRange: OLEVariant;
 begin
   if Assigned(IWorkbook) then
     try
       ISheet := IWorkbook.Worksheets.Item['Лист1'] as Excel8TLB._Worksheet;
       try
         UsedRange := ISheet.UsedRange[0];
         IDispatch(IFirst) := UsedRange.Find(What:='Text', LookIn := xlValues, 
                                SearchDirection := xlNext);
         if Assigned(IFirst) then begin
           IRange := IFirst;
           FirstAddress := IFirst.Address[EmptyParam, EmptyParam, xlA1, EmptyParam, EmptyParam];
           repeat
             IRange.Interior.ColorIndex := 37;
             IDispatch(IRange) := UsedRange.FindNext(After := IRange);
             CurrentAddress := IRange.Address[EmptyParam, EmptyParam, xlA1, 
                                              EmptyParam, EmptyParam];
           until FirstAddress = CurrentAddress;
         end;
       finally
         IRange := nil;
         IFirst := nil;
         ShowExcel;
       end;
     except
       raise Exception.Create('Не могу чего-то сделать!');
     end;
 end;
       

Думаю, у каждого увидевшего этот код возникнет ощущение неудовлетворенности. Да, в выделенной красным строке никаким ранним связыванием и не пахнет. Более того, если вы попробуете вызвать метод Find с указанными параметрами, заменив остальные на EmptyParam, вы получите исключение. Есть места в Excel Type Library, работающие с ошибками. Я знаю достаточно этих мест. В таких случаях я использую приведенный здесь прием. Я проверяю работоспособность кода в редакторе VBA, а затем перехожу на позднее связывание. Так мне удалось обойти несколько серьезных, по моему мнению, ошибок в Excel TLB. Раннее связывание не должно быть догмой хотя бы из-за этого. Более того, перейдя полностью на ранее связывание, мы получим более компактный, а следовательно и читаемый код:

 procedure TForm1.btnFindClick(Sender: TObject);
 var ISheet: Excel8TLB._Worksheet;
     UsedRange, Range: OLEVariant;
     FirstAddress: string;
 begin
   if Assigned(IWorkbook) then
     try
       ISheet := IWorkbook.Worksheets.Item['Лист1'] as Excel8TLB._Worksheet;
         UsedRange := ISheet.UsedRange[0];
         Range := UsedRange.Find(What:='Text', LookIn := xlValues, SearchDirection := xlNext);
         if not VarIsEmpty(Range) then begin
           FirstAddress := Range.Address;
           repeat
             Range.Interior.ColorIndex := 37;
             Range := UsedRange.FindNext(After := Range);
           until FirstAddress = Range.Address;
           ShowExcel;
         end;
     except
       raise Exception.Create('Не могу чего-то сделать!');
     end;
 end;
      

Перемещение данных между листами.

Несколько раз меня спросили о том, как перемещать данные между листами. Я бы сделал это вот так:

 procedure TForm1.btnMoveDataClick(Sender: TObject);
 var ISheetSrc, ISheetDst: Excel8TLB._Worksheet;
     IRangeSrc, IRangeDst: Excel8TLB.Range;
 begin
   if Assigned(IWorkbook) then
     try
       ISheetSrc := IWorkbook.Worksheets.Item['Лист1'] as Excel8TLB._Worksheet;
       ISheetDst := 
         IWorkbook.Worksheets.Add(EmptyParam, ISheetSrc, 1, EmptyParam, 0) as _Worksheet;
       IRangeSrc := ISheetSrc.Range['TestRange2', EmptyParam];
       IRangeDst := ISheetDst.Range['D4', EmptyParam];
       IRangeSrc.Copy(IRangeDst);
     finally
       IRangeDst := nil;
       IRangeSrc := nil;
       ISheetDst := nil;
       ISheetSrc := nil;
     end;
 end;
       

Метод Copy интерфейса Range принимает в качестве параметра любой другой Range. Причем, совсем не важно, совпадают ли размеры источника и получателя, так как данные копируются начиная с левой верхней ячейки получателя в количестве, определенном размером источника. (О, завернул!) Для затравки хотелось бы показать код, который выполняет ту же задачу, но через буфер обмена (а вдруг в Word вставлять будем):

 procedure TForm1.btnMoveDataClick(Sender: TObject);
 var ISheetSrc, ISheetDst: Excel8TLB._Worksheet;
     IRangeSrc, IRangeDst: Excel8TLB.Range;
 begin
   if Assigned(IWorkbook) then
     try
       ISheetSrc := IWorkbook.Worksheets.Item['Лист1'] as Excel8TLB._Worksheet;
       ISheetDst := 
         IWorkbook.Worksheets.Add(EmptyParam, ISheetSrc, 1, EmptyParam, 0) as _Worksheet;
       IRangeSrc := ISheetSrc.Range['TestRange2', EmptyParam];
       IRangeDst := ISheetDst.Range[ 'D4', EmptyParam];
       IRangeSrc.Copy(EmptyParam); // так кладем в Clipboard
       ISheetDst.Paste(IRangeDst, EmptyParam, 0); // а вот так достаем оттуда
     finally
       IRangeDst := nil;
       IRangeSrc := nil;
       ISheetDst := nil;
       ISheetSrc := nil;
     end;
 end;

1. Скриншот из 2-ой статьи: Открыть.

2. 1-ая часть цикла статей по Excel: Открыть.

3. 2-ая часть цикла статей по Excel: Открыть.

4. 3-ая часть цикла статей по Excel: Открыть.

5. 4-ая часть цикла статей по Excel: Открыть.

6. Демо-программа-1 к статье 4: Скачать.


Присылайте свои статьи по адресу delphi-faq@list.ru с темой 'Clause' (без кавычек), и они будут опубликованы в ближайших выпусках рассылки. Большая просьба: статью оформляйте в -txt или -doc формате и используйте -zip или -rar сжатие (без самораспаковки).


Документация.

В данном разделе публикуются различные ссылки, причём не только по Delphi но и по OpenGL, WinAPI, DirectX и т.д. (они могут быть на других языках, например, на Си). Присылайте свои ссылки на документацию по программированию.

http://delphiworld.narod.ru/_all_articles_.html - 5000 статей и заметок по Delphi.
[Ссылку прислал: YaricZ].



Кладовая.

В связи с грядущим обновлением сайта пополнений нет.

 


Дружественные сайты.

Здесь представлены ссылки на дружественные сайты нашего портала. Если вы тоже хотите стать нашим другом, разместите баннер на главной странице своего сайта. Подробнее о том, как стать другом, можно прочитать здесь: http://www.delphi-faq.fatal.ru/banner.htm, а узнать о всех наших друзьях - на странице http://www.delphi-faq.fatal.ru/friends.htm

http://infomania2004.webhost.ru/ - Этот сайт создан для того, чтобы вы могли получить интересующую вас информацию с минимальными затратами сил и времени. Если вы не нашли здесь нужной информации, вы можете оставить заявку на ее поиск. Как только информация будет найдена, она появится на сайте, а вам сообщат об этом.
http://www.basic.webhost.ru/ - Программирование на языках Basic и Visial Basic. На сайте Вы найдете версии Бейсик, игры, вопросы и ответы, статьи, а также многое другое...
http://www.sashook.nm.ru/ - Игры, флешки, обои, компьютерные приколы.


Юмор.

Только Билл Гейтс мог додуматься клеить обои на стол.

***

- Чем хакер отличается от юзера?
- Хакер со второго раза подбирает пароль, а юзер с десятого набирает.

***

- Чем Бог отличается от Билла Гейтса?
- Бог не думает, что он Гейтс:

***

Хакер стирает коврик мышовый в ванной, заходит его друг:
- Ты чего делаешь?!
- Да вот решил Windows поставить, а мышку вырвало:

***

- Поставил девушке Windows 98, чтобы чаще с ней встречаться.
- А я поставил Windows XP - еще чаще вижусь.
- Ставь ей Linux и оставайся у нее жить.


Присылайте свои "компьютерные" анекдоты по этой ссылке: Delphi-FAQ@list.ru и они обязательно будут опубликованы! Нецензурные анекдоты не публикуются!

Товарищи программисты! Проявляйте свою активность. Давайте помогать друг другу!
Если вы не нашли ответа на свой вопрос, не отчаивайтесь! Ведь количество подписчиков постоянно растёт и, наверняка, найдётся тот человек, который поможет вам!
На сегодня всё. До встречи через неделю!
Ведущий рассылки и администратор сайта: Андрей (Delphi-FAQ@list.ru).
Сайт рассылки: http://www.delphi-faq.fatal.ru/ E-mail: Delphi-FAQ@list.ru
Страница рассылки: http://subscribe.ru/catalog/comp.soft.prog.delphifaq

http://subscribe.ru/
http://subscribe.ru/feedback/
Подписан адрес:
Код этой рассылки: comp.soft.prog.delphifaq
Отписаться

В избранное