PHP Delphi CSS HTML JavaScript Perl API ASP MySQL XML С++ VBasic WEB разработка *NIX CouchDB Hack Python
Главная Статьи Delphi По волнам интеграции2
Главная
 Главная  Контакты
 
Программинг
Статьи Книги ЧаВО
 
xBOOKi
Fresh Books Операционки Сети
 
Поиск
-------
 
Counters
Яндекс цитирования
Rambler's Top100
-------
 
CryptDisk.4h
Программа которая позволяет создать виртуальный шифрованный логический диск.

cryptdisk.4hack.com

-------
 
 
По волнам интеграции
Вот и лето уже перевалило через середину. Странное у меня получилось лето. Заказчики требовали напряжения, жена требовала отдыха, а работа - самоотдачи. За всем за этим я успел сделать несколько курсовых работ. Несколько?! Боже. Господа студенты, отчего вы не можете самостоятельно искать ответы на свои многочисленные вопросы? Впрочем, может быть, я старею, забывая меж всеми своими ежедневными делами о том времени, когда сам был таким. Это дождь виноват. Барабанит частенько по сливам окон, портя настроение. Вот и статья, наверняка, из-за этого получится скучной, а, может быть, и вовсе нудной.

Так о чем это я? Ах да, об интеграции этого самого Excel-а с нашим любимым средством разработки. Прямо и не знаю с чего начать….

Важно!
В качестве примера я беру проект из предыдущей моей статьи и стану его понемногу расширять, отвечая на вопросы, появившиеся у специалистов разного профиля и кругозора. Эти вопросы получены мною из двух источников: как реакция на мою статью и, извините, из переписки по 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;

В FileName необходимо передавать имя открываемого файла, желательно указав путь его нахождения. Иначе файл этот Excel будет искать в каталоге по умолчанию. Чтобы файл был запомнен в списке последних открытых файлов, в AddToMru можно передать true. Иногда я знаю, что файл рекомендован только для чтения (не путать с «парольной» защитой книги). Тогда при открытии выдается соответствующее сообщение. Чтобы игнорировать его, можно передать в IgnoreReadOnlyRecommended true. Вот, пожалуй, и все мои скудные знания об этом методе. Впрочем, с его помощью мне приходилось открывать и файлы текстовых форматов с разделителями. Но тогда я обращался к чудесному «пишущему» плейеру VBA и записывал с его помощью макросы, затем правил их по необходимости и все отлично получалось. Рекомендую и вам всяческие тонкие вопросы разрешать этим же способом.

На главной форме проекта-примера я создал кнопку, с помощью которой можно открыть (или создать) файл и RadioGroup к ней, где можно указать, каким из приведенных выше способов файл этот открывается. Для полного удовлетворения сюда же была добавлена обработка исключения. Вот что у меня получилось:
procedure TForm1.btnCreateBookClick(Sender:TObject); 
var 
     FullFileName: string;
begin
     FullFileName := ExtractFilePath(ParamStr(0)) + 'Test.xls';
     if Assigned(IXLSApp) and (not Assigned(IWorkbook) )
        then
try
         case rgWhatCreate.ItemIndex of
      // По шаблону
              0: FIWorkbook := IXLSApp.Workbooks.Add(FullFileName, 0);
      // Просто откроем
              1: FIWorkbook := IXLSApp.Workbooks.Open(FullFileName,
         EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam,
         EmptyParam, EmptyParam, EmptyParam, EmptyParam, EmptyParam, false, 0);
      // Пустая книга
              2: FIWorkbook := IXLSApp.Workbooks.Add(EmptyParam, 0);
         end;
except
         raise Exception.Create('Не могу создать книгу!');
end
end; 

Далее во всех примерах я подразумеваю, что вы всякий раз будете создавать новую книгу по шаблону с помощью кнопки "Create workbook". Книга-шаблон названа, как прежде, Test.xls и включена в проект. Все остальные примеры опираются именно на эту книгу и ее листы. В этой книге я подготовил кое-какие данные и поименованные области для последующих примеров работы с Excel. Для каждого примера кода я буду добавлять кнопку и, возможно, RadioGroup к ней с возможностью выбора варианта работы. Не судите меня строго за то, что главная и единственная форма проекта-примера получится громоздкой и некрасивой. Не это здесь главное. Итак, всегда создавайте по кнопке книгу. Далее нажимайте кнопку, на которую указывает конкретный пример кода, и наблюдайте. Буду рад, если кто-то из читателей создаст более приемлемый демонстрационный проект для этой статьи.

Работа с листами книги

Есть в 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, но полной уверенности нет». А дело в том, что «кодогенератор» правильно генерирует свойства _Default и 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. Думаю, последнее более правильно. А классики пусть сами разбираются (Канту и Калверт).
Существует еще несколько способов чтения данных из книги, которые, впрочем, я не в силах описать здесь. Один их таких способов, использование 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;

Форматирование ячеек

Нет ничего проще в Excel, чем форматирование ячеек. Я специально не стану освещать эту тему. Вряд ли мне по силам составить конкуренцию лучшему помощнику в этом вопросе - пункту меню Excel "Сервис\Макрос\Начать запись…" А приведенный выше материал, надеюсь, создаст благодатную почву для изучения этого вопроса.

ИтогО

Во многих примерах я ссылался на объявление свойств и методов из Excel Type Library. Таким же образом я поступаю всегда, прежде чем использовать что-либо оттуда. Читаем же мы исходные тексты VCL! Понятно, что там, в Excel TLB, вы увидите только объявления этих самых свойств и методов, но этого часто вполне достаточно. К тому же я часто отправлял читателя к справке по VBA. Именно благодаря справке по VBA я создал многое из того, что работает сейчас у меня и моих заказчиков. К сожалению, книг, посвященных этой проблеме, не так много. Так что читайте справку, используйте пишущий плейер и поглядывайте в импортированную библиотеку типов.

Нет, это еще не конец!

Писать подобные статьи достаточно сложно (я только сейчас это понял, столкнувшись с этим). Это не запись в блокноте и не записка на клавиатуре коллеги. Одно время я преподавал студентам. Это занятие, оказывается, намного проще. Я никогда не готовился к лекциям, никогда не имел даже малейшего намека на их план, никогда не имел конспекта. У меня было общее направление и память о прошлом занятии. Здесь есть и то, и другое. Но, честное слово, я снова буду тянуть из себя строка за строкой следующую статью о том, как передавать данные в Excel. Вы не заметили, что об этом здесь ни слова?..

Помню, как лет шесть назад я с раздражением ответил одному заказчику, что «Экселов» не пишу. Тогда они только подошли к идее построения большой информационной системы и работали в этом самом «Экселе», выписывая все документы и делая там же отчеты. Естественно, информация в их сети структурирована не была и была похожа на «свалку» из файлов. Сейчас я снова работаю на этого заказчика. Это уже большое предприятие и новая информационная система (за шесть лет многое изменилось). А насчет «Экселов» я не напоминаю. Все, что здесь печатается, все, с помощью чего анализируются какие-то данные, все, что можно представить диаграммой, живущей во времени, - все это в нем, родном. В Microsoft Excel...

С уважением, Евгений Старостин
Автор XL Report - http://www.afalinasoft.com/rus/
25 июля 2000



Свежее
Резервное копирование rsync-ом
DNS Amplification (DNS усиление)
Алгоритм Шинглов — поиск нечетких дубликатов текста
Metasploit Framework. Обзор
Использование CouchDB
-------



 
Copyright © 2003-2009   Frikazoid.
Rambler's Top100