New Page 1
Программирование COM в
Delphi. Урок 5. Практическое применение интерфейсов (TListView,
IUnknown).
Исходники к уроку можно скачать
здесь.
Сегодня мы разберем пример практического применения интерфеса.
Нет, это еще пока не COM, до него мы рано или поздно
доберемся. Но сегодня мы, наконец то, продемонстрируем, как извлечь из
интерфесов реальную пользу. И так, давайте напишем программу, демонстрирующую
алгоритм сортировки. Поскольку нам может потребоваться сортировать совершенно
разные объекты: строки, числа, да и вообще Бог знает что, объявим специальный
сортировочный интерфейс, который будет содержать метод, предназначенный для
сравнения объектов:
ICompare=interface
['{6D7DEC57-1572-49A0-9287-3DE3EA4496BA}']
function
CompareWith(ACompare:ICompare;
ASortBy:integer):Integer;
end; |
И реализуем простой алгоритм сортировки с применением этого
интерфейса (подробнее об
алгоритмах сортировки см. здесь):
procedure
SortArray(var
A:Array
of
IUnknown;
ASortBy:integer);
var
I,J:integer;
temp:IUnknown;
begin
for
i:=Low(A)
To
High(A)-1
do
for
J:=I
To
High(A)
do
if
(A[J]
as
ICompare).CompareWith(A[I]
as
ICompare,
ASortBy)<0
then
begin
temp:=A[I];
A[I]:=A[J];
A[J]:=temp;
end;
end; |
Как вы заметили, алгоритм получился универсальный - мы можем
использовать его для любых объектов, поддерживающих интерфейс ICompare. Создавая
новый класс, мы просто реализуем у него метод CompareWith, отвечающий за
сравнение объектов.
А теперь переходим к самим классам, которые мы будем
сортировать. Для начала объявим класса TEmployee (информация о сотруднике):
TEmployeeOrder=(eoName,
eoSalary);
//Перечисление порядковых номеров полей
//Интерфейс доступа к полям сотрудника
IEmployee=interface
['{D967D86A-D5D8-41FF-94DC-394B6D6D4659}']
function
GetName:string;
function
GetSalary:double;
end;
//Класс для хранения информации о сотруднике
TEmployee=class(TInterfacedObject,
IEmployee,
ICompare)
private
FName:String;
FSalary:double;
public
constructor
Create(AName:string;
ASalary:Double);
function
CompareWith(ACompare:ICompare;
ASortBy:integer):integer;
function
GetName:string;
function
GetSalary:double;
end; |
Как видим, данный класс реализует два интерфейса ICompare - для
сортировки и IEmployee - для доступа к полям информации о сотруднике. Теперь
реализуем данный класс:
function
TEmployee.CompareWith(ACompare:ICompare;
ASortBy:Integer):integer;
var
emp:IEmployee;
begin
Result:=0;
Emp:=ACompare
as
IEmployee;
case
ASortBy
of
Ord(eoName):Result:=CompareStr(FName,
Emp.GetName);
Ord(eoSalary):
if
FSalary<Emp.GetSalary
then
Result:=-1
else
if
FSalary>Emp.GetSalary
then
Result:=1;
else
Result:=0;
end;
end;
Constructor
TEmployee.Create(AName:string;
ASalary:Double);
begin
Inherited
Create;
FName:=AName;
FSalary:=ASalary;
end;
function
TEmployee.GetName:string;
begin
Result:=FName;
end;
function
TEmployee.GetSalary:Double;
begin
Result:=FSalary;
end; |
Как видим, алгоритм сравнения довольно прост - в зависимости от
параметра ASortBy мы сравниваем одно из полей информации о сотруднике.
Теперь, аналогичным образом объявим класс TInventoryItem для
хранения информации о номенклатурных единицах производственного инвентаря:
TInventoryItemOrder=(iioPartNumber,
iioDescription,
iioInStock);
IInventoryItem=interface
['{496E2A60-A9BC-4214-9040-6E8CA926AC60}']
function
GetPartNumber:
string;
function
GetDescription:string;
function
GetInStock:integer;
end;
TInventoryItem=class(TInterfacedObject,
IInventoryItem,
ICompare)
private
FPartNumber:string;
FDescription:string;
FInStock:integer;
public
constructor
Create(APartNumber:string;
ADescription:string;
AinStock:integer);
function
CompareWith(ACompare:ICompare;
ASortBy:integer):integer;
function
GetPartNumber:string;
function
GetDescription:string;
function
GetInStock:Integer;
end; |
И реализуем его:
function
TInventoryItem.CompareWith(ACompare:
ICompare;
ASortBy:integer):integer;
var
Inv:IInventoryItem;
r:double;
begin
Result:=0;
Inv:=ACompare
As
IInventoryItem;
case
ASortBy
of
ord(iioPartNumber):Result:=CompareStr(FPartNumber,Inv.GetPartNumber);
ord(iioDescription):Result:=CompareStr(FDescription,Inv.GetDescription);
ord(iioInStock):Result:=FInStock-Inv.GetInStock;
end;
end;
constructor
TInventoryItem.Create(APartNumber:string;
ADescription:string; AInStock:integer);
begin
inherited
Create;
FPartNumber:=APartNumber;
FDescription:=ADescription;
FInStock:=AInStock;
end;
function
TInventoryItem.GetPartNumber:string;
begin
Result:=FPartNumber;
end;
function
TInventoryItem.GetDescription:string;
begin
Result:=FDescription;
end;
function
TInventoryItem.GetInStock:integer;
begin
Result:=FInStock;
end; |
Теперь переходим к созданию тестового примера. И так, помещаем
на форму TPageControl (закладка Win
32):
Создаем у него две страницы: "Bubble Sort"
и "List View". На первую страницу кладем
TListBox (закладка Standard):
А так же пару кнопок TButton:
В итоге первая страница формы у нас должна выглядеть вот
так:
На вторую закладку мы кладем TListView (закладка
Win 32):
И редактируем у него колонки, как показано на скриншоте:
Что бы отредактировать колонки, щелкаем по объект правой кнопкой
мыши и во всплывающем меню выбираем Columns Editor:
И в нем мы уже редактируем колонки:
Теперь возвращаемся к программированию. Для начала в классе
формы объявим кое какие поля и методы в секции private:
TSortDemoForm
=
class(TForm)
PageControl1:
TPageControl;
TabSheet1:
TTabSheet;
TabSheet2:
TTabSheet;
btnByName:
TButton;
btnBySalary:
TButton;
lbEmployees:
TListBox;
lvInventory:
TListView;
procedure
FormCreate(Sender:
TObject);
procedure
btnByNameClick(Sender:
TObject);
procedure
btnBySalaryClick(Sender:
TObject);
procedure
lvInventoryColumnClick(Sender:
TObject;
Column:
TListColumn);
procedure
lvInventoryCompare(Sender:
TObject;
Item1,
Item2:
TListItem;
Data:
Integer;
var
Compare:
Integer);
private
{ Private declarations }
FEmpArray:Array[1..MAX_EMPLOYEES]
of
IUnknown;
FInvArray:Array[1..MAX_INVENTORYITEM]
of
IUnknown;
procedure
LoadListBox;
procedure
CreateInventoryItem(Index:integer;
APartNo,
ADescription:string;
AInStock:integer);
public
{ Public declarations }
end;
|
Теперь пишем обработчик нажатия кнопок сортировки:
procedure
TSortDemoForm.btnByNameClick(Sender:
TObject);
begin
SortArray(FEmpArray,
Ord(eoName));
LoadListBox;
end;
procedure
TSortDemoForm.btnBySalaryClick(Sender:
TObject);
begin
SortArray(FEmpArray,
Ord(eoSalary));
LoadListBox;
end; |
Теперь нам нужно реализовать процедуру отображения массива
сотрудников в списке значений:
procedure
TSortDemoForm.LoadListBox;
var
Index:integer;
emp:IEmployee;
begin
lbEmployees.Items.Clear;
for
Index:=1
to
MAX_EMPLOYEES
do
begin
Emp:=FEmpArray[index]
As
IEmployee;
lbEmployees.Items.Add(Emp.GetName+'
'+FloatToStrF(Emp.GetSalary,ffCurrency,8,2))
end;
end; |
Еще у нас на второй закладке есть таблица - компонент
TListView. Во первых, что бы при нажатии на заголовок
столбца происходила сортировка, нам нужно реализовать у этого компонента событие
OnColumnClick:
procedure
TSortDemoForm.lvInventoryColumnClick(Sender:
TObject;
Column:
TListColumn);
begin
lvInventory.CustomSort(nil,Column.Index);
end; |
Для реализации сортировки программируем событие
OnCompare
procedure
TSortDemoForm.lvInventoryCompare(Sender:
TObject;
Item1,
Item2:
TListItem;
Data:
Integer;
var
Compare:
Integer);
var
I1,I2:IUnknown;
begin
I1:=IUnknown(Item1.Data);
I2:=IUnknown(Item2.Data);
Compare:=(I1
AS
ICompare).CompareWith((I2
AS
ICompare),
Data);
end; |
И, наконец, последний штрих, событие формы
OnCreate, в нем мы заполняем массив сотрудников и строки компонента
TListView:
procedure
TSortDemoForm.FormCreate(Sender:
TObject);
begin
FEmpArray[1]:=TEmployee.Create('Иванов',10000);
FEmpArray[2]:=TEmployee.Create('Петров',9000);
FEmpArray[3]:=TEmployee.Create('Сидоров',12500);
FEmpArray[4]:=TEmployee.Create('Воробьев',8500);
FEmpArray[5]:=TEmployee.Create('Лебедев',21000);
LoadListBox();
CreateInventoryItem(1,'P5409','Widget',35);
CreateInventoryItem(2,'X1234','Gadget',4);
CreateInventoryItem(3,'J7749','Doodlad',17);
CreateInventoryItem(4,'1A749','Table',2);
CreateInventoryItem(5,'A2349','Mouse',1);
end; |
Создает строку функция CreateInventoryItem:
procedure
TSortDemoForm.CreateInventoryItem(Index:integer;
APArtNo,
ADescription:string;
AInStock:integer);
var
ListItem:TListItem;
begin
FInvArray[index]:=TInventoryItem.Create(APartNo,ADescription,AInStock);
ListItem:=lvInventory.Items.Add;
ListItem.Caption:=APartNo;
ListItem.SubItems.Add(ADescription);
ListItem.SubItems.Add(IntToStr(AInStock));
ListItem.Data:=Pointer(FInvArray[index]);
end; |
В этой функции мы заполняем данные столбца, а ссылку на объект
TInventoryItem, который у нас будет сравниваться при сортировке в событии
OnCompare, передаем компоненту
TListView через поле Data.
Все, теперь можно запускать программу на тестирование:
Нажимаем на кнопу сортировки по имени (btnByName):
Сортировка по зарплате (btnBySalary):
Дальше переключаемся на List View:
Сортируем по Part #::
По Description:
По In Store:
Исходники к уроку можно скачать
здесь..
(C) Шуравин Александр
|