Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Hello, World!
  
 

Фильтр по датам

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  20:51[Войти] | [Зарегистрироваться]

По волнам интеграции… II

Евгений Старостин
дата публикации 25-07-2000 00:00

По волнам интеграции… II

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

Так о чем это я? Ах да, об интеграции этого самого 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, но полной уверенности нет». А дело в том, что «кодогенератор» правильно генерирует свойства _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. Думаю, последнее более правильно. А классики пусть сами разбираются (Канту и Калверт).
Есть еще несколько способов чтения данных из книги, которые, впрочем, я не в силах описать здесь. Один их таких способов, это использование 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/
Специально для Королевства Delphi: HelloWorld.
На все вопросы, связанные с взаимодействием Delphi и Excel,
отвечает служба XL Report Support на Круглом Столе.

Проект - SampleExcel.zip (133 K)




Смотрите также материалы по темам:
[TObject] [TForm] [Exception] [Работа с Excel]

 Обсуждение материала [ 10-03-2010 09:01 ] 18 сообщений
  
Время на сайте: GMT минус 5 часов

Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
Функция может не работать в некоторых версиях броузеров.

Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

 
© При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
Все используемые на сайте торговые марки являются собственностью их производителей.

Яндекс цитирования