Испытываю возобновление интереса к теме и вот в связи с чем. ADODB.Recordset имеет полезнейшие свойства Filter и Sort, которыми можно эффективно пользоваться, фильтруя исходный набор или изменяя порядок записей исходного Recordset'а и далее перебирая в цикле нужные записи в нужном порядке.
Резонно было бы предположить, хотя про это нигде не сказано в справке, что методом CopyFromRecordset должен выгружаться набор записей с учетом фильтрации и сортировки. Увы, при управлении Excel из Аксапты этого не происходит:
X++:
static void Job103(Args _args)
{
#CCADO
ComExcelDocument_RU doc = new ComExcelDocument_RU();
COM rst = new COM('ADODB.Recordset');
COM xlApp;
COM wbook;
COM activeSheet;
COM range;
COM flds,fld;
int i;
;
doc.NewFile('',false);
wbook = doc.getComDocument();
xlApp = wbook.Parent();
activeSheet = xlApp.ActiveSheet();
flds = rst.Fields();
flds.Append('myField1', #adVarChar, 50);
flds.Append('myField2', #adInteger);
rst.Open();
for (i=1;i<=1000;i++)
{
rst.AddNew();
fld = flds.Item(0); fld.Value(int2str(i));
fld = flds.Item(1); fld.Value(i*2);
rst.Update();
}
rst.Filter("myField1 Like '*24*'");
rst.Sort('myField1 DESC');
rst.MoveFirst();
range = activeSheet.Range('A1');
range.CopyFromRecordset(rst, 300);
range = activeSheet.Range('A1000');
range.CopyFromRecordset(rst, 300);
doc.visible(true);
// чтобы показать, что набор действительно фильтруется и сортируется
rst.MoveFirst();
while (! rst.EOF())
{
info(strFmt('%1 --- %2',
new CCADOField( flds.Item(0) ).value(),
new CCADOField( flds.Item(1) ).value()));
rst.MoveNext();
}
}
Поведение метода CopyFromRecordset в этом примере не соответствует ожиданиям. Он не учитывает не только сортировку и фильтрацию, но и декларированное в справке продвижение указателя текущей записи. Так, по здравой логике, первый оператор "range.CopyFromRecordset(rst, 300);" должен вывести в Excel первые 300 записей и установить указатель на 301-ю. Второй же оператор "range.CopyFromRecordset(rst, 300);" должен вывести следующие 300 записей: с 301-й по 600-ю и остановиться на 601-й.
Но этого не происходит. Вместо этого - и начиная с ячейки A1, и начиная с ячейки A1000 - одинаково выводятся первые 300 записей исходного набора. А хотелось бы, чтобы вывелись только 20 записей с ячейки A1, т.е. те записи, которые пример выводит в окно Infolog.
Возможно мои претензии необоснованы и CopyFromRecordset должен себя вести именно так, как ведет себя в этом примере? Я уже было засомневался, вернее, почти отказался от своих "претензий", но решился на еще одну проверку - в самом Excel на VBA:
Код:
Sub Job103_VBA()
Dim i As Integer
Dim rst As Object
Dim flds As Object
Dim xlApp As Excel.Application
Set rst = CreateObject("ADODB.Recordset")
'я сознательно создал rst через CreateObject, а не как New ADODB.Recordset,
'чтобы не искать ссылку на ADO через Tools\References
Set flds = rst.Fields
flds.Append "myField1", 200, 50 '200 = adVarChar
flds.Append "myField2", 3 '3 = adInteger
rst.Open
For i = 1 To 1000
rst.AddNew
rst.Fields(0).Value = i
rst.Fields(1).Value = i * 2
rst.Update
Next i
rst.Filter = "myField1 Like '*24*'"
rst.Sort = "myField1 DESC"
rst.MoveFirst
'Set xlApp = New Excel.Application
Set xlApp = Application
xlApp.Workbooks.Add
xlApp.Range("A1").CopyFromRecordset rst, 300
xlApp.Range("A1000").CopyFromRecordset rst, 300
xlApp.Visible = True
End Sub
Что скажете о результате? Он такой, как хотелось! Ура? Увы, нет. Закомментируйте строку "Set xlApp = Application" и раскомментируйте строку выше, т.е. должно стать вот так:
Код:
Set xlApp = New Excel.Application
'Set xlApp = Application
Таким образом, теперь мы из Excel создаем другой Excel и выводим рекордсет в этот другой. Результат такой же, как при выводе из Аксапты - не такой, как хочется.
Есть ли у кого какие-нибудь соображения? Может, кто-нибудь интересующийся продвинуто заглянет какими-нибудь крутыми вьюерами в тайны происходящих процессов COM-автоматизации? Может быть удастся победить ситуацию использованием каких-нибудь COMDispFunction?
P.S. Проделал даже такой финт. Создал шаблон xlt, в который зашил VBA-процедуру:
Код:
Sub myCopyFromRecordset(ByVal rng As Range, ByVal rst As ADODB.Recordset, ByVal maxRows As Integer)
rng.CopyFromRecordset rst, maxRows
End Sub
Из Аксапты создал новый файл на базе этого шаблона и вызвал процедуру строкой:
X++:
xlApp.Run('myCopyFromRecordset', range, rst, 300);
Результат - тоже не тот