Чернопятов Е.А. Автоматизация приложений MS Office. Часть 5.
Продолжение темы автоматизации MS Excel. Работа с диапазонами.
В этой части я расскажу, как осуществляется обмен данными с диапазоном ячеек. В
предыдущей части использовался прямой доступ к одиночным ячейкам для
занесения в них данных/формул или счтывания данных и результатов. Однако
поячеечный доступ является недопустимо медленным, и именно о том, как работать
быстро с большим количеством ячеек и пойдет речь.
5.1. Запись данных из массива в диапазон ячеек.
Основным классом для работы с ячейками является класс CRange (соответствующий
объекту Range в MS Excel). Мы уже
использовали этот класс в предыдущей части. Хочу ещё раз отметить,
что этот класс используется для доступа не только к диапазону ячеек,
но и к одиночной ячейке. Чтобы выбрать значения ячеек в диапазон
используется созданный для нас ClassWizard-ом метод LPDISPATCH get_Range(VARIANT
Cell1, VARIANT Cell2
). В качестве параметров он принимает номер первой (верхней-левой) и последней
(нижней-правой) ячеек диапазона. Если в передать один и тот же номер, то
получим диапазон из одной ячейки.
Для примера попробуем передать в Excel двумерный массив
(матрицу) вещественных чисел. Работать будем с диапазоном A10:J19.
CRange oRange1;
// получить диапазон
oRange1 = oSheet.get_Range(COleVariant(_T("A10")),COleVariant(_T("J19")));
На VBA подобный фокус делается с пол-пинка, примерно так:
Sub Range_test()
Dim arr(10, 10)
Dim rng As Range
With
ThisWorkbook.Worksheets(1)
For i = 0 To 9
For j = 0 To 9
arr(i, j) = i * j + 0.1
Next j
Next i
Set rng = Range(Cells(10, 1), Cells(20, 10))
rng = arr
End With
End Sub
Однако у нас все будет несколько сложнее. Во-первых, мы будем
использовать класс MFC COleSafeArray ,
очень грубо говоря, представляющий собой обертку над структурой SAFEARRAY
из VARIANT , т.е. массивом для использования в COM
(в действительности этот класс пронаследован непосредственно от VARIANT,
просто он "заточен" для работы с массивами произвольной размерности, а
также с байт-векторами, и использует в работе функции из "VARIANT API").
Во-вторых, приём из VBA по присвоению массива диапазону у нас не пройдет, так
как не существует никакого способа привести типы к "единому знаменателю". К
счастью, у Range есть свойство (property), которое называется Value (и ещё
Value2). Это свойство и представляет собой значение диапазона. Оно открыто как
на чтение, так и на запись, и ClassWizard уже создал нам 2 метода для доступа к
нему:
-
для Value:
-
VARIANT get_Value(VARIANT RangeValueDataType)
-
void put_Value(VARIANT RangeValueDataType, VARIANT newValue)
-
для Value2:
-
VARIANT get_Value2()
-
void put_Value2(VARIANT newValue)
Для нашего примера мы будем использовать Value2 (о разнице между Value и Value2
можно прочесть в
MSDN).
Итак, приступим...
COleSafeArray saMatrixToExcel;
DWORD numElements[] = {10, 10};
// создать вариантный массив, тип double (64-bit), размерность 2, число элементов 10*10
saMatrixToExcel.Create(VT_R8, 2, numElements);
ASSERT(saMatrixToExcel.GetDim() == 2);
// заполнить его какими-нибудь данными
long index[2];
double val;
for(index[0]= 0;index[0]<10; index[0]++)
{
for(index[1]= 0;index[1]<10; index[1]++)
{
val = index[0] + index[1]*10 + 0.1;
saMatrixToExcel.PutElement(index, &val);
}
}
// поместить данные массива в желаемый диапазон ячеек
oRange1.put_Value2(saMatrixToExcel);
Если на этом этапе переключиться в Excel
, мы увидим примерно следующую картину:
5.2. Получение данных из диапазона ячеек.
Следующей задачей будет научиться получать данные из диапазона. Для этого нам
снова понадобится COleSafeArray. Однако теперь создавать его
для нас будет сам Excel. Здесь есть одна хитрость. Поскольку
заранее нельзя сказать, какие данные находятся в ячейках, то Excel
передаст нам массив, каждый элемент которого будет типа VARIANT.
Сравните это с передачей данных в Excel, когда мы задавали тип
элементов самостоятельно. Преимуществом такого подхода является то, что мы
можем обрабатывать любой тип данных из ячеек.
Для иллюстрации заменим в последней ячейке (J19) цифровое значение 99,1 на слово
"Тест".
oRange1 = oSheet.get_Range(COleVariant(_T("J19")),COleVariant(_T("J19")));
oRange1.put_Value2(COleVariant(_T("Тест")));
После этого получим массив из диапазона, и выведем дамп в окно отладчика (для
простоты):
// get_Value|get_Value2 вернет нам либо 1 значение (если диапазон содержит тольrо одну ячейку),
// либо SAFEARRAY, если oRange1 представляет собой прямоугольную область
VARIANT va;
va = oRange1.get_Value2();
COleSafeArray saMatrixFromExcel(va); // в нашем примере будет массив
VARIANT el;
long row_start, col_start, row_count, col_count;
ASSERT(saMatrixFromExcel.GetDim() == 2); // размерность должна быть 2 (матрица)
saMatrixFromExcel.GetLBound(1,&row_start);
saMatrixFromExcel.GetLBound(2,&col_start);
saMatrixFromExcel.GetUBound(1,&row_count);
saMatrixFromExcel.GetUBound(2,&col_count);
// обойдем массив и сбросим каждый элемент в дамп в соответсвии с его типом
for(index[0]=row_start; index[0]<=row_count; index[0]++)
{
for(index[1]=col_start; index[1]<=col_count; index[1]++)
{
// содержимое saMatrixFromExcel представляет собой матрицу
// элементов типа VARIANT, именно так ее передает нам Excel
saMatrixFromExcel.GetElement(index,&el);
// кто в теремочке живёт?
switch (el.vt)
{
case VT_R8:
TRACE("%g ", el.dblVal);
break;
case VT_BSTR:
{
CString s(el);
TRACE("%s ", s);
}
break;
default: // прочие типы в этом примере не используются
break;
}
}
TRACE("\n", el.bstrVal);
}
Если все сделано правильно, то в окне Output появится наша матрица:
Исходники
Здесь вы можете загрузить иллюстрирующий проект.
Скачать исходники - архив ZIP,~70 Кб
Предыдущая часть |
Оглавление
Последние изменения от 09.08.2013