Главная » Шпаргалка » DELPHI INTERBASE FireBird » delphi Office®

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

 	  

Практически тот же код. Только в D5 вы работаете уже не с интерфейсом напрямую, а с экземпляром класса TexcelApplcation. Если посмотреть его предков, то можно увидеть, что это настоящий класс, освободить который просто необходимо. Поэтому вместо присваивания в nil там написано FreeAndNil (помните такую процедуру?).

Часть 2: Лучшее решение - шаблоны.

Excel, интегрированный с моими приложениями, хорош (для меня - программиста) только по одной причине. Я всегда создаю шаблоны и использую их потом при построении отчетов. Шаблоны позволяют мне избежать ручного (в исходном тексте) форматирования. В общем случае, алгоритм выглядит просто: по шаблону создается книга, каким-то образом помеченные области заполняются данными и... (а дальше все уже готово). Как я создаю книгу по шаблону:

Delphi 4.0 / 5.0


function TForm1.AddWorkbook(const WorkbookName: string): Excel8TLB._Workbook;
begin
Result := nil;
if Assigned(FIXLSApp) and (trim(WorkbookName) <> '') then begin
Result := FIXLSApp.Workbooks.Add(WorkbookName, 0);
end;
end;

 	  

В этом коде нет ничего сложного. В принципе при работе с Excel я мало находил мест, где что-либо сделать было бы сложно. Чаще достаточно прочитать справку по VBA или записать макрос (благо, Microsoft встроила в Excel хороший пишущий player). После выполнения этого метода будет добавлена книга, близнец шаблона, с именем шаблона и порядковым номером (как "Книга1.xls" или "Книга228.xls"). Правда здесь есть одна тонкость. Эти "циферки" в имя книги Excel добавляет после поиска книг с таким же названием в каталоге по умолчанию. Я несколько раз наступал на грабли (больно!), когда пытался сохранять книги в другом каталоге и создавать новую - по этому же шаблону. К сожалению, не может эта "злобная" программа держать открытыми несколько книг с одинаковыми названиями, несмотря на то, что они лежат в разных каталогах.

Как я помечаю области, в которые необходимо разместить данные? В Excel существует возможность объединить ячейки в группу и поименовать эту группу. В терминах Microsoft это объект Range (область). Для своего проекта я создал тестовую книгу "Test.xls", в которой на листе "Лист1" разместил область "TestRange" (см. рисунок). Более того, для ячеек этой области я указал форматы вывода (Field4 - дата, Field3 - красный цвет шрифта). Я надеюсь, что после переноса тестовых данных форматы сохранятся.


Что есть шаблон без данных в нем?

Существует масса способов передать данные в Excel, начиная с DDE и заканчивая обычным присваиванием (типа Cell.Value := NewValue ). Конечно, максимальную скорость передачи данных можно получить, только используя DDE. Но я отказался от этого пути из-за некоторых ограничений и давно смущающего меня флажка в настройках Excel ("Игнорировать DDE-запросы"). Поэтому здесь я опишу менее эффективный, но работоспособный, путь решения этой проблемы. Итак, после нажатия кнопки CreateExcel имеем открытый шаблон с листом "Лист1" и областью с именем "TestRange". Для чистоты эксперимента (скорей из лени, великая вещь - собственная лень) я описал константный массив с тестовыми данными - TestDataArray. Именно эти данные я и передаю в ячейки области:

Delphi 4.0


procedure TForm1.btnDataToBookClick(Sender: TObject);
var LaunchDir: string;
IWorkbook: Excel8TLB._Workbook;
ISheet: Excel8TLB._Worksheet;
IRange: Excel8TLB.Range;
NewValueArray, V: OLEVariant;
i: integer;
begin
if Assigned(IXLSApp) then begin
LaunchDir := ExtractFilePath( ParamStr(0) );
IWorkbook := AddWorkbook(LaunchDir + 'Test.xls');
try
ISheet := IWorkbook.Worksheets.Item['Лист1'] as Excel8TLB._Worksheet;
IRange := ISheet.Range['TestRange', EmptyParam];
NewValueArray := VarArrayCreate([0, 20, 1, 4], varVariant);
for i := 0 to 20 do begin
NewValueArray[i, 1] := TestDataArray[i].V1;
NewValueArray[i, 2] := TestDataArray[i].V2;
NewValueArray[i, 3] := TestDataArray[i].V3;
NewValueArray[i, 4] := date + i;
end;
IRange.Value := NewValueArray;
finally
IRange := nil;
ISheet := nil;
IWorkbook := nil;
end;
end;
end;

 	  

Delphi 5.0


procedure TForm1.btnDataClick(Sender: TObject);
type
var LaunchDir: string;
IWorkbook: Excel97.ExcelWorkbook;
ISheet: Excel97.ExcelWorksheet;
IRange: Excel97.Range;
NewValueArray, V: OLEVariant;
i: integer;
begin
if Assigned(IXLSApp) then begin
LaunchDir := ExtractFilePath( ParamStr(0) );
IWorkbook := AddWorkbook(LaunchDir + 'Test.xls');
try
ISheet := IWorkbook.Worksheets.Item['Лист1'] as Excel97.ExcelWorksheet;
IRange := ISheet.Range['TestRange', EmptyParam];
NewValueArray := VarArrayCreate([0, 20, 1, 4], varVariant);
for i := 0 to 20 do begin
NewValueArray[i, 1] := TestDataArray[i].V1;
NewValueArray[i, 2] := TestDataArray[i].V2;
NewValueArray[i, 3] := TestDataArray[i].V3;
NewValueArray[i, 4] := date + i;
end;
IRange.Value := NewValueArray;
finally
IRange := nil;
ISheet := nil;
IWorkbook := nil;
end;
end;
end;

 	  

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

