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 < (tblCust.Fields.Count - 1) then
Buff := Buff + #9;
end;
tblCust.Next;
if not tblCust.EOF then
Buff := Buff + #10;
end;
BufferToClipboard(Buff);
try
IDispatch(IR1) := ISheet.Cells.Item[StartRow, StartColumn];
IDispatch(IR2) := ISheet.Cells.Item[StartRow + tblCust.RecordCount - 1,
StartColumn + tblCust.Fields.Count - 1];
OLEVariant(ISheet.Range[IR1, IR2]).PasteSpecial;
finally
Clipboard.Clear;
end;
end;

Как я и писал выше, в строковый буфер 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. “Мы его теряем!”

Февраль 9, 2008 — Рубрика: Delphi
Метки: , ,