Если пустая ячейка в excel для delphi

Здесь, на самом деле, есть подвох. Например, если в Excel для ячейки exSheet.Cells[1, 1] выбран формат «Числовой» и в ней записано какое-нибудь число, то при проверке:

Delphi
1
if exSheet.Cells[1, 1].Value = '' then

возникнет исключение EVariantTypeCastError с сообщением: «Could not convert variant of type (String) into type (Double)» — невозможно преобразовать строку к типу Variant с содержимым типа Double.

Решить эту проблему можно двумя способами:
1.
Вместо вариантного значения ячейки (Value), проверить текстовое представление (Text):

Delphi
1
if exSheet.Cells[1, 1].Text = '' then

2.
Проверить — является ли вариантное значение ячейки пустым вариантом:

Delphi
1
if VarIsEmpty(exSheet.Cells[1, 1].Value) then

Добавлено через 15 минут
Да, кстати, есть очень важная поправка. Предположим, в Excel сброшен параметр: «Показывать нули в ячейках, которые содержат нулевые значения» и при этом в ячейке задано значение = 0 (ноль). Тогда, если судить о пустоте ячейки по пустоте текста, то мы получим неверный результат. Т .к., в этом случае текст ячейки будет пуст, но ячейка пустой не является — в ней записан ноль.

Поэтому, есть только один правильный способ проверки ячейки на пустоту — через проверку на пустоту вариантного значения:

Delphi
1
if VarIsEmpty(exSheet.Cells[1, 1].Value) then

RAD Studio 10.1 Berlin.
Работаю с excel — xlsx. Компонент xlsreadwriteii 5.
Все данные считываются, но возникла проблема. xlsreadwriteii 5 не может определить пустую ячейку.
При задании условия. Например — если в ячейке текст 1, делай одно, а если пустая — делай другое.
Excel — Формат ячеек общий. Числа сохранены как текст.
Так вот текст 1 он (xlsreadwriteii 5) находит, а пустые ячейки нет.

XLS:=TXLSReadWriteII5.Create(nil);
XLS.Filename:=ExtractFilePath(Application.ExeName)+'file.xlsx';
XLS.Read;
XLS[0].CalcDimensions;
//Еще код.....
//Читаем содержимое ячейки в переменную
NUMBER:=XLS[0].AsString[NUMBER_column,row];
//Узнаем длину строки
Form1.Memo1.lines.add('Длина строки NUMBER='+inttostr(Length(NUMBER)));
if (NUMBER='') then Form1.Memo1.lines.add('Пусто');
if (NUMBER='1')   then Form1.Memo1.lines.add('Единица');