Часть 3. Создание или открытие книги.

В качестве примера я беру проект из предыдущей моей статьи и стану его понемногу расширять, отвечая на вопросы, появившиеся у специалистов разного профиля и кругозора. Эти вопросы получены мною из двух "источников": как реакция на мою статью и, извините, из переписки по XL Report Support. Эти две вещи уж очень сильно пересекаются, поэтому я и обращаюсь к обоим источникам моего вдохновения. Я не буду последователен в своих рассуждениях, местами буду писать подробно, местами - кратко. Попросту, я опишу некоторые часто встречающиеся проблемы и решения этих проблем.

И еще! Я решил совсем опустить в своем пространном (как обычно) повествовании тонкости работы с Excel в Delphi 5.0, так как считаю, что работа с импортированной библиотекой типов принципиально одинакова и в версии 4, и в версии 5. Различен, разве что, только уровень импорта этой самой библиотеки. К тому же, я уже полностью "переехал" на Excel 2000, поэтому тестирую весь код, который приведен здесь, именно в нем. Итак, поехали.


Создание или открытие книги.

Повторюсь, не смотря на то, что я уже писал об этом в предыдущей статье. В главной форме проекта-примера я объявил свойство IWorkbook. Оно будет содержать интерфейс книги, которую мы будем создавать и использовать. Естественно, в обработчике FormDestroy я его освобождаю.

property IWorkbook: Excel8TLB._Workbook read FIWorkbook;

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

if Assigned(IXLSApp) and (not Assigned(IWorkbook) ) then
FIWorkbook := IXLSApp.Workbooks.Add(EmptyParam, 0);

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

Коллекция Workbooks содержит все открытые книги и предоставляет возможность кое-как управлять всем этим.

Боже, как убоги коллекции от Microsoft, и особенно поиск в них! Я отклонюсь, но это надо видеть. Вот пример поиска книги с заданным именем, приведенный как совет в MSDN Office Developer.


Public Function SheetExists(strSearchFor As String) As Boolean
SheetExists = False
For Each sht In ThisWorkbook.Worksheets
If sht.Name = strSearchFor Then
SheetExists = True
End If
Next sht
End Function

Это вам не IndexOf писать. Сами ищите! А я так иделаю. Но, далее...

Метод Add этой коллекции (читай, метод интерфейса) позволяет добавить книгу к этой коллекции, пустую либо по шаблону. Первый параметр этого метода, Template (из справки по Excel VBA), может принимать имя файла с путем.

Поэтому, выполнив код

if Assigned(IXLSApp) and (not Assigned(IWorkbook) ) then
FIWorkbook := IXLSApp.Workbooks.Add(ExtractFilePath(ParamStr(0)) + 'Test.xls', 0);

вы получите книгу, идентичную файлу "Test.xls" с именем Test1.xls. Именно этим способом я создаю все свои отчеты, так как создаю их по заранее разработанным шаблонам. Естественно, что это шаблоны XL Report.

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


if Assigned(IXLSApp) and (not Assigned(IWorkbook) ) then
FIWorkbook := IXLSApp.Workbooks.Open(ExtractFilePath(ParamStr(0)) + "Test.xls', EmptyParam,
EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
EmptyParam, EmptyParam, EmptyParam, false, 0);

 	  

Понимаю, что в душе нормального программиста такой код вызовет отвращение. Как-то я даже получил гневное письмо о собственной ненормальности из-за того, что использую ранее связывание и кучу EmptyParam. Впрочем, я не сильно агрессивный человек (правда, только в переписке), и отвечать не стал. В конечном итоге, раннее связывание дает мне немного преимуществ, но я за него. Я не могу помнить все методы и их параметры из Excel Type Library, поэтому получаю их (только при раннем связывании, естественно) из подсказок редактора Delphi - продуманная вещь этот редактор. А чтобы не мучаться с написанием такого количества EmptyParam, можно написать и так:


if Assigned(IXLSApp) and (not Assigned(IWorkbook)) then
IDispatch(FIWorkbook) := OLEVariant(IXLSApp.Workbooks).Open(
FileName := ExtractFilePath(ParamStr(0)) + 'Test.xls');

 	  

Но, мы отклонились. Что же стоит за таким количеством параметров по умолчанию в методе Open? Да, много чего. Из этого "громадья" я использую лишь несколько вещей. Их я и опишу, а заинтересовавшихся остальными отсылаю к справке по Excel VBA. Вот объявление этого метода в импортированной библиотеке типов:


function Open(const Filename: WideString; UpdateLinks: OleVariant; ReadOnly: OleVariant;
Format: OleVariant; Password: OleVariant; WriteResPassword: OleVariant;
IgnoreReadOnlyRecommended: OleVariant; Origin: OleVariant;
Delimiter: OleVariant; Editable: OleVariant; Notify: OleVariant;
Converter: OleVariant; AddToMru: OleVariant; lcid: Integer): Workbook; safecall;

 	  
Pages:  Prev  1 | 2 | 3 | 4 | 5  Next