Если 1 будет Единица. Тут всё понятно.
Если пустая ячейка, то ничего не будет выведено. Не «ловит.» (
Узнаю длину строки с пустой ячейкой. Длина строки 1. Т.е. ячейка пустая, а длина строки 1!
Такое поведение программы, без изменения excel файла.

Давайте теперь изменим excel файл.
Открываю excel файл. Руками изменяю ячейку с пустой на 1. Сохраняю.
Программа «видит» 1. Что очевидно.
Но теперь программа видит и все пустые ячейки в этом ряду.
Я изменил одну, и это как то повлияло на «видимость» всех пустых ячеек ниже.
Т.е. теперь программа работает корректно. Но я не менял формат ячеек. Смотрю ещё раз — Формат ячеек общий. Числа сохранены как текст.

Руками снова изменяю ячейку с 1 на пустую. Сохраняю.
Программа «видит» и определяет пустую ячейку (и все ниже, разумеется). Длина строки 0. Все как и должно быть.

Итого.
Без ручного открытия excel файла программа ( xlsreadwriteii 5) не определяет пустые ячейки.
С открытием excel файла руками и любым изменением ячейки и сохранением файла, программа определяет все пустые ячейки.

Такая ситуация возникает с xlsreadwriteii 5. С OLE Excel такого не замечал.

Как понять что ячейка пустая (xlsreadwriteii 5)?
Почему происходит такой глюк?

Содержание

  1. Как понять что ячейка пустая (xlsreadwriteii 5)?
  2. Если ячейка пустая excel delphi
  3. «Delphi 4: Автоматизация приложений MS® Office® для эффективного анализа результатов»
  4. Глава 1. Работа с MS Excel.
  5. Статьи, похожие по тематике
  6. Комментарии читателей к данной статье

Как понять что ячейка пустая (xlsreadwriteii 5)?

RAD Studio 10.1 Berlin.
Работаю с excel — xlsx. Компонент xlsreadwriteii 5.
Все данные считываются, но возникла проблема. xlsreadwriteii 5 не может определить пустую ячейку.
При задании условия. Например — если в ячейке текст 1, делай одно, а если пустая — делай другое.
Excel — Формат ячеек общий. Числа сохранены как текст.
Так вот текст 1 он (xlsreadwriteii 5) находит, а пустые ячейки нет.

Если 1 будет Единица. Тут всё понятно.
Если пустая ячейка, то ничего не будет выведено. Не «ловит.» (
Узнаю длину строки с пустой ячейкой. Длина строки 1. Т.е. ячейка пустая, а длина строки 1!
Такое поведение программы, без изменения excel файла.

Давайте теперь изменим excel файл.
Открываю excel файл. Руками изменяю ячейку с пустой на 1. Сохраняю.
Программа «видит» 1. Что очевидно.
Но теперь программа видит и все пустые ячейки в этом ряду.
Я изменил одну, и это как то повлияло на «видимость» всех пустых ячеек ниже.
Т.е. теперь программа работает корректно. Но я не менял формат ячеек. Смотрю ещё раз — Формат ячеек общий. Числа сохранены как текст.

Руками снова изменяю ячейку с 1 на пустую. Сохраняю.
Программа «видит» и определяет пустую ячейку (и все ниже, разумеется). Длина строки 0. Все как и должно быть.

Итого.
Без ручного открытия excel файла программа ( xlsreadwriteii 5) не определяет пустые ячейки.
С открытием excel файла руками и любым изменением ячейки и сохранением файла, программа определяет все пустые ячейки.

Такая ситуация возникает с xlsreadwriteii 5. С OLE Excel такого не замечал.

Как понять что ячейка пустая (xlsreadwriteii 5)?
Почему происходит такой глюк?

Источник

Если ячейка пустая excel delphi

Пожалуйста, выделяйте текст программы тегом [сode=pas] . [/сode] . Для этого используйте кнопку [code=pas] в форме ответа или комбобокс, если нужно вставить код на языке, отличном от Дельфи/Паскаля.

Соблюдайте общие правила форума

Следующие вопросы задаются очень часто, подробно разобраны в FAQ и, поэтому, будут безжалостно удаляться:
1. Преобразовать переменную типа String в тип PChar (PAnsiChar)
2. Как «свернуть» программу в трей.
3. Как «скрыться» от Ctrl + Alt + Del (заблокировать их и т.п.)
4. Как запустить программу/файл? (и дождаться ее завершения)
5. Как перехватить API-функции, поставить hook? (перехват сообщений от мыши, клавиатуры — внедрение в удаленное адресное прстранство)
. (продолжение следует) .
Внимание:
Попытки открытия обсуждений реализации вредоносного ПО, включая различные интерпретации спам-ботов, наказывается предупреждением на 30 дней.
Повторная попытка — 60 дней. Последующие попытки — бан.
Мат в разделе — бан на три месяца.
Полезные ссылки:
MSDN Library FAQ раздела Поиск по разделу Как правильно задавать вопросы

Выразить свое отношение к модераторам раздела можно здесь: Rouse_, Krid

Источник

Сегодня:
000

Последние:
— 09.08, 09:39 / #6696
— 29.03, 23:32 / #6682
— 30 апреля 2012

Сейчас онлайн:
На сайте — 8
На IRC-канале — 2

«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.

    Часть 1. Создание, отображение и удаление экземпляра Excel.

    Собственно, цель этой статьи мне понятна — поделиться своим опытом с народом. Делюсь.

    Итак, зачем нам, лучшим в мире программистам, нужен Excel, порождение «злого» гения Microsoft? Конечно, часто это лишнее — «юзать» Excel для отчетов. Напечатать «платежку» можно и в QReport-е. Но.

    Есть заказчики, готовые отдать «кучищи» денег за то, что они будут знать все и всегда о своем предприятии. Да еще, чтоб это было красиво и со вкусом.

    Приезжает один из моих заказчиков (немец — они повсюду! курорты Испании просто куплены ими — это знаю наверняка) на свое местное предприятие и начинает задавать интересные вопросы. Как трудились за время его отсутствия, сколько продукции выпустили, кому сколько отгрузили, в разных валютах, итого в USD и пр.? А я ему в ответ открываю отчет, неслабый такой, — сводная таблица по движению готовой продукции (посвященные знают, что это 40-ой счет в бухгалтерии). А в ней одних PageField-ов десяток. И на каждый его вопрос я начинаю отвечать не напрягаясь, потихоньку перетаскивая поля таблицы туда-сюда, фильтрую кое-что, строю диаграммы. Что, вы думаете, было потом? Он, как маленький ребенок, сидел за этой сводной таблицей несколько часов, все восхищался. И правильно, наши программисты круче ихних! Заодно и мы спокойно поработали (ему занятие нашлось). О деньгах тут вообще не говорим.

    Потом я ему показал, как эту самую сводную таблицу в Сеть можно опубликовать. Сейчас просит, чтоб ему доступ из Германии сделали к этой табличке. Мы, конечно, рады стараться.

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

    Так как же с ним работать ?

    А просто. Создал «Excel.Application», использовал его по назначению, «убил» и готово. Вот именно об этом я и попытаюсь написать здесь.

    Важно! Параллельно с написанием статьи создавался демо-проект (точнее два — для Delphi 4 и 5), где вы сможете найти весь код примеров статьи. Проект для Delphi 4.0 использует импортированную Type Library из Excel 97. Здесь я использую ранее связывание, ибо CreateOLEObject отлично описал мой любимый классик в «Delphi 4 Unleashed» (мне ли с ним тягаться?). Кроме того, обращайтесь к комментариям в исходных текстах этого проекта. Местами там написано намного понятней, нежели здесь. Delphi 5 содержит более удобный механизм импорта библиотек типов с поддержкой событий и прекрасной генерацией ко-классов. Специально для счастливых обладателей Delphi 5 (я тоже им являюсь) я создал проект, но уже применительно к TexcelApplication (правда ли, что импортированный MS Office есть только в версии Enterprise?). Примеры кода я буду приводить сначала для Delphi 4, потом для Delphi 5. Заранее приношу прощения за дублирование информации в комментариях и в статье — писал сразу везде.

    И еще. Эффективная работа с Excel-ом из Delphi-приложений немыслима без знания одной важной вещи. И имя ей — интерфейс. Мне, конечно, хотелось бы написать о принципах работы с интерфейсами здесь, в этой статье. Более того, я обещал сделать это самой Королеве. Но.

    Мне ли (совсем еще не профессионалу — и это так!) пытаться сделать это лучше, чем классики этой области. Я честно признаюсь, что не смогу этого сделать быстро (в небольшом объеме) и качественно. Поэтому всякого, не знакомого еще с этой областью программирования, я с глубочайшими извинениями отсылаю к книге Чеппела «OLE Inside».

    Достойную помощь (уже применительно к Delphi) может вам оказать «Delphi 4 Unleashed» Чарльза Калверта.

    Создание экземпляра Excel.Application.

    Модуль импортированной Excel TLB (неважно, для D4 или D5) содержит описания всех интерфейсов, которые правильные программисты из Microsoft решили выставить наружу. Там есть все необходимое: типы, константы и интерфейсы. Этого вполне достаточно для работы с Excel-ом из Delphi-приложения (во написал! а что еще нужно-то?). Я создаю Excel для последующего его использования с помощью такого кода:

    procedure TForm1.CreateExcel(NewInstance: boolean);
    var IU: IUnknown;
    isCreate: boolean;
    begin
    // FIXLSApp — private-поле у формы
    // у меня в привычке добавлять букву I для всех интерфейсов
    // понятно почему FI. ?
    if not Assigned(FIXLSApp) then begin // а зачем создавать, если уже есть?
    isCreate := NewInstance or
    (not SUCCEEDED( GetActiveObject(CLASS_Application_, nil, IU) ) );
    if isCreate then
    FIXLSApp := CreateComObject(CLASS_Application_) as _Application
    else
    FIXLSApp := IU as _Application;
    end;
    end;

    Этот достаточно простой код вы найдете практически во всех книгах, посвященных работе с интерфейсами. Как и везде, я напишу, что в результате выполнения этого кода создастся объект COM с CLSID-ом «<00024500-0000-0000-c000-000000000046>» (читайте и перечитывайте Калверта, это не только укрепляет сон!).

    procedure TForm1.CreateExcel(NewInstance: boolean);
    begin
    if not Assigned(IXLSApp) then begin
    FIXLSApp := TExcelApplication.Create(Self);
    if NewInstance then FIXLSApp.ConnectKind := ckNewInstance;
    FIXLSApp.Connect;
    end;
    end;

    В отличие от предыдущих версий, Delphi 5.0 предоставляет более удобный сервис при импорте библиотек типов. Большой шаг вперед — появление класса ToleServer с поддержкой событий. Теперь работа с существующими и создание новых OLE-серверов стала намного удобней. Как видите, не приходится обращаться к низкоуровневым функциям. Впрочем, в Delphi 4.0 тоже существовал этот класс, только не от Borland. Отличная библиотека была создана Бином Ли (Binh Ly) в COM Nodes — это Threading COM Library. С легкой руки Алексея Вуколова (специальное спасибо!) я использовал ее для построения масштабируемых COM-серверов в сервисах WinNT.

    Обращу ваше внимание только на параметр NewInstance. Он позволяет создать новый процесс. Я часто задаю себе вопрос — «А нужен ли NewInstance?». Одна копия процесса, все ж, требует меньше памяти. Но еще чаще я думаю — «Боже, как хорошо я сделал, когда создал новый процесс!». Почему? Если вы не хотите потерять уже открытые, но еще не сохраненные книги, экспериментируя даже с моими примерами, создавайте новый процесс. Печальный опыт научил меня использовать GetActiveObject только в случае полной уверенности в коде, который будет выполняться после. Поэтому, мой вам совет, тестируйте свои приложения только с NewInstance. Или закрывайте важные книги пред этим. Excel — хитрая программа, бывает, улетает в неизвестность, ни слова не сказав. Это не вина Microsoft. Это неудачное расположение звезд.

    Как показать Excel, если он, разумеется, создан ?

    Вот здесь начинаются хитрости. Любой, читавший помощь по Excel VBA, скажет, что достаточно написать FIXLSApp.Visible := true. Не тут-то было. Я делаю так:

    procedure TForm1.ShowExcel;
    begin
    if Assigned(FIXLSApp) then begin // а если он не создан?
    FIXLSApp.Visible[0] := true;
    if FIXLSApp.WindowState[0] = TOLEEnum(xlMinimized) then
    FIXLSApp.WindowState[0] := TOLEEnum(xlNormal);
    FIXLSApp.ScreenUpdating[0] := true;
    end;
    end;

    Зачем здесь условие на минимайз и какой-то ScreenUpdating? Давайте попробуем закомментировать эти строки, остаиви только Visible, запустить проект, создать Excel (кнопка CreateExcel), показать его (кнопка ShowExcel), минимизировать, вернуться в приложение и сделать снова ShowExcel. Да-да, Visible = true переводит фокус в минимизированный Excel, не восстанавливая размеры окна. Это ситуация, с которой я борюсь условием на xlMinimized. Но ScreenUpdating зачем?

    Знающие люди говорят, что это свойство отвечает за перерисовку окон Excel. Это все равно, что DisableControls у TDataSet. Добавляет скорости, если в нем false. И это правда что, если выключить его во время длительных пересчетов, то быстрее пересчитается. Но мы, ведь, не выключали его. Зачем тогда эта строка?

    Делаем так: комметируем эту строку, запускаем демо, CreateExcel, ShowExcel, закрываем его (можно кнопкой с крестиком в правом верхнем углу окна, кому нравится — через меню «Файл/Выход»). Знающие люди скажут, что Excel на самом деле не закрыт. Интерфейс мы не освободили, поэтому в TaskManager мы его и увидим. Итак, Excel по-прежнему у нас в руках. Мы имеем право сделать ему снова Show.

    После такого действия у меня возникает ощущение, что я переплатил за свою видеокарту. Фокус в Excel-е, но я по-прежнему наблюдаю форму демо-проекта. Видимо, программисты из MS не рассчитывали на то, что кто-то закроет Excel, вызванный через создание Excel.Application, а потом захочет увидеть его снова. Но я-то захотел?!

    Свойства Visible, WindowState и ScreenUpdating вызываются с каким-то непонятным индексом массива — 0. В модуле Excel TLB во многих свойствах и методах вы можете встретить параметр или индекс lcid. Не помню, у кого я это прочитал (Калверт или Канту), но с тех пор я туда передаю всегда 0. И все работает. LCID — это что-то насчет локализации. В MSDN написано «Indicates that the parameter is a locale ID (LCID)».

    Спрячем Excel от посторонних глаз!

    На свой процесс я всегда создаю один экземпляр Excel.Application. Уже пару лет все отчеты у меня — это отчеты Excel. Я написал несколько классов, которые мне очень помогают в этом. Сегодня у меня целая «отчетная» подсистема, зашитая в класс и обслуживающая непомерно большие запросы моих пользователей. В промежутках между работой с отчетами нет необходимости «мозолить глаза» лишним окном в TaskBar-е. Вот и прячу я этот Excel. Это очень просто и комментариев, думаю, не требует:

    Delphi 4.0 / 5.0
    procedure TForm1.HideExcel;
    begin
    if Assigned(FIXLSApp) then begin
    FIXLSApp.Visible[0] := false;
    end;
    end;

    Закроем Excel корректно!

    Собственно говоря, при закрытии приложения Excel сам будет закрыт, если вы там не устели чего-нибудь отредактировать. И это правильно. Программисты Borland (Inprise до сих пор мне режет слух, да и некоторым в Inprise, судя по всему, тоже) позаботились об этом. Но я еще с Delphi 3 заимел дурную привычку освобождать все самостоятельно. Освобождать обычным присваиванием в nil (это касается проекта для D4). Труда это не составляет, да и проверка на Assigned удобна. Поэтому, и еще из кое-каких соображений, я делаю так:

    procedure TForm1.ReleaseExcel;
    begin
    if Assigned(FIXLSApp) then begin
    if (FIXLSApp.Workbooks.Count > 0) and (not FIXLSApp.Visible[0]) then begin
    FIXLSApp.WindowState[0] := TOLEEnum(xlMinimized);
    FIXLSApp.Visible[0] := true;
    Application.BringToFront;
    end;
    end;
    FIXLSApp := nil;
    end;

    Ну вот, написал только про nil, а кода — на полстраницы. Опишу ситуацию.

    Вы не запускали новый процесс, вы «законнектились» к уже существовавшему. В нем была открыта книга. Попробуйте: CreateExcel, ShowExcel, HideExcel (имеем право), ReleaseExcel. Если оставить только присваивание в nil, то существовавший процесс не будет выгружен (он же существовал до запуска нашего демо), но будет спрятан от пользователя с его открытой книгой.

    procedure TForm1.ReleaseExcel;
    begin
    if Assigned(IXLSApp) then begin
    if (IXLSApp.Workbooks.Count > 0) and (not IXLSApp.Visible[0]) then begin
    IXLSApp.WindowState[0] := TOLEEnum(xlMinimized);
    IXLSApp.Visible[0] := true;
    if not(csDestroying in ComponentState) then Self.SetFocus;
    Application.BringToFront;
    end;
    end;
    FreeAndNil(FIXLSApp);
    end;

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

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

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

    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. Именно эти данные я и передаю в ячейки области:

    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;

    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;

    В 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 к ней с возможностью выбора варианта работы. Не судите меня строго за то, что главная и единственная форма проекта-примера получится громоздкой и некрасивой. Не это здесь главное. Итак, всегда создавайте по кнопке книгу. Далее нажимайте кнопку, на которую указывает конкретный пример кода, и наблюдайте. Буду рад, если кто-то из читателей создаст более приемлемый демонстрационный проект для этой статьи.

    Часть 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; //

    Дело в том, что строки 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;

    Проект — SampleExcel (в архиве с данной статьёй).

    Часть 5. Передача данных разного типа.

    Как и прежде, я беру проект-пример из предыдущей своей статьи и переделываю его. Напомню, что в нем используется импортированная в Delphi 4 библиотека типов Excel (правда, исходный код я пишу уже в Delphi 5). Свои примеры я тестирую с помощью Excel 2000 с установленным пакетом обновлений Service Release 1. Впрочем, я уверен, что все примеры вы сможете откомпилировать в Delphi 4 и использовать с Excel 97 SR2 и Excel 2000 без SR1. Обращаю внимание на установку SR2 для Excel 97. Это обязательное условие, так как без этого обновления Excel содержит очень неприятную ошибку, периодически возникающую при закрытии книг. Поэтому, пожалуйста, будьте внимательны, господа!

    Какие данные мы используем.

    «Всяческие»! Да, каждый из нас использует в своих приложениях все многообразие типов данных, с которыми способен справиться компилятор и операционная система. В принципе, можно было бы описать решение проблемы для всего этого «многообразия». Но! Я всегда утверждал и буду утверждать, что типы данных, для которых нельзя написать Cell.Value = NewValue, бесполезно использовать в Excel. Я не «влюблен» в Excel. Но я твердо уверен в том, что Excel в сегодняшнем его состоянии — одно из мощнейших средств анализа корпоративных данных. И я до сих пор не могу найти другого применения картинкам в книгах Excel, кроме как наведение красоты. Поэтому я остановлюсь только на способах передачи целых и вещественных чисел, строк, дат и логических значений. В общем, всего того, что так надоело нам в наш быстротекущий media-век.

    Важно! Проект-пример содержит одну форму, в обработчиках OnCreate и OnDestroy которой автоматически создается и освобождается Excel.Application. Причем, для этого я использовал методы из предыдущих примеров — CreateExcel, ShowExcel и ReleaseExcel. Особое внимание хочу обратить на ReleaseExcel, с помощью которого освобождается интерфейс Excel.Application. Если же необходимо закрыть Excel, вызывайте перед освобождением интерфейса метод Quit этого интерфейса (у себя я закомментировал эту строку). На форму я поместил таблицу (TTable) из DBDEMOS — CUSTOMER.DB. Чтобы видеть хоть что-то, я использовал для этой таблицы DBGrid и навигатор. В правой части формы вы увидите кнопку со странным названием «Send data» и группу переключателей под ней. С помощью этой группы вы сможете выбрать один из рассмотренных в этой статье вариантов передачи данных в Excel (данные я беру из вышеназванной таблицы). После выбора варианта передачи и нажатия кнопки в Excel создается новая книга на основе шаблона Test.xls — книги, которую я прилагаю вместе с проектом. В эту книгу из таблицы переносятся значения полей из всех записей. При переносе я измеряю количество миллисекунд, затраченных на этот перенос, и помещаю это количество в ячейку A1 созданной книги.

    И еще. При создании примера я старался избегать лишнего кода, который в любом другом случае добавил бы законченность алгоритмам. Меня стоило бы основательно поругать за то, что я не запоминаю закладок при проходе по таблице, использую значения полей, забывая о DisplayText, и просто переношу значения в книгу без какого-либо форматирования ячеек. Наверняка, вы найдете еще несколько моментов, которые я совсем упустил из вида. Попросту, красота кода уступила свое место желанию сосредоточиться на единственной цели — эффективной передаче данных. Единственное, что я сделал, так это избавился от возможности вмешательства пользователя в процесс переноса данных, отключив (и включив затем) у Excel свойства Interactive и ScreenUpdating, а также вызвав DisableControls для набора данных.

    Какие же варианты я предложу на суд читателя? Вариантов и возможностей передачи данных в Excel существует достаточно много — от очень «заумных» (BIFF) до экзотических (сохраним в текстовый файл и затем его откроем). Все они имеют какие-то свои достоинства и недостатки. В этой статье я расскажу об одних из самых простых и эффективных решениях этой проблемы (название раздела все-таки обязывает — HelloWorld!). Правда, первый описанный здесь способ, у меня ничего кроме зубной боли не вызывает. Итак.

    Не делайте так — клиническая смерть!

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

    Первый переключатель на форме (с заголовком «Value :=») скрывает за собой вызов процедуры ToNewValue. Вот ее исходный код:

    procedure TForm1.ToNewValue(ISheet: IxlWorksheet);
    var Row, Column, i: integer;
    begin
    tblCust.First;
    Row := StartRow;
    tblCust.First;
    while not tblCust.EOF do begin
    Column := StartColumn;
    for i := 0 to tblCust.Fields.Count — 1 do begin
    ISheet.Cells.Item[Row, Column].Value := FieldToVariant(tblCust.Fields[i]);
    Inc(Column);
    end;
    Inc(Row);
    tblCust.Next;
    end;
    end;

    Что в ней особенного? Да, это обычный проход по всей таблице (First; while not EOF do Next;) и по всем ее полям (вложенный for). Но! Во-первых, в этом примере я всегда начинаю переносить данные с ячейки, определенной константами StartRow и StartColumn. Во-вторых, ожидаемый вами оператор присваивания «Cell.Value := Field.Value» я заменил на «Cell.Value := FieldToVariant(Field)». То есть, в отличие от классического, «из учебников», примера я использую свою функцию получения вариантного значения поля.

    Если присмотреться к исходному тексту функции FieldToVariant,

    function FieldToVariant(Field: TField): OLEVariant;
    begin
    Result := »;
    case Field.DataType of
    ftString, ftFixedChar, ftWideString, ftMemo, ftFmtMemo: Result := »» + Field.AsString;
    ftSmallint, ftInteger, ftWord, ftLargeint, ftAutoInc: Result := Field.AsInteger;
    ftFloat, ftCurrency, ftBCD: Result := Field.AsFloat;
    ftBoolean: Result := Field.AsBoolean;
    ftDate, ftTime, ftDateTime: Result := Field.AsDateTime;
    end;
    end;

    то можно разглядеть причину. Кроме достаточно глупых «AsInteger», «AsFloat» и пр. я добавляю в начало значений строковых полей одиночную кавычку. Вы, наверняка, помните, что ввод в формулу ячейки первым символом одиночной кавычки заставляет Excel принимать остальные символы как текст. Но, это касается формул ячеек, а не их значений!? Попробуйте убрать добавление этой кавычки и перекомпилировать проект. Конечно, и в этом варианте все будет работать. Но (предлагаю кощунственный метод) отредактируйте поле «Company» в первой записи таблицы, введя туда строку «3/7». Не увидите ли вы в полученной книге вместо этой строки дату или результат деления (зависит от языковых настроек ОС)? Столь же некорректный результат будет получен и при попытке передачи строки «0001», которая будет воспринята как число 1. Благо, одиночная кавычка в начале строки решает эту проблему даже при присваивании в Value (а не в Formula).

    Впрочем, я не намерен долго останавливаться на этом варианте. Все дело в значении ячейки A1 в полученной книге. На моем компьютере перенос всей таблицы занял более 4 секунд. И это на не более полусотне записей. А что было бы, если бы количество записей перевалило за пару десятков тысяч? Кстати, повторные запуски показали колебание этого времени в пределах от трех до пяти секунд. Думаю, что такие колебания были связаны только с файловым кэшем ОС. Так что, «Не делайте так!»

    Больному уже лучше. Правда, он все еще в реанимации.

    На что же уходит время в предыдущем варианте? Все просто! Львиная доля времени уходит на вызовы интерфейсов внешнего COM-сервера. И, не смотря на то, что мы используем ранее связывание с библиотекой типов, это так. Еще мой любимый классик (Калверт, знаете ли) писал о нетерпимости к вызовам интерфейсов внешних OLE-серверов в больших циклах. Как видите, классик прав.

    Наша задача — избавиться от вызова Cell.Value в цикле. И это решаемо с помощью вариантных массивов. Вот так:

    procedure TForm1.ToVarArray(ISheet: IxlWorksheet);
    var Row, Column, i: integer;
    IR1, IR2: IxlRange;
    Arr: OLEVariant;
    begin
    Arr := VarArrayCreate([1, tblCust.RecordCount, 1, tblCust.Fields.Count], varVariant);
    Row := 1;
    tblCust.First;
    while not tblCust.EOF do begin
    Column := 1;
    for i := 0 to tblCust.Fields.Count — 1 do begin
    Arr[Row, Column] := FieldToVariant(tblCust.Fields[i]);
    Inc(Column);
    end;
    Inc(Row);
    tblCust.Next;
    end;
    IDispatch(IR1) := ISheet.Cells.Item[StartRow, StartColumn];
    IDispatch(IR2) := ISheet.Cells.Item[StartRow + tblCust.RecordCount — 1,
    StartColumn + tblCust.Fields.Count — 1];
    ISheet.Range[IR1, IR2].Value := Arr;
    end;

    Здесь я использую вариантный массив Arr, который предварительно создается с размерами таблицы (количество записей на количество полей). Благо Microsoft построила очень четкую схему работы с вариантными массивами и интерфейсами, их «понимающими» (этим и пользуюсь). Из кода видно, что я по-прежнему прохожу всю таблицу, запоминая в элементах массива значения полей, полученных из вышеописанной функции FieldToVariant. Мы, ведь, снова используем варианты, и проблема строки «3/7» остается. Последние три строки процедуры позволяют получить верхнюю левую и нижнюю правую ячейки области, в которую будут перенесены данные. А, затем, одним присваиванием в «Область.Value» я переношу данные из массива в ячейки этой области. Хорош способ, не правда ли? Код максимально прост. Время, полученное в ячейке A1 на порядок меньше. Правда, есть несколько проблем.

    Главное, что бросилось бы в глаза опытного Delphi-разработчика, это создание массива в начале процедуры. Известно ли количество записей SQL-запроса после его открытия? Не всегда (FechAll). Хорошо, можно создать пустой массив и делать ему VarArrayRedim. Вряд ли! Так как количество записей — есть первое измерение вариантного массива (необходимо здесь тире или нет. ). А я не нашел до сих пор способа изменить первую размерность вариантного массива при наличии второй. Может, кто подскажет начинающему (про начинающего — правда). Возможно, было бы правильно создать массив массивов (понимаете о чем я?). Но, что-то не заладилось там, Наверху. Поэтому такое решение не проходит. Точнее проходит, но как-то не очень хорошо — попробуйте!

    Тем не менее, этот вариант вполне «живуч» при осторожном его использовании и на небольших объемах данных. Скорость нормальная, проблем с «3/7» нет. В общем, больной будет жить!

    Часть 6. Передача данных используя буфер обмена и DDE.
    Clipboard.

    Совсем недавно мне пришло очередное гневное послание на тему буфера обмена — Clipboard. Объясню. XL Report очень долго использовал только буфер обмена для передачи данных из приложения в Excel. Дело в том, что при таком варианте (а я его здесь опишу) достигается практически максимальная скорость переноса данных. Дело в том, что в буфер обмена «кладется» длинная строка, содержащая строковые значения полей набора данных (AsString), разделенные символом табуляции. Записи отделяются друг от друга переводом строки (#10). Собственно, этот формат известен в научных кругах как CSV (разделитель между значениями). Долгое время это меня устраивало, пока XL Report использовался только нашими разработчиками и ограниченным кругом клиентов фирмы. Но тут мы решили выложить это решение в Сеть. И.

    Каюсь. Я никак не беспокоился за сохранность содержимого буфера обмена. Понятное дело, это не очень правильно. Но, так было. Так есть и сейчас. Правда, по другим причинам. Для того, чтобы «выжать» из Excel максимальное быстродействие, приходится использовать определенные методы и свойства его интерфейсов. А их использование не оставляет ничего, кроме как уничтожение содержимого буфера обмена. В общем, сейчас я покажу максимально возможное (в этой статье. ) решение по переносу данных — CSV. Итак:

    procedure TForm1.ToCSV(ISheet: IxlWorksheet);
    var i: integer;
    IR1, IR2: IxlRange;
    Buff: String;
    begin
    Buff := »;
    tblCust.First;
    while not tblCust.EOF do begin
    for i := 0 to tblCust.Fields.Count — 1 do begin
    Buff := Buff + FieldToStr(tblCust.Fields[i]);
    if i

    Как я и писал выше, в строковый буфер Buff собирается вся таблица. Строковые значения полей я разделяю символом табуляции, а в «конце» записи добавляю перевод строки. Все значения я заключаю дополнительно в двойные кавычки. Затем вызовом процедуры BufferToClipboard я помещаю содержимое этой переменной в буфер обмена и делаю «хитрый» вызов PasteSpecial для области, в которую будут помещены данные. Вот и все?! Нет, есть еще проблемы.

    Во-первых, процедура BufferToClipboard — вещь не стандартная. Она создана как альтернатива методу SetTextBuf класса TClipboard. Наверняка, все знают что в VCL доступна глобальная переменная Clipboard, экземпляр класса TClipboard, инкапсулирующего свойства и методы доступа к этому самому буферу обмена. И, собственно, вызов SetTextBuf позволяет поместить строку в буфер. Но!

    SetTextBuf помещает в буфер обмена текст в формате CF_TEXT — обычный текст с однобайтовым представлением символов, что не есть хорошо. Точнее, это совсем не хорошо, если вы работаете с «русскими буквами» на разных операционных системах от MS, причем, разных с точки зрения локализации. Именно тогда и возникают у пользователей вопросы при попытке прочитать некий набор «закорючек», отдаленно напоминающих письмена племени зибару. Поэтому я предпочитаю UNICODE, вставка которого в буфер обмена и реализована в этой процедуре. Ее текст я не буду приводить в этой статье, так как это потребует лишних для этого объяснений. Сама процедура присутствует в исходных текстах проекта-примера. Поэтому знатоков этого дела отсылаю к ним, а таким начинающим, как и я, просто рекомендую ее использовать в последующих своих разработках (при необходимости).

    UNICODE — это первая проблема, которая была решена. Но при использовании CSV есть и другие. И, главная, из них — «3/7». Без вмешательства в содержимое поля (аналогично добавлению одиночной кавычки при вариантах) ее нельзя обойти никак. Я добавляю пробел. Да, тривиального пробела вполне хватает для решения этой проблемы. И замеченная вами функция FieldToStr как раз это и делает.

    По поводу «хитрого» PasteSpecial. При «правильном» вызове метода PasteSpecial интерфейса Range я столкнулся с неразрешимыми проблемами. Поэтому я привел к варианту область и заставил ОС и сам Excel самостоятельно разбираться с тем, что и с какими параметрами я вызываю. Часто, при разработке с библиотеками типов от MS такой ход экономит время и, главное, сохраняет здоровье и нормальное состояние нервной системы. Не используйте его все время, но и не пренебрегайте им. Итак, при использовании CSV и буфера обмена мы достигли «громаднейшей» скорости передачи данных, обойдя при этом несколько проблем.

    Недостатки, которые заметят критики и просто опытные разработчики. Переданное строковое значение «0001» предстает перед изумленным пользователем числом 1. Это можно обойти только через предварительное форматирование конкретной ячейки в текстовый формат. Подчеркиваю, предварительно, то есть перед переносом данных. И, по-прежнему, содержимое буфера обмена не сохраняется. Более того, оно затем нагло очищается вызовом Clipboard.Clear. «Мы его теряем!»

    Думаю, вы уже заметили, что один из переключателей вариантов имеет название, состоящее из зловещего сочетания букв — DDE. Первое, что вы можете почувствовать при виде этих букв, — это застоялый запах плесени и старения. Да, DDE — пережиток старого доброго времени (а, кажется, это было совсем недавно — «как молоды мы были»), времени DOS-резидентов, командной строки, капитана Нортона и первых (не смотря на их номер — 3.0) «форточек». Черт, тут жизнь уже проходит, а DDE все еще жив. И слава Богу! Ведь, мы решим «главную» проблему — сохранение содержимого буфера обмена. Вот так:

    procedure TForm1.ToDDE(ISheet: IxlWorksheet);
    var xlDDE: TDDEClientConv;
    i: integer;
    IR1, IR2, IRange: IxlRange;
    Buff: string;
    begin
    Buff := »;
    tblCust.First;
    while not tblCust.EOF do begin
    for i := 0 to tblCust.Fields.Count — 1 do begin
    Buff := Buff + FieldToStr(tblCust.Fields[i]);
    if i

    Все реже я встречаю книги, где было бы описано взаимодействие приложений посредством DDE. Многие и вовсе думают, что DDE уже давно умер. Но, нет, «жив курилка». И, более того, это одно из самых быстрых решений по передаче данных в Excel. И, что самое интересное, Excel по-прежнему поддерживает DDE и все DDE-команды, которые стали доступны со времен версии 4.0. И по-прежнему будет несказанно рад тот счастливчик, который обнаружит в непроходимых джунглях Сети файл под названием «excel40macro.hlp», ибо там он найдет все, что нужно для быстрой и качественной работы с Excel. Это вам не интерфейсы «пользовать». Но, вернемся к исходному тексту.

    В этой процедуре я, как было описано выше, прохожу по всей таблице, собирая в строковый буфер значения полей, разделенные символом табуляции. Записи же разделяются символом перевода строки. Затем я получаю интерфейс на область, куда необходимо поместить данные из таблицы. Это первая часть кода. Далее — самое интересное.

    Переменная xlDDE используется для доступа к Excel посредством DDE. Если опустить теорию, напрямую обратившись к практике, то можно увидеть следующий алгоритм ее использования. Во-первых, создается экземпляр класса TDDEClientConv. Во-вторых, вызовом SetLink происходит соединение через DDE с запущенным Excel. SetLink возвращает true, если это соединение успешно. А далее происходит вызов метода PokeData, одним из параметров которого является строковый буфер Buff. Второй параметр — это адрес области в формате R1C1. Вот и все. Думаю, это работает и у вас. Скорость сравнима с CSV через буфер обмена. Плюс, здесь буфер обмена мы совсем не используем. Но!

    Попробуйте несколько раз подряд быстро нажать кнопку «Send data» с этим вариантом передачи данных. У меня Excel просто виснет. Точнее, он что-то делает, загружая на все сто процессор. Благо, Windows NT безболезненно позволяет снять задачу. Тем, у кого Win9x, повезло меньше и, видимо, им придется перезагрузиться. Почему это происходит? Меня смутила вот эта строка в реализации TDDEClientConv:

    hdata := DdeClientTransaction(Pointer(hszDat), DWORD(-1), FConv, hszItem,
    FDdeFmt, XTYP_POKE, TIMEOUT_ASYNC, nil);

    Точнее параметр TIMEOUT_ASYNC, позволяющий передавать данные асинхронно. Вот и сыплется Excel, не выдерживая реализации DDE-клиента от Borland/Inprise. Впрочем, Borland тоже не причем. Для себя я создал потомка класса TDDEClientConv, добавив ему новый метод xlPokeData, в котором просто заменил эту строку на:

    Const xddeTransactionTimeOut = 100000;
    .
    hdata := DdeClientTransaction(Pointer(hszDat), DWORD(-1), Conv, hszItem,
    CF_XLTABLE, XTYP_POKE, xddeTransactionTimeOut, nil);
    .

    И все в порядке — работает.

    Я не стану описывать подробности взаимодействия процессов через DDE по, думаю, понятным вам причинам. Все это давно описано в классике жанра — документации по Delphi. Тем не менее, по-прежнему остается проблема со строкой «0001». И останется она при этом варианте нерешенной, так как здесь используются строковые представления всех значений полей. И где же выход, спросите вы? Выход прост.

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

    Чтобы в нем использовались native представления данных, где число было бы не строкой, а привычным набором из четырех (восьми) байт. Ведь, напомню, целые и вещественные числа и даты — это, в конечном счете, вещественное число в Excel. Для себя я решение нашел — Fast Table Format. Но это уже не из этой статьи.

    Демо-Проект — SampleExcel3 (в архиве с данной статьёй).

    Часть 7: Пример обмена данными с Excel используя VCL и OLE.

    Автор: Bird Feniks.

    В Delphi 5 и выше, для обмена данными между Вашим приложением и Excel можно использовать компонент TExcelApplication, доступный на Servers Page в Component Palette.

    Создаем новый проект. На форму кладем TStringGrid, заполняем его некоторыми данными и добавляем две кнопки, с названиями «To Excel» и «From Excel». Так же на форму кладем компонент TExcelApplication и в свойстве «Name» указываем «XLApp», а свойство «ConnectKind» меняем на «ckNewInstance».

    Когда нам необходимо работать с Excel, то обычно мы открываем ExcelApplication, затем открываем WorkBook и в конце используем WorkSheet.

    Итак, несомненный интерес представляет для нас листы (WorkSheets) в книге (WorkBook). Давайте посмотрим как всё это работает.

    Посылка данных в Excel.

    Это можно сделать с помощью следующей процедуры :

    procedure TForm1.BitBtnToExcelOnClick(Sender: TObject);
    var
    WorkBk : _WorkBook; // определяем WorkBook
    WorkSheet : _WorkSheet; // определяем WorkSheet
    I, J, K, R, C : Integer;
    IIndex : OleVariant;
    TabGrid : Variant;
    begin
    if GenericStringGrid.Cells[0,1] <> » then
    begin
    IIndex := 1;
    R := GenericStringGrid.RowCount;
    C := GenericStringGrid.ColCount;
    // Создаём массив-матрицу
    TabGrid := VarArrayCreate([0,(R — 1),0,(C — 1)],VarOleStr);
    I := 0;
    // Определяем цикл для заполнения массива-матрицы
    repeat
    for J := 0 to (C — 1) do
    TabGrid[I,J] := GenericStringGrid.Cells[J,I];
    Inc(I,1);
    until
    I > (R — 1);

    // Соединяемся с сервером TExcelApplication
    XLApp.Connect;
    // Добавляем WorkBooks в ExcelApplication
    XLApp.WorkBooks.Add(xlWBatWorkSheet,0);
    // Выбираем первую WorkBook
    WorkBk := XLApp.WorkBooks.Item[IIndex];
    // Определяем первый WorkSheet
    WorkSheet := WorkBk.WorkSheets.Get_Item(1) as _WorkSheet;
    // Сопоставляем Delphi массив-матрицу с матрицей в WorkSheet
    Worksheet.Range[‘A1’,Worksheet.Cells.Item[R,C]].Value := TabGrid;
    // Заполняем свойства WorkSheet
    WorkSheet.Name := ‘Customers’;
    Worksheet.Columns.Font.Bold := True;
    Worksheet.Columns.HorizontalAlignment := xlRight;
    WorkSheet.Columns.ColumnWidth := 14;
    // Заполняем всю первую колонку
    WorkSheet.Range[‘A’ + IntToStr(1),’A’ + IntToStr(R)].Font.Color := clBlue;
    WorkSheet.Range[‘A’ + IntToStr(1),’A’ + IntToStr(R)].HorizontalAlignment := xlHAlignLeft;
    WorkSheet.Range[‘A’ + IntToStr(1),’A’ + IntToStr(R)].ColumnWidth := 31;
    // Показываем Excel
    XLApp.Visible[0] := True;
    // Разрываем связь с сервером
    XLApp.Disconnect;
    // Unassign the Delphi Variant Matrix
    TabGrid := Unassigned;
    end;
    end;

    Получение данных из Excel.

    Это можно сделать с помощью следующей процедуры :

    procedure TForm1.BitBtnFromExcelOnClick(Sender: TObject);
    var
    WorkBk : _WorkBook;
    WorkSheet : _WorkSheet;
    K, R, X, Y : Integer;
    IIndex : OleVariant;
    RangeMatrix : Variant;
    NomFich : WideString;
    begin
    NomFich := ‘C:MyDirectoryNameOfFile.xls’;
    IIndex := 1;
    XLApp.Connect;
    // Открываем файл Excel
    XLApp.WorkBooks.Open(NomFich,EmptyParam,EmptyParam,EmptyParam,EmptyParam, EmptyParam,EmptyParam,EmptyParam,EmptyParam,
    EmptyParam,EmptyParam,
    EmptyParam,EmptyParam,0);
    WorkBk := XLApp.WorkBooks.Item[IIndex];
    WorkSheet := WorkBk.WorkSheets.Get_Item(1) as _WorkSheet;
    // Чтобы знать размер листа (WorkSheet), т.е. количество строк и количество
    // столбцов, мы активируем его последнюю непустую ячейку
    WorkSheet.Cells.SpecialCells(xlCellTypeLastCell,EmptyParam).Activate;
    // Получаем значение последней строки
    X := XLApp.ActiveCell.Row;
    // Получаем значение последней колонки
    Y := XLApp.ActiveCell.Column;
    // Определяем количество колонок в TStringGrid
    GenericStringGrid.ColCount := Y;
    // Сопоставляем матрицу WorkSheet с нашей Delphi матрицей
    RangeMatrix := XLApp.Range[‘A1’,XLApp.Cells.Item[X,Y]].Value;
    // Выходим из Excel и отсоединяемся от сервера
    XLApp.Quit;
    XLApp.Disconnect;
    // Определяем цикл для заполнения TStringGrid
    K := 1;
    repeat
    for R := 1 to Y do
    GenericStringGrid.Cells[(R — 1),(K — 1)] := RangeMatrix[K,R];
    Inc(K,1);
    GenericStringGrid.RowCount := K + 1;
    until
    K > X;
    // Unassign the Delphi Variant Matrix
    RangeMatrix := Unassigned;
    end;

    Ниже представлен пример создания новой таблицы в Excel 2000:

    uses
    ComObj, ActiveX;

    var
    Row, Col: integer;
    DestRange: OleVariant;
    Excel: Variant;

    begin
    Excel := CreateOleObject(‘Excel.Application.9’);
    Excel.Visible := True;
    Excel.WorkBooks.Add; //Создать новую таблицу

    //Можно помещать текст и значения в диапазон ячеек
    //Поместить слово тест в диапазон ячеек
    Excel.ActiveSheet.Range[‘A2’, ‘B3’].Value := ‘Тест’;
    //Или число
    Excel.ActiveSheet.Range[‘A4’, ‘B5’].Value := 42;

    //А вот так задаётся формула
    Excel.ActiveSheet.Range[‘A10’, ‘A11’].Formula := ‘=RAND()’;

    //Можно задавать номера ячеек и столбцов
    Excel.ActiveSheet.Cells.Item[1, 1].Value := ‘Первая ячейка’;

    Row:=1;
    Col:=3;
    Excel.ActiveSheet.Cells.Item[Row, Col].Value := ‘Другая ячейка’;

    //Можно скопировать данный из одного диапазона ячеек в другой
    DestRange := Excel.Range[‘D6’, ‘F10’];
    Excel.Range[‘A1’, ‘C5’].Copy(DestRange);

    //Можно задавать параметры шрифта в определённой ячейке
    Excel.Range[‘A2’, ‘A2’].Font.Size := 20;
    Excel.Range[‘A2’, ‘A2’].Font.FontStyle := ‘Bold’;
    Excel.Range[‘A2’, ‘A2’].Font.Color := clFuchsia;
    Excel.Range[‘A2’, ‘A2’].Font.Name := ‘Arial’;

    //Можно ещё и так изменить цвет диапазона ячеек
    Excel.Range[‘B2’, ‘C6’].Interior.Color := RGB(223, 123, 123);

    Далее представлен пример открытия и закрытия таблицы:

    uses
    ComObj, ActiveX;

    var
    Excel: Variant;
    WBk : OleVariant;
    SaveChanges: OleVariant;

    begin
    Excel := CreateOleObject(‘Excel.Application.9’);
    Excel.Visible := True;

    //Открыть существующую таблицу
    WBk := Excel.WorkBooks.Open(‘C:Test.xls’);

    WBk.Close(SaveChanges := True);
    Excel.Quit;

    Статья добавлена: 8 мая 2005

    Зарегистрируйтесь/авторизируйтесь,
    чтобы оценивать статьи.

    Статьи, похожие по тематике

    Для вставки ссылки на данную статью на другом сайте используйте следующий HTML-код:

    Ссылка для форумов (BBCode):

    Быстрая вставка ссылки на статью в сообщениях на сайте:
    <> (буква a — латинская) — только адрес статьи (URL);
    <<статья:10>> — полноценная HTML-ссылка на статью (текст ссылки — название статьи).

    Поделитесь ссылкой в социальных сетях:

    Комментарии читателей к данной статье


    Репутация: нет

    Репутация: нет

    Репутация: +40

    Репутация: нет

    Оставлять комментарии к статьям могут только зарегистрированные пользователи.

    Источник

    Adblock
    detector

    >
    Проблема при проверке значения ячейки в Excel через Delphi

    • Подписаться на тему
    • Сообщить другу
    • Скачать/распечатать тему



    Сообщ.
    #1

    ,
    03.07.08, 08:12

      Junior

      *

      Рейтинг (т): нет

      Добрый день! Столкнулся с такой проблемой мне нужно проверить значение ячейки в Excel, а именно какой номер квартала находиться в нужной мне ячейке, так же нужно проверить не пустая ли это ячейка написал следующий код:

      if (Excel.ActiveWorkBook.WorkSheets[1].Cells[i+6, 10].value<>1) and (Excel.ActiveWorkBook.WorkSheets[1].Cells[i+6, 10].value<>2) and (Excel.ActiveWorkBook.WorkSheets[1].Cells[i+6, 10].value<>3) and (Excel.ActiveWorkBook.WorkSheets[1].Cells[i+6, 10].value<>4) and (Excel.ActiveWorkBook.WorkSheets[1].Cells[i+6, 10].value<>») then

      Выдает следующую ошибку «Could not convert variant of type (String) into type double «

      Как можно еще сделать подобную проверку, может у меня не правильный подход к этому?!

      Так есть вариант написать такую проверку в макросе в отдельном файле, но возникают такие вопросы:
      1. Как запустить макрос из Delphi?
      2. Как узнать что макрос закончил свою работу?

      Сообщение отредактировано: bas_sanya — 03.07.08, 08:15


      vladimir74



      Сообщ.
      #2

      ,
      03.07.08, 08:32

        а сразу вынимать значения как double не пробывал? Еще лучше если эти значения часто проверяются, сначала перегнать значения во временные переменные double при этом проверить чтоб там небыло строк, а потом сравнивать переменные….

        0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)

        0 пользователей:

        • Предыдущая тема
        • Delphi: Система, Windows API
        • Следующая тема

        [ Script execution time: 0,0182 ]   [ 16 queries used ]   [ Generated: 15.04.23, 09:50 GMT ]  

        уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

        Сегодняшняя статья блога  будет целиком посвящена работе с Excel Range или, говоря другими словами — работе с диапазонами ячеек Excel.

        Про работу с этими объектами я уже вкратце говорил, а сегодня хотел бы поделиться с вами более полной информацией. Итак, начнем с самого простого.

        1. Что такое Range и как его получить?

        Согласно официальному определению Microsoft, Range :

        представляет собой ячейки, строки, столбцы, набор ячеек, содержащих один или более смежных блоков ячеек, или 3-D диапазон.

        Однако это определение не исключает того, что объектом Range может выступать и одна ячейка (Cell) листа.  Таким образом, чтобы получить в свое распоряжение объект Range, можно выполнить следующие операции c объектом Excel в Delphi:

        var  MyRange: OLEVariat;
        begin
        {объект Range, состоящий из одной ячейки}
          MyRange:=MyExcel.ActiveWorkBook.ActiveSheet.Range['A1'];
        {объект Range в виде строки из четырех ячеек}
          MyRange:=MyExcel.ActiveWorkBook.ActiveSheet.Range['A1:D1'];
        {объект Range в виде столбца из четырех ячеек}
          MyRange:=MyExcel.ActiveWorkBook.ActiveSheet.Range['A1:A4'];
        {объект Range в виде таблицы 4х4 ячейки}
          MyRange:=MyExcel.ActiveWorkBook.ActiveSheet.Range['A1:D4'];
        end;

        Если Вам неудобно в какой-либо ситуации использовать буквенные обозначение ячеек или Вы привыкли до этого момента иметь дело только с отдельными ячейками (Cells), то объект Range можно получить например вот так:

        var  MyRange: OLEVariat;
             Cell_1, Cell2: OLEVariant;
        begin
        {получаем ссылку на объект Cells, соответствующей ячейке A1}
          Cell_1:=MyExcel.ActiveWorkBook.ActiveSheet.Cells[1,1];
        {получаем ссылку на объект Cells, соответствующей ячейке C5}
          Cell_2:=MyExcel.ActiveWorkBook.ActiveSheet.Cells[5,3];
        {получаем объект Range размером 3х5}
          MyRange:=MyExcel.ActiveWorkBook.ActiveSheet.Range[Cell_1, Cell_2]
        end;

        Какой из способов Вы будете использовать в Delphi — не важно, так как результат будет один и тот же. Получив в свое распоряжение диапазон Вы можете использовать его далее, например, для того, чтобы установить вид его границ.

        2. Свойства объекта Excel Range.

        Рассмотрим основные свойства объекта Range и их применение работе в Excel в Delphi.

        Вначале перечислю свойства, которые будут далее рассмотрены в статье.

        Свойство Описание
        Formula Возвращает или помещает в диапазон формулу
        Value Возвращает или устанавливает значение для диапазона
        Text возвращает текст из ячейки
        Column Возвращает номер первого столбца в первой области, в указанном диапазоне
        Columns возвращает объект Range, представляющий собой один столбец из всего диапазона
        Comment Возвращает объект Comment для Range. В данном случае Range должен определять одну ячейку.
        Address Возвращает реальный адрес диапазона Range

        Formula

        Возвращает или помещает в диапазон формулу.

        Value

        Возвращает или устанавливает значение для диапазона.Свойство Value замечательно тем, что с помощью него можно записать в ячейки абсолютно любые данные, особо не задумываясь о формате данных. Например, запишем в ячейки диапазона строку, число типа integer и число типа single: чтобы каждый раз не повторяться в листингах и не писать одни и те же элементы по 100 раз, будем считать, что в переменной Sheet уже содержится ссылка на активный лист (ActiveWorkSheet) активной книги (ActiveWorkBook) Excel (MyExcel)

        var i:integer;
            s: single;
            str: string;
            Sheet: OLEVariant;
        begin
          i:=100;
          s:=2.12;
          str:='Hello World!';
          Sheet.Range['A1'].Value:=i;
          Sheet.Range['A2'].Value:=s;
          Sheet.Range['A3'].Value:=str;
        end;

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

        Если Вы хотите записать в весь диапазон Range одно и то же значение, то просто выполните:

        Sheet.Range['A1:A10'].Value:=str;

        и получите одну и ту же строку «Hello World!» в десяти ячейках Excel, но такие операции очень редко необходимы при работе с Excel в Delphi. Зато очень часто необходимо воспользоваться другой стороной свойства Value — прочитать большой объем данных из книги Excel за один прием и получить весь массив данных в Delphi. Операция чтения данных из Excel в Delphi более проста, чем Вам может показаться на первый взгляд. Проведем обратную операцию — прочитаем данные из Excel:

        var  val: Variant;
             Sheet: OLEVariant;
             i:integer;
        begin
          Val:=Sheet.Range['A1:A3'].Value;
          for i:=1 to 3 do
            ShowMessage(val[i,1]);
        end;

        Если Вам необходимо читать/писать большое количество данных, то наиболее быстрым способом работы с данными будет использование вариантных массивов. О том, как использовать вариантные массивы при работе с Excel в Delphi рассказывается в статье «Быстрая обработка данных Excel в Delphi.«

        Как видите здесь мы за один прием прочитали данные сразу из трех ячеек Excel и отобразили их в сообщении. Этот прием чтения на порядок более скоростной, чем, например чтение содержимого каждой ячейки (Cells) в отдельности.

        Text

        Ещё одно простенькое свойство объекта Range — возвращает текст из ячейки. Самое главное отличие от свойства Value Text возвращает string только для чтения и использовать это свойство для чтения большого объема данных, как в предыдущем примере — ни в коем случае нельзя, так как переменная Val вернет значение Null.

        Column

        Возвращает номер первого столбца в первой области, в указанном диапазоне. Свойство только для чтения.

        Чтобы продемонстрировать свойство в действии, давайте создадим такие диапазоны как показано на рисунке:

        Excel_RangeТо есть каждый из диапазонов Range будет содержать по две несвязанные друг с другом области (Area). Причем первая область диапазона Range будет начинаться в столбце А, а первая область второго диапазона (Range 2) — в столбце B.

        После того, как диапазоны будут созданы — посмотрим, что вернет нам свойство Column для каждого из диапазонов.

        Листинг процедуры создания двух несвязных диапазонов Range следующий:

        var Range1,Range2,BigRange: OLEVariant;
        begin
        {создаем первый диапазон}
          Range1:=Sheet.Range['A1:C4'];
          Range2:=Sheet.Range['E6:H9'];
          BigRange:=Sheet.Range[Range1,Range2];
          ShowMessage(IntToStr(BigRange.Column)); //показываем значение свойства
        {создаем второй диапазон}
          Range1:=Sheet.Range['B7:C13'];
          Range2:=Sheet.Range['E1:H3'];
          BigRange:=Sheet.Range[Range1,Range2];
          ShowMessage(IntToStr(BigRange.Column)); //показываем значение свойства
        end;

        Так, в случае с первым Range Column вернет нам значение 1, а для второго Range — значение 2.

        Columns

        В отличие от предыдущего свойства, Columns возвращает не простое число, а объект Range, представляющий собой один столбец из всего диапазона.

        Посмотрим как, например, используя это свойство можно изменять столбцы во всем диапазоне Range.

        Для демонстрации воспользуемся предыдущим примером, изменим только окончание:

        ...
        BigRange:=Sheet.Range[Range1,Range2];
        for i:=1 to BigRange.Columns.Count do
          BigRange.Columns[i].Value:='Столбец №'+IntToStr(i);

        Excel_Range_ColumnsВ итоге в каждый из столбцов диапазона должна записаться строка с номером этого столбца, результат представлен на рисунке. Как видите, в цикле все столбцы обработались «насквозь», хотя Range не содержал в себе столбец D.

        Возвращает объект Comment для Range. В данном случае Range должен определять одну ячейку.

        Для демонстрации свойства не будем заходить в Excel, поработаем с приложением прямо из Delphi. Для этого воспользуемся методом AddComment. То есть сначала запишем комментарий в ячейку, а потом прочитаем его используя свойство Comment:

        Range2:=Sheet.Range['E6'];
        {записали комментарий}
        Range2.AddComment('Это комментарий ячейки области');
        {прочитали}
        ShowMessage(Range2.Comment.Text);

        Address

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

        Range2:=Sheet.Range['A1:E6'];
        Свойство Address будет содержать строку "$A$1:$E$6"

        Это свойство удобно использовать, когда Вы оперируете в программе только числовыми значениями (номерами столбцов и строк) и появляется необходимость показать пользователю адреса ячеек из диапазона.

        Помимо перечисленных выше свойств к объекту Range применимы все свойства, описанный в статье об изменении внешнего вида ячеек Excel, т.е. borders, color и пр.

        3. Методы объекта Excel Range.

        Теперь рассмотрим несколько полезных методов, которые могут Вам пригодиться при работе с Excel в Delphi.

        Метод Описание
        CheckSpelling Проверяет грамматику в выбранном диапазоне и при нахождении ошибок выводит окно для замены
        PrintPreview Выводит на экран окно предварительного просмотра перед печатью выбранного диапазона ячеек
        AutoFill Автозаполнение диапазона ячеек на основе данных из другого диапазона
        AutoFit Изменяет ширину или высоту ячеек диапазона для наилучшего представления данных.
        Clear Удаляет все данные из диапазона
        ClearComments Удаляет все комментарии в диапазоне Range
        ClearContents Удаляет все формулы из диапазона Range
        ClearFormats Очищает форматы в диапазоне Range
        ClearNotes Очищает все заметки в диапазоне Range
        Copy Копирует содержимое диапазона Range в буфер обмена или в другой диапазон
        PasteSpecial Специальная вставка диапазона
        Cut Вырезает данные и при необходимости вставляет их в новый диапазон
        Merge Объединение ячеек диапазона

        CheckSpelling

        Проверяет грамматику в выбранном диапазоне и при нахождении ошибок выводит окно для замены. Замечательной особенностью этого метода является то, что окно замены появляется даже при скрытом окне Excel, то есть, когда свойство Visible у объекта Excel равно false.

        Вызывается метод без каких-либо дополнительных параметров:

        Range['A1:H6'].CheckSpelling

        PrintPreview

        Выводит на экран окно предварительного просмотра перед печатью выбранного диапазона ячеек. Также как и предыдущий метод PrintPreview не имеет дополнительных параметров. Однако при попытке вызвать метод при скрытом окне Excel в лучшем случае возникнет исключительная ситуация, в худшем — зависание Вашего приложения. Так что перед вызовом:

        не забывайте включить свойство Visible у Excel:

        AutoFill

        Автоматическое заполнение диапазона ячеек на основе данных из другого диапазона.

        Вызов метода:

        Range.AutoFill(Destination, Type)
        Параметр Тип Описание
        Destination Variant представляет собой объект Range уже заполненных ячеек. Эти ячейки должны входить в автозаполняемый диапазон
        Type Integer тип автозаполнения (возможные значения см. ниже)

        Рассмотрим применение метода на примере.

        Sheet.Range['A1'].Value:=1;
        Sheet.Range['A2'].Value:=2;
        Source:=Sheet.Range['A1:A6'];
        Range2:=Sheet.Range['A1:E6'];
        Source.AutoFill(Range2, xlFillDefault)

        В результате лист Excel примет следующий вид:

        Excel_Range_AutoFillКак видите все столбцы второго диапазона Range заполнились значениями из диапазоны Source.

        При использовании этого метода следует иметь в виду, что диапазон-источник (в нашем случае — это source) по одному из измерений должен совпадать с источником назначения. В приведенном примере совпадает размерность по строкам (в обоих диапазонах их шесть). Если размерности диапазонов не совпадают, то возникает исключительная ситуация.

        При использовании метода мы использовали одну из констант в параметре Type xlFillDefault = 0. Используя её мы скопировали данные один-к-одному. Дополнительно Вы можете использовать следующие константы при автозаполнении:

        Имя Значение Описание
        xlFillDefault 0 скопировать данные один-к-одному
        xlFillDays 5 копирование дней недели с расширением, т.е., если Вы запишете в ячейку слово «Понедельник» и попробуете провести автозаполнение ещё на 2 строки, то во второй и третьей строке появятся «Вторник» и «Среда»
        xlFillCopy 1 копирует все данные и форматы, повторяя при необходимости
        xlFillFormats 3 копирует только форматы источника
        xlFillMonths 7 копирует названия месяцев. Работает аналогично xlFillDays
        xlFillSeries 2 копирует данные с расширением, например 1,2,3 будут при копировании расширены до 4,5,6 и т.д. Также копирует форматы данных
        xlFillValues 4 копирует только значения
        xlFillWeekdays 6 копирует дни рабочей недели, работает аналогично xlFillDays, но только до пятницы
        xlFillYears 8 копирует года. Работает аналогично xlFillDays
        xlGrowthTrend 10 копирует числовые значения из источника, расширяя их в предположении, что каждое последующее число представляет собой предыдущее, но умноженное на некоторую величину. Например 1,2 раскопируются в 4, 8, 16 и т.д. Формат данных также копируется
        xlLinearTrend 9 копирует числовые значения из источника, расширяя их в предположении, что каждое последующее число представляет собой предыдущее + некоторая величина. Например 1,2 раскопируются в 3, 4, 5 и т.д. Формат данных также копируется

        AutoFit

        Изменяет ширину или высоту ячеек диапазона для наилучшего представления данных.

        Пример вызова:

        Sheet.Range['A1'].Value:=1234567891012;
        Sheet.Range['A2'].Value:=23456789;
        Source:=Sheet.Range['A1:A2'];
        Source.Columns.AutoFit

        Приведет и изменению ширины столбца А таким образом, чтобы число было полностью видно на листе.

        Методы очистки данных диапазона Range

        Есть несколько различных методов очистки содержимого диапазона Range при работе с Excel в Delphi.

        1. Clear

        Удаляет все данные из диапазона.

        Пример вызова:

        2. ClearComments

        Удаляет все комментарии в диапазоне Range.

        Пример вызова:

        3. ClearContents

        Удаляет все формулы из диапазона Range

        Пример вызова:

        4. ClearFormats

        Очищает форматы в диапазоне Range

        Пример вызова:

        5. ClearNotes

        Очищает все заметки в диапазоне Range

        Пример вызова:

        Методы работы с буфером обмена Excel

        1. Copy

        Копирует содержимое диапазона Range в буфер обмена или в другой диапазон.

        Попробуем реализовать работу метода следующим образом: заполним столбец А некоторыми значениями, а затем скопируем его сначала в буфер, а потом в столбец Е:

        Sheet.Range['A1'].Value:=1;
        Sheet.Range['A2'].Value:=2;
        Sheet.Range['A3'].Value:=3;
        Sheet.Range['A4'].Value:=4;
        Sheet.Range['A5'].Value:=5;
        Sheet.Range['A6'].Value:=6;
        Source:=Sheet.Range['A1:A6'];
        Source.Copy; //скопировали в буфер
        Range2:=Sheet.Range['E1:E6'];
        Source.Copy(Range2)//скопировали в новый диапазон

        А для того, чтобы вставить данные из буфера обмена существует ещё один метод

        2. PasteSpecial

        Вызов метода:

        MyRange.PasteSpecial(Paste, Operation, SkipBlanks, Transpose)
        Параметр Тип Описание
        Paste Integer определяет какая часть данных диапазона будет вставлена (возможные значения см. ниже)
        Operation Integer операция, которая будет выполнена при вставке данных (возможные значения см. ниже)
        SkipBlanks boolean True, для того чтобы пустые ячейки из буфера обмена не вставлялись в диапазон назначения. Значение по умолчанию False
        Transpose boolean транспонирование столбцов и строк после вставки. По умолчанию устанавливается значение False

        При определении параметра Paste следует использовать следующие константы:

        Имя Значение Описание
        xlPasteAll -4104 Вставка всех данных
        xlPasteAllExceptBorders 7 Вставка всего содержимого за исключением вида границ диапазона
        xlPasteAllUsingSourceTheme 13 Вставка всего содержимого, используя тему оформления источника
        xlPasteColumnWidths 8 Копирует ширину столбцов
        xlPasteComments -4144 Вставка комментариев
        xlPasteFormats -4122 Вставка форматов данных
        xlPasteFormulas -4123 Вставка формул
        xlPasteFormulasAndNumberFormats 11 Вставка формул и чисел
        xlPasteValidation 6 Вставка проверок
        xlPasteValues -4163 Вставка значений
        xlPasteValuesAndNumberFormats 12 Вставка значений и чисел

        При использовании параметра Operation следует использовать следующие константы:

        Имя Значение Описание
        xlPasteSpecialOperationAdd 2 К скопированным данным будут добавлены значения из целевых ячеек
        xlPasteSpecialOperationDivide 5 Скопированные данные будут разделены на значения в целевых ячейках
        xlPasteSpecialOperationMultiply 4 Скопированные данные будут умножены на значения в целевых ячейках
        xlPasteSpecialOperationNone -4142 При вставке значений никакие операции не будут применяться
        xlPasteSpecialOperationSubtract 3 Из скопированных данных будут вычитаться значения целевых ячеек

        Теперь рассмотрим применение метода на примере. В качестве исходных данных возьмем данные из предыдущего примера, но для вставки используем метод PasteSpecial:

        ...
        Range2.PasteSpecial(Operation:=xlPasteSpecialOperationAdd);

        После выполнения этой операции все данные вставятся в диапазон Range2, т.к. диапазон до вставки был пуст, то данные скопировались один-к-одному.

        3. Cut

        Вырезает данные и при необходимости вставляет их в новый диапазон. Метод работает аналогично методу Copy, но с последующим удалением данных из источника.

        Merge

        Объединение ячеек диапазона.

        Для новичков, только начинающих постигать азы работы с excel в Delphi этот метод в какой-то момент становится камнем преткновения :). Дело в том, что очень часто возникает необходимость объединить часть ячеек листа для записи в объединенную область большого количества данных или длинной строки. В Excel есть такой метод Union, который и отвечает за объединение — им-то и пробуют пользоваться многие. А метод Union при вызове из Delphi применительно к диапазону Range вызывает исключительную ситуацию. Про Merge люди либо не знают, либо забывают. А использовать его достаточно просто:

        Параметр Тип Описание
        Across boolean True, чтобы объединить ячейки в каждой строке указанного диапазона как отдельные объекты. Значение по умолчанию False

        Например, объединим ячейки в диапазоне Range2 из предыдущего примера:

        После выполнения этой операции в диапазоне останется только верхнее значение, т.е. 1.

        Дополнительные методы автозаполнения ячеек диапазона Range

        В отдельную группу методов можно выделить 4 метода автозаполнения ячеек диапазона:

        1. FillDown
        2. FillUp
        3. FillRight
        4. FillLeft

        Принцип действия этих методов один и тот же за исключением направления автозаполнения.

        Например, давайте запишем в ячейку A1 какое-нибудь значение и воспользуемся методами FillDown и FillRight:

        Sheet.Range['A1'].Value:=1;
        Sheet.Range['A1:A6'].FillDown;
        Sheet.Range['A1:F1'].FillRight;

        После этого в строк 1 и столбце A появятся числа 1. Аналогичным образом можно заполнять не только строки или столбцы, но и таблицы целиком — достаточно знать в какую сторону двигаться при автозаполнениии и применять соответствующие методы Excel для Range.

        Вот в принципе краткий обзор возможностей работы с объектов Range в Excel. В целом можно отметить, что работа с этим объектом в Delphi один из самых эффективных способов организации пресылки/получения данных между Excel и Delphi, не считая работы через XML формат.

        Книжная полка

        Название:Разработка приложений Microsoft Office 2007 в Delphi

        Описание Описаны общие подходы к программированию приложений MS Office. Даны программные методы реализации функций MS Excel, MS Word, MS Access и MS Outlook в среде Delphi.

        купить книгу delphi на ЛитРес

        0
        0
        голоса

        Рейтинг статьи

        уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

        Понравилась статья? Поделить с друзьями:
      • Если текст равен тексту excel
      • Если пустая клетка excel
      • Если текст не входит в ячейку excel
      • Если пстр excel примеры
      • Если текст выходит за лист в word