Макросы как в excel delphi

2006 г.

Запись макроса (меню Excel «СервисМакросНачать запись…») незаменимая вещь при написании отчетов или создания диаграмм в Excel’е, особенно для тех, кто только начинает с ним работать. Но, записанный в Excel макрос, иногда выглядит довольно громоздко и читается с трудом. В данной статье я хочу рассмотреть методы перевода записанных макросов в более удобный вид для использования их в Delphi. Также будет рассмотрены некоторые нестыковки в объектной модели Excel’я в записанных макросах и методы их исправления.

Для начала рассмотрим записанные в Excel’е макросы и попробуем сократить их VBA-код для переноса в Delphi. Откроем в Excel’e новую книгу и выполним, к примеру, простые действия — запустим запись макроса, выделим область «A1:D5» и в тулбаре «Границы» выберем «Все границы». Остановим запись макроса и посмотрим, что у нас получилось. Должен появиться примерно такой код (чтоб открыть VBA редактор в Excel’е нажмите Alt+F11):

Sub Макрос1()
'
    Range("A1:D5").Select
    Selection.Borders(xlDiagonalDown).LineStyle = xlNone
    Selection.Borders(xlDiagonalUp).LineStyle = xlNone
    With Selection.Borders(xlEdgeLeft)
        .LineStyle = xlContinuous
        .Weight = xlThin
        .ColorIndex = xlAutomatic
    End With
    With Selection.Borders(xlEdgeTop)
        .LineStyle = xlContinuous
        .Weight = xlThin
        .ColorIndex = xlAutomatic
    End With

    With Selection.Borders(xlEdgeBottom)
        .LineStyle = xlContinuous
        .Weight = xlThin
        .ColorIndex = xlAutomatic
    End With
    With Selection.Borders(xlEdgeRight)
        .LineStyle = xlContinuous
        .Weight = xlThin
        .ColorIndex = xlAutomatic
    End With
    With Selection.Borders(xlInsideVertical)
        .LineStyle = xlContinuous
        .Weight = xlThin
        .ColorIndex = xlAutomatic
    End With

    With Selection.Borders(xlInsideHorizontal)
        .LineStyle = xlContinuous
        .Weight = xlThin
        .ColorIndex = xlAutomatic
    End With

End Sub

Да, многовато… Давайте посмотрим, что содержит полученный VBA-код:

  • Выделили область и убрали диагональные линии (а они у нас были?).
  • Нарисовали последовательно левую, верхнюю, правую, нижнюю границы.
  • Нарисовали внутренние горизонтальные и вертикальные границы.

Теперь попробуем сократить этот макрос, например, так (скопируйте код, приведенный ниже в VBA редактор):

Sub Макрос1_1()
'
    With Range("A1:D5").Borders
        .LineStyle = xlContinuous
        .Weight = xlThin
        .ColorIndex = xlAutomatic
    End With

End Sub

Очистим область «A1:D5» от границ и запустим наш макрос (перейдите в Excel из редактора, нажмите Alt+F8, выберите Макрос1_1 и нажмите «Выполнить»). Код намного короче, а результат тот же! Что мы сделали? Во-первых, убрали Select, просто указав какую область мы будем «обордюривать», во-вторых, вообще не указали какие границы будем заполнять, просто написав Borders без параметров (т.е. все). Почему понадобилось убирать Select? Потому что, во-первых, можно обойтись без него, а во-вторых, Select вызывает доп. перерисовку экрана, а это, как известно, самые долгие операции.

Теперь перейдем к другой «особенности» записи макроса, а именно к непонятному свойству объекта [Excel.]Application Selection. Что это такое? В данном макросе, как можно догадаться это область ячеек (Range). Давайте запишем еще один макрос: добавим окно инструментов «Рисование», включим запись макроса, выберем тулбар «Надпись», поместим ее на наш лист и наберем текст «Наша надпись». Выделим ячейку A1 и остановим запись макроса. Должен получиться примерно такой код:

Sub Макрос2()
'
    ActiveSheet.Shapes.AddTextbox(msoTextOrientationHorizontal, 19.5, 88.5, _
        191.25, 86.25).Select
    Selection.Characters.Text = "Наша надпись"
    With Selection.Characters(Start:=1, Length:=7).Font
        .Name = "Arial"
        .FontStyle = "обычный"
        .Size = 10

        .Strikethrough = False
        .Superscript = False
        .Subscript = False
        .OutlineFont = False
        .Shadow = False
        .Underline = xlUnderlineStyleNone
        .ColorIndex = xlAutomatic
    End With
    Range("A1").Select

End Sub

Опять попробуем сократить код:

Sub Макрос2_2()

    Dim MyShape As Shape

    Set MyShape = ActiveSheet.Shapes.AddTextbox(msoTextOrientationHorizontal, _
                  19.5, 88.5, 191.25, 86.25)

    MyShape.Characters.Text = "Наша надпись"


End Sub

Перейдем в Excel, удалим нашу надпись и выполним макрос Макрос2_2.
Получим ошибку «Объект не поддерживает данное свойство или метод» на
строке с кодом

MyShape.Characters.Text = "Наша надпись". 

Почему Selection его поддерживает, а Shape нет?
Посмотрев на объект Shape мы не найдем свойства Characters.
Что же скрывается за загадочным Selection?
Для того чтобы это понять давайте в Макрос2,
добавим строку MsgBox TypeName(Selection) после строки

Selection.Characters.Text = "Наша надпись"

и выполним макрос. Получим сообщение «TextBox».

Вот оно что! Значит Selection — это TextBox. Попробуем создать
такой объект и… Нет такого объекта! Есть только TextFrame. Замена Shape на
TextFrame тоже не увенчается успехом… Что же делать?

Посмотрим на свойства
объекта Shape и увидим там свойство TextFrame, у которого уже есть свойство
Characters… Посмотрев справку по VBA можно убедиться, что Characters —
это метод и принадлежит объекту TextFrame. Пробуем:

Sub Макрос2_2()
'
    Dim MyShape As Shape

    Set MyShape = ActiveSheet.Shapes.AddTextbox(msoTextOrientationHorizontal, _
                  19.5, 88.5, 191.25, 86.25)

    MyShape.TextFrame.Characters.Text = "Наша надпись"

End Sub

Запустим макрос — работает! Оставим мифический TextBox на совести Microsoft…

Примечание:

объект TextBox таки существует, но только как Control для Form.

Еще небольшой пример на VBA про Selection и займемся непосредственно переносом кода из VBA в Delphi. Откройте файл Книга1.xls, который приложен к статье и перейдите на Лист2. Там таблица и график. Включим запись макроса, выделим первый столбик, вызовем «Формат рядов данных» и изменим цвет на темно синий. Остановим запись. Должен получиться примерно такой код:

Sub Макрос3()
'
    ActiveSheet.ChartObjects("Диагр. 1").Activate
    ActiveChart.SeriesCollection(1).Select

    With Selection.Border
        .Weight = xlThin
        .LineStyle = xlAutomatic
    End With

    Selection.InvertIfNegative = False
    With Selection.Interior
        .ColorIndex = 23
        .Pattern = xlSolid
    End With
End Sub

Проверим, как он работает — перейдем в Excel, вызовем макросы и запустим Макрос3… Ошибка на первой же строке! Записанный макрос не работает. Почему? Попробуем сделать так, чтоб он заработал. Напишем небольшой макрос (руками) и будем вставлять в него код и тестировать. Начнем с определения имен имеющихся на листе диаграмм:

Sub Test1()
    Dim i As Integer

    For i = 1 To ActiveSheet.ChartObjects.Count
      MsgBox ActiveSheet.ChartObjects(i).Name
    Next i


End Sub

Запустив макрос, получим имя диаграммы «Chart 1» — почему не «Диагр. 1», как в записанном макросе — это очередная загадка. Исправим макрос и проверим:

Sub Макрос3()
'
    ActiveSheet.ChartObjects("Chart 1").Activate
    ActiveChart.SeriesCollection(1).Select

    With Selection.Border
        .Weight = xlThin
        .LineStyle = xlAutomatic
    End With

    Selection.InvertIfNegative = False
    With Selection.Interior
        .ColorIndex = 23
        .Pattern = xlSolid
    End With
End Sub

Работает :o).

Дальше определим тип объекта после строки ActiveChart.SeriesCollection(1).Select известной строкой MsgBox TypeName(Selection). Получим Series. Сократим макрос и избавимся от Selection.

Sub Макрос3_3()
'
    Dim ch As Chart, s As Series

    Set ch = ActiveSheet.ChartObjects("Chart 1").Chart
    Set s = ch.SeriesCollection(1)

    With s.Interior
        .ColorIndex = 23

        .Pattern = xlSolid
    End With
End Sub

Если посмотреть на код Макрос3 и Макрос3_3, то видно, что код в Макрос3 использует Selection как промежуточный буфер для передачи управления между объектами, т.е. Activate, Select и для «безликого» вызова свойств и методов. Чтобы получить объект типа Chart нам понадобилось добавить обращение к свойству ChartObject.Chart

Set ch = ActiveSheet.ChartObjects("Chart 1").Chart

Дальше мы просто поменяли цвет столбика без использования Select.

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

Перенесем наш код в Delphi и параллельно в C# (если не возражаете).

Сразу оговорюсь, что в статье не рассматриваются методы подключения к
Excel’ю (по данному вопросу можно почитать
здесь ), также используется
раннее связывание (что это такое читайте здесь).

Я считаю позднее связывание не «паскалевким» подходом, так как везде
используется один тип Variant (как в языке «Основняк»),
что, по моему, сродни шаманизму — что-то происходит, что-то куда то записывается,
но никто не понимает, почему это работает.

Начнем с Макрос1. Да, именно с него, а не сокращенного варианта. Попытаемся написать код для первых трех строк:

Delphi

ASheet.Range['A1:D5', EmptyParam].Select;
XL.Selection[lcid].Borders[xlDiagonalDown].LineStyle := xlNone;
XL.Selection[lcid].Borders[xlDiagonalUp].LineStyle := xlNone;

Попробовав скомпилировать данный участок, сразу же получим ошибку компилятора «E2003 Undeclared identifier: ‘Borders’«. Посмотрим, какой тип имеет Selection (в данном примере смотрим файл Excel2000.pas):

property ExcelApplication.Selection[lcid: Integer]: IDispatch;

Посмотрев на интерфейс IDispatch, мы в самом деле не найдем такого свойства и метода… Попробуем подправить код:

Delphi

ASheet.Range['A1:D5', EmptyParam].Select;
    (XL.Selection[lcid] As ExcelRange).Borders[xlDiagonalDown].LineStyle := xlNone;
    (XL.Selection[lcid] As ExcelRange).Borders[xlDiagonalUp].LineStyle := xlNone;
    With (XL.Selection[lcid] As ExcelRange).Borders[xlEdgeLeft] do begin

        LineStyle := xlContinuous;
        Weight := xlThin;
        ColorIndex := xlAutomatic;
    End;
    With (XL.Selection[lcid] As ExcelRange).Borders[xlEdgeTop] do begin
        LineStyle := xlContinuous;
        Weight := xlThin;
        ColorIndex := xlAutomatic;
    End;
    With (XL.Selection[lcid] As ExcelRange).Borders[xlEdgeBottom] do begin

        LineStyle := xlContinuous;
        Weight := xlThin;
        ColorIndex := xlAutomatic;
    End;
    With (XL.Selection[lcid] As ExcelRange).Borders[xlEdgeRight] do begin
        LineStyle := xlContinuous;
        Weight := xlThin;
        ColorIndex := xlAutomatic;
    End;

C#

ASheet.get_Range("A1:D5", Type.Missing).Select();
((Excel.Range) XL.Selection).Borders.get_Item(
          Excel.XlBordersIndex.xlDiagonalDown).LineStyle =
          Excel.XlLineStyle.xlLineStyleNone;
((Excel.Range) XL.Selection).Borders.get_Item(
          Excel.XlBordersIndex.xlDiagonalUp).LineStyle =
          Excel.XlLineStyle.xlLineStyleNone;
// левая граница
((Excel.Range) XL.Selection).Borders.get_Item(
          Excel.XlBordersIndex.xlEdgeLeft).LineStyle =
          Excel.XlLineStyle.xlContinuous;
((Excel.Range) XL.Selection).Borders.get_Item(
          Excel.XlBordersIndex.xlEdgeLeft).Weight =
          Excel.XlBorderWeight.xlThin;
((Excel.Range) XL.Selection).Borders.get_Item(
          Excel.XlBordersIndex.xlEdgeLeft).ColorIndex =
          Excel.XlColorIndex.xlColorIndexAutomatic;
// верхняя граница
        ((Excel.Range) XL.Selection).Borders.get_Item(
          Excel.XlBordersIndex.xlEdgeTop).LineStyle =
          Excel.XlLineStyle.xlContinuous;
        ((Excel.Range) XL.Selection).Borders.get_Item(
          Excel.XlBordersIndex.xlEdgeTop).Weight =
          Excel.XlBorderWeight.xlThin;
        ((Excel.Range) XL.Selection).Borders.get_Item(
          Excel.XlBordersIndex.xlEdgeTop).ColorIndex =
          Excel.XlColorIndex.xlColorIndexAutomatic;
        // нижняя граница
        ((Excel.Range) XL.Selection).Borders.get_Item(
          Excel.XlBordersIndex.xlEdgeBottom).LineStyle =
          Excel.XlLineStyle.xlContinuous;
        ((Excel.Range) XL.Selection).Borders.get_Item(
          Excel.XlBordersIndex.xlEdgeBottom).Weight =
          Excel.XlBorderWeight.xlThin;
        ((Excel.Range) XL.Selection).Borders.get_Item(
          Excel.XlBordersIndex.xlEdgeBottom).ColorIndex =
          Excel.XlColorIndex.xlColorIndexAutomatic;
        // правая граница

        ((Excel.Range) XL.Selection).Borders.get_Item(
          Excel.XlBordersIndex.xlEdgeRight).LineStyle =
          Excel.XlLineStyle.xlContinuous;
        ((Excel.Range) XL.Selection).Borders.get_Item(
          Excel.XlBordersIndex.xlEdgeRight).Weight =
          Excel.XlBorderWeight.xlThin;
        ((Excel.Range) XL.Selection).Borders.get_Item(
          Excel.XlBordersIndex.xlEdgeRight).ColorIndex =
          Excel.XlColorIndex.xlColorIndexAutomatic;

Работает… Что мы для этого сделали? Привели тип IDispatch к ExcelRange: XL.Selection[lcid] as ExcelRange). Но такой перевод записанного макроса в Delphi поистине героический труд, да и нужен ли нам Select для того чтоб нарисовать границы (а глядя на C# код, вообще можно сразу отказаться на нем программировать)? Ведь всякая перерисовка — лишняя трата времени и, следовательно, скорости. Поэтому займемся Макросом1_1:

Delphi

With ASheet.Range['A1:D5', EmptyParam].Borders do begin

  LineStyle := xlContinuous;
  Weight := xlThin;
  ColorIndex := xlAutomatic;
End;

C#

oRng = ASheet.get_Range("A1:D5", Type.Missing);
// установим две границы
oRng.Borders.LineStyle = Excel.XlLineStyle.xlContinuous;
oRng.Borders.Weight = Excel.XlBorderWeight.xlThin;
oRng.Borders.ColorIndex = Excel.XlColorIndex.xlColorIndexAutomatic;

Различия есть? Мы не делали Select и не использовали безликий Selection, обратившись непосредственно к области ExcelRange. Или все же лучше с Selection? Сравните:

Delphi

ASheet.Range['A1:D5', EmptyParam].Select;
With (XL.Selection[lcid] As ExcelRange).Borders do begin

  LineStyle := xlContinuous;
  Weight := xlThin;
  ColorIndex := xlAutomatic;
End;

Все то же самое, но что-то рябит в глазах при Select, не правда ли? И вроде как-то медленнее или мне показалось?
Перейдем к Макрос2, вернее к уже подготовленному Макрос2_2:

Delphi

MyShape := (XL.ActiveWorkbook.ActiveSheet As _Worksheet).Shapes.AddTextbox(
           msoTextOrientationHorizontal, 19.5, 88.5, 191.25, 86.25);
MyShape.TextFrame.Characters(EmptyParam, EmptyParam).Text := 'Наша надпись';

C#

myShape = (Excel.Shape) ASheet.Shapes.AddTextbox(
          Office.MsoTextOrientation.msoTextOrientationHorizontal,
          (float) 19.5, (float) 88.5, (float) 191.25, (float) 86.25);
myShape.TextFrame.Characters(Type.Missing, Type.Missing).Text =
          "Наша надпись";

В коде на Delphi практически никаких отличий, кроме указания двух обязательных параметров: начала изменяемых символов и их длины. Мы написали EmptyParam, тем самым указав, что обрабатывается весь текст.

И, наконец, Макрос3_3. Усложним его — полностью создадим таблицу с данными, создадим график и изменим цвет первого столбца:

Delphi

oSheet.Cells.Item[1, 1] := 'First Name';
oSheet.Cells.Item[1, 2] := 'Last Name';
oSheet.Cells.Item[1, 3] := 'Full Name';
oSheet.Cells.Item[1, 4] := 'Salary';


//Format A1:D1 as bold, vertical alignment := center.
oSheet.Range['A1', 'D1'].Font.Bold := True;
oSheet.Range['A1', 'D1'].VerticalAlignment := xlVAlignCenter;

// Create an array to multiple values at once.
saNames := VarArrayCreate([0, 4, 0, 1], varVariant);

saNames[0, 0] := 'John';
saNames[0, 1] := 'Smith';
saNames[1, 0] := 'Tom';
saNames[1, 1] := 'Brown';
saNames[2, 0] := 'Sue';
saNames[2, 1] := 'Thomas';
saNames[3, 0] := 'Jane';
saNames[3, 1] := 'Jones';
saNames[4, 0] := 'Adam';
saNames[4, 1] := 'Johnson';

oSheet.Range['A2', 'B6'].Formula := saNames;

oRng := oSheet.Range['C2', 'C6'];
oRng.Formula := '=A2 & " " & B2';

oRng := oSheet.Range['D2', 'D6'];
oRng.Formula := '=RAND()*100000';

oSheet.Range['A1', 'D1'].EntireColumn.AutoFit;


// создадим график на листе в обласи E8:L29
Ch := (oSheet.ChartObjects As ChartObjects).Add(
      oSheet.Range['B8', EmptyParam].Left,
      oSheet.Range['B8', EmptyParam].Top,
      oSheet.Range['I8', EmptyParam].Left - oSheet.Range['B8', EmptyParam].Left,
      oSheet.Range['B30', EmptyParam].Top - oSheet.Range['B8', EmptyParam].Top).Chart
      As _Chart;

oRng := oSheet.Range['C1', 'D6'];

With Ch do begin
  SetSourceData(oRng, xlRows);
  ChartType := xl3DColumnClustered;
  HasTitle[lcid] := True;
  ChartTitle[lcid].Characters[EmptyParam, EmptyParam].Text := 'Диаграмма 1';
  (Axes(xlCategory, xlPrimary, lcid) As Axis).HasTitle := False;
  (Axes(xlValue, xlPrimary, lcid) As Axis).HasTitle := True;
  (Axes(xlValue, xlPrimary, lcid) As Axis).AxisTitle.
  Characters[EmptyParam, EmptyParam].Text := 'Деньги';
  (Axes(xlValue, xlPrimary, lcid) As Axis).AxisTitle.Orientation := xlUpward;

End;

// здесь код замены цвета у первого столбика
// взятый из Макрос3_3
With (Ch.SeriesCollection(1, lcid) As Series) do begin

  Interior.ColorIndex := 23;
  Interior.Pattern := xlSolid;
End;

C#

oSheet.Cells[1, 1] = "First Name";
oSheet.Cells[1, 2] = "Last Name";
oSheet.Cells[1, 3] = "Full Name";
oSheet.Cells[1, 4] = "Salary";

//Format A1:D1 as bold, vertical alignment := center.
oSheet.get_Range("A1", "D1").Font.Bold = true;
oSheet.get_Range("A1", "D1").VerticalAlignment =
          Excel.XlVAlign.xlVAlignCenter;
oSheet.get_Range("A1", "D1").HorizontalAlignment =
          Excel.XlHAlign.xlHAlignCenter;
// Create an array to multiple values at once.
string[,] saNames = new string[5, 2];

saNames[0, 0] = "John";
saNames[0, 1] = "Smith";
saNames[1, 0] = "Tom";
saNames[1, 1] = "Brown";
saNames[2, 0] = "Sue";
saNames[2, 1] = "Thomas";
saNames[3, 0] = "Jane";
saNames[3, 1] = "Jones";
saNames[4, 0] = "Adam";
saNames[4, 1] = "Johnson";

oSheet.get_Range("A2", "B6").Formula = saNames;


//Fill C2:C6 with a relative formula (=A2 & " " & B2).
oRng = oSheet.get_Range("C2", "C6");
oRng.Formula = "=A2 & " " & B2";

//Fill D2:D6 with a formula(=RAND()*100000) and apply format.
oRng = oSheet.get_Range("D2", "D6");
// oRng.Formula = "=RAND()*100000";
oRng.Formula = "=СЛЧИС()*100000";
// oRng.NumberFormat = "0.00";

//AutoFit columns A:D.
oRng = oSheet.get_Range("A1", "D1");
oRng.EntireColumn.AutoFit();

// создадим график на листе в обласи E8:L29
Ch =  ((Excel.ChartObjects) oSheet.ChartObjects(Type.Missing)).Add(
          (double) oSheet.get_Range("B8", Type.Missing).Left,
          (double) oSheet.get_Range("B8", Type.Missing).Top,
          (double) oSheet.get_Range("I8", Type.Missing).Left -
          (double) oSheet.get_Range("B8", Type.Missing).Left,
          (double) oSheet.get_Range("B30", Type.Missing).Top -
          (double) oSheet.get_Range("B8", Type.Missing).Top
          ).Chart;

oRng = oSheet.get_Range("C1", "D6");
Ch.SetSourceData(oRng, Excel.XlRowCol.xlRows);
Ch.ChartType = Excel.XlChartType.xl3DColumnClustered;
Ch.HasTitle = true;
Ch.ChartTitle.get_Characters(Type.Missing, Type.Missing).Text = "Диаграмма 1";
        ((Excel.Axis) Ch.Axes(Excel.XlAxisType.xlCategory,
          Excel.XlAxisGroup.xlPrimary)).HasTitle = false;
        ((Excel.Axis) Ch.Axes(Excel.XlAxisType.xlValue,
          Excel.XlAxisGroup.xlPrimary)).HasTitle = true;
        ((Excel.Axis) Ch.Axes(Excel.XlAxisType.xlValue,
          Excel.XlAxisGroup.xlPrimary)).AxisTitle.
          get_Characters(Type.Missing, Type.Missing).Text = "Деньги";
        ((Excel.Axis) Ch.Axes(Excel.XlAxisType.xlValue,
          Excel.XlAxisGroup.xlPrimary)).AxisTitle.Orientation =
          Excel.XlOrientation.xlUpward;


// здесь код замены цвета у первого столбика
// взятый из Макрос3_3
((Excel.Series) Ch.SeriesCollection(1)).Interior.ColorIndex = 23;
((Excel.Series) Ch.SeriesCollection(1)).Interior.Pattern =
          Excel.XlPattern.xlPatternSolid;

Из перенесенных строк из Макрос3_3 видно, что коллекция Ch.SeriesCollection(1, lcid) тоже возвращает интерфейс IDispatch, поэтому мы привели ее к типу Series. Почему в библиотеке типов сразу не использован тип Series остается только гадать. Еще в только что описанном примере приведен код задания титулов для осей (axes) и здесь метаморфоза превращения Axes в Axis, т.е. Axes — это коллекция Axis, хотя в VBA это ни как не отображается.

Резюме:

Мы рассмотрели несколько примеров перевода VBA кода, созданного записью макроса в Excel в Delphi. Увидели, как можно сократить ненужный код, избавившись от Select. Как уйти от безликого Selection (тип IDispatch) во избежание ошибок и возможных недоразумений. Также обнаружили несоответствие записанного кода (к примеру, имени объекта «Наша надпись») и типов реальным типам объектов. Т.е. записанный код VBA не всегда оказывается работоспособным. Для правильного перевода VBA в Delphi требуется представление об объектной модели Excel’я, обращение к справке Excel VBA, а также большое желание достигнуть результата.

  • Все примеры тестировались на BDS 2006 и Microsoft Office 2003
  • К статье прилагается Книга1.xls с приведенными в статье макросами и Demo-проект на Delphi и C#. Для работы проекта на C# требуется Framework 1.1
Полезные ссылки:
  1. MSDN:Automating Excel Using the Excel Object Model
  2. How to automate Microsoft Excel from Microsoft Visual C# .NET
  3. MSDN:Understanding the Excel Object Model from a .NET Developer’s Perspective
  4. MSDN:Microsoft Excel Object Model
  5. Excel Tips: Macros and VBA
  6. Add a Chart to a Worksheet Programmatically (C#)
  7. MSDN: Best Practices for Setting Range Properties

Специально для Королевства Delphi

К материалу прилагаются файлы:
  • Книга Excel с приведенными в статье макросами (17.8 K) обновление от 4/24/2006 7:00:00 AM
  • Demo-проект на Delphi (6 K) обновление от 4/24/2006 7:00:00 AM
  • Demo-проект на C# (392 K) обновление от 4/24/2006 7:00:00 AM

Работа с макросами в
документе MSEXCEL через Delphi

Фрагмент кода, выполняющий следующие функции:

— создает новую книгу Excel

— создает новый макрос в ней

— запускает макрос на выполнение

— сохраняет книгу

unit Unit1;interface

uses
Windows, Messages, SysUtils, Variants, Classes,
Graphics, Controls, Forms,
Dialogs, ComObj, StdCtrls, ExcelXP, OleServer;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
Excel, WorkBook, Sheet: variant;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
temp: WideString;

begin
Excel:= CreateOleObject(‘Excel.application‘);
Excel.visible:= True;
WorkBook:= Excel.WorkBooks.Add;
WorkBook.SaveAs(‘c:test.xls’);
Excel.modules.add(emptyparam,emptyparam,1);
 

// создание строк макроса
temp:= ‘sub macro1()’+ #13;
temp:= temp + ‘dim a as integer’ + #13;
temp:= temp + ‘ThisWorkBook.Sheets
Лист1″).Range(«a1») = 10′
+ #13;
temp:= temp + ‘ThisWorkBook.Sheets
Лист1″).Range(«b1») = 30′
+ #13;
temp:= temp + ‘end sub’;
WorkBook.vbproject.vbcomponents.item(1).codemodule.addfromstring(temp);
Excel.run(‘macro1’);  //
запуск макроса на выполнение

WorkBook.Save;
WorkBook.Close;
Excel.Quit;
Excel:= null;
end;

end.

0 / 0 / 0

Регистрация: 20.05.2014

Сообщений: 2

1

21.05.2014, 08:08. Показов 7122. Ответов 3


Студворк — интернет-сервис помощи студентам

Нужна помощь.
Есть База данных, из неё переношу данные в Excel. Напротив каждой строки вставляю картинку.

Нужно, чтобы при нажатии на соответствующую картинку в строке, формировалась Диаграмма по данным строки.

Код

//ВставляемКартинку
Excel_Doklad.ActiveSheet.Shapes.AddPicture((ExtractFilePath(Application.ExeName)+'Diagram.JPG'),True,True,
Excel_Doklad.ActiveWorkBook.ActiveSheet.Range['K'+IntToStr(I)].Left,
Excel_Doklad.ActiveWorkBook.ActiveSheet.Range['K'+IntToStr(I)].Top,16,16);

Что дальше делать не знаю…

Добавлено через 12 часов 55 минут
help

Добавлено через 27 секунд
help



0



Mawrat

13094 / 5875 / 1706

Регистрация: 19.09.2009

Сообщений: 8,808

21.05.2014, 16:02

2

Лучший ответ Сообщение было отмечено kekskeks как решение

Решение

Чтобы на листе MS Excel по нажатию на картинку (объект Shape — фигура) выполнялось какое-то действие — для этого с картинкой должен быть связан макрос. Чтобы программным способом создать макрос, нужно, чтобы в настройках безопасности MS Excel было соответствующее разрешение.
Такое разрешение задаётся следующим образом:
— В MS Excel 2003: Главное меню — Сервис — Макрос — Безопасность… — перейти на вкладку «Надёжные издатели» — установить галку в поле: «Доверять доступ к Visual Basic Project».
— В MS Excel 2007: На ленте вкладка «Разработчик» — Безопасность макросов — меню «Параметры макросов» — раздел «Параметры макросов для разработчика» — поставить галку в поле «Доверять доступ к объектной модели проектов VBA».
Если на ленте нет вкладки «Разработчик», надо эту вкладку отобразить: Главная офисная кнопка (слева вверху) — кнопка «Параметры Excel» — в открывшемся окне меню «Основные» — раздел «Основные параметры работы с Excel» — поставить галку в поле «Показывать вкладку «Разработчик» на ленте».

После этого можно программным способом управлять средой разработки VBA — VBE (VB Editor), VBIDE (VB Integrated Development Environment).

Вот пример, как программным способом добавить изображение на лист, добавить в VB проект модуль с макросом и связать изображение с этим макросом.

Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
uses
  ComObj;
 
procedure TForm1.Button1Click(Sender: TObject);
const
  FnBook = 'FilesКнига-01.xls';
  FnPicture = 'FilesPicture-01.bmp';
 
  //Виды модулей VBA.
  vbext_ct_StdModule = 1; //Обыкновенный модуль.
var
  exApp, exBook, exSheet, exRng, exShape, exVbProj, exVbModule, exVbCode : OleVariant;
  Path : String;
begin
  //Путь к той папке, в которой располжен исполняемый файл нашей программы.
  //В конце пути записан слеш "".
  Path := ExtractFilePath(ParamStr(0));
 
  //Попытка подключиться к корневому объекту MS Excel.
  try
    exApp := CreateOleObject('Excel.Application');
  except
    MessageBox(Handle, 'Не удалось запустить MS Excel. Действие отменено.',
      'Ошибка', MB_OK + MB_ICONERROR + MB_APPLMODAL);
    Exit;
  end;
  //Делаем видимым окно MS Excel. На время отладки или на постоянной основе.
  exApp.Visible := True;
  //Открываем файл рабочей книги.
  exBook := exApp.WorkBooks.Open(FileName:=Path + FnBook);
  //Получаем ссылку на первый лист рабочей книги.
  exSheet := exBook.Worksheets[1];
  //Ссылка на ячейку с адресом "C4".
  exRng := exSheet.Cells[4, 3]; //Или так: exRng := exSheet.Cells['C4'];
  //Размещаем картинку на листе MS Excel таким образом, чтобы она
  //совпала с положением и размерами ячейки "C4".
  exShape := exSheet.Shapes.AddPicture(Filename:=Path + FnPicture,
    LinkToFile:=False, SaveWithDocument:=True,
    Left:=exRng.Left, Top:=exRng.Top, Width:=exRng.Width, Height:=exRng.Height);
 
  //Пытаемся получить доступ к VB проекту рабочей книги.
  try
    exVbProj := exBook.VBProject;
  except
    MessageBox(Handle, 'В настройках безопасности MS Excel запрещён доступ к объектам VBIDE.'
      + ' Действие отменено.', 'Ошибка', MB_OK + MB_ICONERROR + MB_APPLMODAL);
    Exit;
  end;
  //Добавляем в проект обыкновенный модуль.
  exVbModule := exVbProj.VBComponents.Add(vbext_ct_StdModule);
  //Имя модуля.
  //exVbModule.Name := 'VBModule1';
  //Получаем ссылку на модуль кода.
  exVbCode := exVbModule.CodeModule;
  //Удаляем исходный код. Т. к., при создании модуля в код автоматически
  //могут быть добавлены строки, задающие настроки (опции) контекста VB программы.
  exVbCode.DeleteLines(1, exVbCode.CountOfLines);
  //Записываем код модуля. В коде реализуем процедуру Sub1(), которая будет
  //представлять макрос. Любая процедура без параметров является макросом.
  exVbCode.AddFromString(
    '''Настройка контекста VB программы.'#13#10
    + '''Режим обязательного описания переменных.'#13#10
    + 'Option Explicit'#13#10
    + #13#10
    + '''Этот модуль создан программно.'#13#10
    + #13#10
    + '''Макрос, который должен быть связан с изображением (Shape) на листе MS Excel.'#13#10
    + 'Sub Sub1()'#13#10
    + '  MsgBox "Был щелчок по картинке!"'#13#10
    + 'End Sub'#13#10);
 
  //Связываем картинку с макросом.
  exShape.OnAction := exVbModule.Name + '.Sub1';
end;

Если выполнить этот код, будет сделано следующее:
— Запуск MS Excel.
— Открытие рабочей книги «Книга-01.xls».
— Загрузка картинки и её размещение на месте ячейки «C4» в первом листе рабочей книги.

Delphi - Работа с макросами в Excel (Создание и привязка макроса к картинке)

— Создание VBA модуля и запись в него VBA кода. В VBA коде создаётся процедура Sub1() без параметров. Любая процедура без параметров является макросом.

Delphi - Работа с макросами в Excel (Создание и привязка макроса к картинке)

— Связывание картинки с макросом.
Теперь, если пользователь щёлкает мышью по картинке, то запускается связанный макрос. Этот макрос в данном случае выводит сообщение: «Был щелчок по картинке!».

Delphi - Работа с макросами в Excel (Создание и привязка макроса к картинке)

Вложения

Тип файла: rar CreateVBProj-01.rar (185.6 Кб, 50 просмотров)



0



13094 / 5875 / 1706

Регистрация: 19.09.2009

Сообщений: 8,808

21.05.2014, 16:04

3

Лучший ответ Сообщение было отмечено kekskeks как решение

Решение

Вот хороший материал по управлению VBE и VBIDE: http://www.cpearson.com/excel/vbe.aspx



1



0 / 0 / 0

Регистрация: 20.05.2014

Сообщений: 2

21.05.2014, 20:20

 [ТС]

4

Спасибо большое, очень помог, потихоньку начинает получаться.



0



Содержание

  1. Delphi excel добавить макрос
  2. Перенос VBA-макросов в Delphi
  3. Delphi и OLE Automation с Excel

Delphi excel добавить макрос

Перенос VBA-макросов в Delphi

Запись макроса (меню Excel «СервисМакросНачать запись…») незаменимая вещь при написании отчетов или создания диаграмм в Excel’е, особенно для тех, кто только начинает с ним работать. Но, записанный в Excel макрос, иногда выглядит довольно громоздко и читается с трудом. В данной статье я хочу рассмотреть методы перевода записанных макросов в более удобный вид для использования их в Delphi. Также будет рассмотрены некоторые нестыковки в объектной модели Excel’я в записанных макросах и методы их исправления.

Для начала рассмотрим записанные в Excel’е макросы и попробуем сократить их VBA-код для переноса в Delphi. Откроем в Excel’e новую книгу и выполним, к примеру, простые действия — запустим запись макроса, выделим область «A1:D5» и в тулбаре «Границы» выберем «Все границы». Остановим запись макроса и посмотрим, что у нас получилось. Должен появиться примерно такой код (чтоб открыть VBA редактор в Excel’е нажмите Alt+F11): Да, многовато… Давайте посмотрим, что содержит полученный VBA-код:

  • Выделили область и убрали диагональные линии (а они у нас были?).
  • Нарисовали последовательно левую, верхнюю, правую, нижнюю границы.
  • Нарисовали внутренние горизонтальные и вертикальные границы.

Теперь попробуем сократить этот макрос, например, так (скопируйте код, приведенный ниже в VBA редактор):

Очистим область «A1:D5» от границ и запустим наш макрос (перейдите в Excel из редактора, нажмите Alt+F8, выберите Макрос1_1 и нажмите «Выполнить»). Код намного короче, а результат тот же! Что мы сделали? Во-первых, убрали Select, просто указав какую область мы будем «обордюривать», во-вторых, вообще не указали какие границы будем заполнять, просто написав Borders без параметров (т.е. все). Почему понадобилось убирать Select? Потому что, во-первых, можно обойтись без него, а во-вторых, Select вызывает доп. перерисовку экрана, а это, как известно, самые долгие операции.

Теперь перейдем к другой «особенности» записи макроса, а именно к непонятному свойству объекта [Excel.]Application Selection. Что это такое? В данном макросе, как можно догадаться это область ячеек (Range). Давайте запишем еще один макрос: добавим окно инструментов «Рисование», включим запись макроса, выберем тулбар «Надпись», поместим ее на наш лист и наберем текст «Наша надпись». Выделим ячейку A1 и остановим запись макроса. Должен получиться примерно такой код: Опять попробуем сократить код:

Перейдем в Excel, удалим нашу надпись и выполним макрос Макрос2_2 . Получим ошибку «Объект не поддерживает данное свойство или метод» на строке с кодом Почему Selection его поддерживает, а Shape нет? Посмотрев на объект Shape мы не найдем свойства Characters . Что же скрывается за загадочным Selection? Для того чтобы это понять давайте в Макрос2, добавим строку MsgBox TypeName(Selection) после строки и выполним макрос. Получим сообщение «TextBox» .

Вот оно что! Значит Selection — это TextBox. Попробуем создать такой объект и… Нет такого объекта! Есть только TextFrame. Замена Shape на TextFrame тоже не увенчается успехом… Что же делать?

Посмотрим на свойства объекта Shape и увидим там свойство TextFrame, у которого уже есть свойство Characters… Посмотрев справку по VBA можно убедиться, что Characters — это метод и принадлежит объекту TextFrame. Пробуем:

Запустим макрос — работает! Оставим мифический TextBox на совести Microsoft…

Примечание:
объект TextBox таки существует, но только как Control для Form .

Еще небольшой пример на VBA про Selection и займемся непосредственно переносом кода из VBA в Delphi. Откройте файл Книга1.xls, который приложен к статье и перейдите на Лист2. Там таблица и график. Включим запись макроса, выделим первый столбик, вызовем «Формат рядов данных» и изменим цвет на темно синий. Остановим запись. Должен получиться примерно такой код:

Проверим, как он работает — перейдем в Excel, вызовем макросы и запустим Макрос3 … Ошибка на первой же строке! Записанный макрос не работает. Почему? Попробуем сделать так, чтоб он заработал. Напишем небольшой макрос (руками) и будем вставлять в него код и тестировать. Начнем с определения имен имеющихся на листе диаграмм:

Запустив макрос, получим имя диаграммы «Chart 1» — почему не «Диагр. 1», как в записанном макросе — это очередная загадка. Исправим макрос и проверим: Работает :o).

Дальше определим тип объекта после строки ActiveChart.SeriesCollection(1).Select известной строкой MsgBox TypeName(Selection). Получим Series. Сократим макрос и избавимся от Selection.

Если посмотреть на код Макрос3 и Макрос3_3, то видно, что код в Макрос3 использует Selection как промежуточный буфер для передачи управления между объектами, т.е. Activate, Select и для «безликого» вызова свойств и методов. Чтобы получить объект типа Chart нам понадобилось добавить обращение к свойству ChartObject.Chart Дальше мы просто поменяли цвет столбика без использования Select.

Конечно, это далеко не все загадки при записи макросов — их еще много, но нам сейчас нужно было понять, что это возможно и как с этим бороться. Перенесем наш код в Delphi и параллельно в C# (если не возражаете).

Сразу оговорюсь, что в статье не рассматриваются методы подключения к Excel’ю (по данному вопросу можно почитать здесь ), также используется раннее связывание (что это такое читайте здесь).

Я считаю позднее связывание не «паскалевким» подходом, так как везде используется один тип Variant (как в языке «Основняк»), что, по моему, сродни шаманизму — что-то происходит, что-то куда то записывается, но никто не понимает, почему это работает.

Начнем с Макрос1. Да, именно с него, а не сокращенного варианта. Попытаемся написать код для первых трех строк:

Попробовав скомпилировать данный участок, сразу же получим ошибку компилятора » E2003 Undeclared identifier: ‘Borders’ «. Посмотрим, какой тип имеет Selection (в данном примере смотрим файл Excel2000.pas):

Посмотрев на интерфейс IDispatch, мы в самом деле не найдем такого свойства и метода. Попробуем подправить код:

Работает… Что мы для этого сделали? Привели тип IDispatch к ExcelRange: XL.Selection[lcid] as ExcelRange). Но такой перевод записанного макроса в Delphi поистине героический труд, да и нужен ли нам Select для того чтоб нарисовать границы (а глядя на C# код, вообще можно сразу отказаться на нем программировать)? Ведь всякая перерисовка — лишняя трата времени и, следовательно, скорости. Поэтому займемся Макросом1_1 :

Различия есть? Мы не делали Select и не использовали безликий Selection, обратившись непосредственно к области ExcelRange. Или все же лучше с Selection? Сравните:

Все то же самое, но что-то рябит в глазах при Select, не правда ли? И вроде как-то медленнее или мне показалось? Перейдем к Макрос2, вернее к уже подготовленному Макрос2_2:

В коде на Delphi практически никаких отличий, кроме указания двух обязательных параметров: начала изменяемых символов и их длины. Мы написали EmptyParam, тем самым указав, что обрабатывается весь текст.

И, наконец, Макрос3_3 . Усложним его — полностью создадим таблицу с данными, создадим график и изменим цвет первого столбца:

Из перенесенных строк из Макрос3_3 видно, что коллекция Ch.SeriesCollection(1, lcid) тоже возвращает интерфейс IDispatch, поэтому мы привели ее к типу Series. Почему в библиотеке типов сразу не использован тип Series остается только гадать. Еще в только что описанном примере приведен код задания титулов для осей (axes) и здесь метаморфоза превращения Axes в Axis, т.е. Axes — это коллекция Axis, хотя в VBA это ни как не отображается.

Резюме:

Мы рассмотрели несколько примеров перевода VBA кода, созданного записью макроса в Excel в Delphi. Увидели, как можно сократить ненужный код, избавившись от Select. Как уйти от безликого Selection (тип IDispatch) во избежание ошибок и возможных недоразумений. Также обнаружили несоответствие записанного кода (к примеру, имени объекта «Наша надпись») и типов реальным типам объектов. Т.е. записанный код VBA не всегда оказывается работоспособным. Для правильного перевода VBA в Delphi требуется представление об объектной модели Excel’я, обращение к справке Excel VBA, а также большое желание достигнуть результата.

  • Все примеры тестировались на BDS 2006 и Microsoft Office 2003
  • К статье прилагается Книга1.xls с приведенными в статье макросами и Demo-проект на Delphi и C#. Для работы проекта на C# требуется Framework 1.1

Источник

Delphi и OLE Automation с Excel

Автоматизация позволяет одному приложению управлять другим приложением. Управляемое приложение называется сервером автоматизации (в нашем случае Excel). Приложение, управляющее сервером называется диспетчером автоматизации.

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

Позднее связывание (Интерфейс IDispatch)

При использовании данного метода имена функций и типы параметров решаются во время выполнения программы, все параметры определены вариантным типом.

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

Так как имена функций и типы параметров должны проверяться во время выполнения программы, данный метод выполняется медленно.

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

Раннее связывание (Использование библиотеки типов/интерфейсов)

При использовании данного метода имена функций и типы параметров полностью решаются во время компиляции.

Библиотека типов должна импортироваться в Delphi. Библиотека типов является языковым нейтральным описанием всех объектов и функций, поддерживаемых сервером. (Это подобно файлу заголовка языка C).

При вызове функции должны обязательно присутствовать все параметры, даже те, которые в документации указаны как дополнительные (необязательные). Это позволяет обнаружить и исправить множество ошибок еще до запуска программы.

Скорость выполнения значительно быстрее, чем при использовании позднего связывания.

Из-за преимуществ второго метода остальная часть документа демонстрирует принципы создания приложений с ранним связыванием. Все приложения, использующие Excel автоматизацию, должны пользоваться последним методом, если нет причин для первого.

Подготовка библиотеки типов.

Модуль Pascal должен быть создан на основе файла библиотеки типов.

Выберите пункт меню Project|Import Type Library .

Нажмите кнопку Add и выберите следующий файл

c:program filesmicrosoft officeofficeexcel8.olb

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

Наиболее простой путь заключается в следующем: удалите модуль excel_tlb из проекта и только после этого добавьте его в список используемых модулей.

Справочный файл c:program filesmicrosoft officeofficevbaxl8.hlp содержит информацию о доступных объектах Excel.

«Записыватель» макросов позволяет быстро создавать VBA-код. После этого он довольно может легко быть портирован в Delphi.

Код следующего примера демонстрирует создание простой электронной таблицы и наполнение ее данными. Не забудьте добавить excel_tlb в список используемых модулей.

Настоятельно рекомендую хранить код автоматизации в отдельном модуле, это поможет избежать проблем конфликта имен.

Добавьте библиотеку типов в список используемых молулей.

Первая строчка кода создает объект Excel приложения.

Следующая строчка кода получает пользовательский локальный идентификатор по умолчанию. Он необходим многим методам и свойствам Excel.

Следующая строчка кода устанавливает в истину свойство видимости, что заставляет Excel вывести свое окно. Это полезно для контроля выполняемого кода.

Примечание: Для вызова этой функции необходим параметр LCID. К сожалению этот факт умалчивается в электронной документации по Excel. В файле c:program filesborlandDelphi 3importsexcel_tlb.pas наглядно видны свойства функций и определения методов.

Следующий код создает новую книгу и назначает ссылку на нее одной из переменных Delphi. Для VBA параметр шаблона необязателен, для Delphi — обязателен.

Примечание: Вам вовсе не обязательно подставлять файл шаблона Excel (.xlt), но все же это наилучший способ для форматирования информации. Чем больше сделано с помощью Excel, тем меньше придется делать с помощью Delphi. На данный момент это является лидирующей технологией.

Для создания пустой книги используйте:

Следующие две строки присваивают значение двум ячейкам. Здесь демонстрируются две технологии для работы с данными.

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

Следующая строка демонстрирует использование формулы.

Следующая строка кода выполняет VBA функцию, хранящуюся в файле шаблона. На первый взгляд все выглядит достаточно сложно, но это только кажется. Преобразование типа xla к OLEVariant позволяет вызвать функцию, используя позднее, а не раннее связывание. (Причина в имени метода и параметрах, решаемых только во время прогона программы, а никак во время разработки). Delphi просто не знает количество и тип параметров, передаваемых макросу ‘Demo’.

Следующий код сохраняет в файле filename электронную таблицу. Все параметры должны быть переданы в обязательном порядке, хотя многие из них являются дополнительными.

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

  • Всегда используйте раннее связывание.
  • Если позднее связывание необходимо для вызовов некоторых функций, используйте где возможно раннее связывание и преобразование типа объектной переменной к типу OLEVariant для вызовов, требующим позднее связывание.
  • Не включайте модуль библиотеки типов в ваш проект.
  • Создавайте код автоматизации в отдельном модуле.
  • Используйте «записыватель» макросов Excel для создания прототипа кода автоматизации.
  • Используйте файл электронной справки vbaxl8.hlp для получения информации об объектах Excel.
  • Используйте модуль excel_tlb.pas для проверки необходимых Delphi типов и количества параметров.
  • Загружайте и используйте шаблоны Excel (.xlt файлы), содержащие предварительное форматирование и связывание данных. Этот способ существенно быстрее и не требует большого времени для создания форматированных электронных таблиц. Шаблоны ДОЛЖНЫ сохраняться приложением в своей рабочей директории. Это поможет избежать проблем, связанных с конфликтом имен. Файлы шаблонов могут также содержать макросы, которые могут быть вызваны из приложений Delphi.
  • Удостоверьтесь в том, что ваш код содержит команду закрытия приложения Excel (xla.quit). Не вызывая xla.quit, можно быстро исчерпать системные ресурсы, особенно при работе с большим количеством документов Excel.
  • Наличие множества незакрытых документов Excel легко проверить в Windows NT, используя Менеджер Задач (нажмите CTL+ALT+Del для его открытия).
  • В больших электронных таблицах повысить быстродействие вам поможет обработка ячеек посредством «мультикоманды», оперирующей одновременно множеством ячеек. Это также улучшит читаемость кода.

Приложение A – Быстродействие

Тестирование производилось на компьютере P166 с 64Мб памяти. Первоначальная инициализация приложения не производилась. Это гарантировало, что Excel при загрузке пользовался диском, а не кэшем. Первоначальная инициализация существенно уменьшила бы скорость загрузки приложения. В реальной ситуации процесс загрузки занимает около 5 секунд.

Тест включал в себя загрузку числовых данных в чистую электронную таблицу размером 10 колонок на n строк. Для вычисления быстродействия использовались следующие три метода:

  • Заполнение листа ячейка за ячейкой.
  • Заполнение одной колонки за один проход.
  • Заполнение всей таблицы за один проход.

Приведенное время не включает в себя время, затраченное на поиск данных в базе данных. Но оно вносит существенные коррективы при создании больших электронных таблиц.

Время приведено в минутах и секундах, округленных до ближайшей целой.

Размер электронной таблицы (строки * колонки)

Источник

Для того, чтобы получить отчет в формате Excel из Delphi
постройте сначала прототип таблицы в самом Excel. Полученную
таблицу можно в дальнейшем использовать, как шаблон. Например,
Вы будете уже знать ширину колонок, применяемые шрифты и то,
как она будет выглядеть при печати.
Допустим Вам необходимо построить отчет в формате Excel следующего
вида (пусть количество колонок и столбцов будет фиксированным):

Наименование продукта

Приход

Расход

Дорожные велосипеды

121

11

Спортивные велосипеды

75

55

Всего:

196

66

Обратите внимание, что все строчки в таблице центрированы,
а самая верхняя написана «жирным» шрифтом. В таблице
так же содержатся ячейки в которых подсчитываются суммы по
приходу и расходу. Ширина для колонок — 187, 94, 70 пиксель.

1. Установим ширину колонок:

//Выбираем каждую колонку целиком
Sheet.Columns['1:1'].ColumnWidth := 121;
Sheet.Columns['2:2'].ColumnWidth := 94;
Sheet.Columns['3:3'].ColumnWidth := 70;

2. Аналогичным образом можно было бы поступить для центрирования
текста. Но представте себе, что в дальнейшем к данной таблице
добавится что-то справа или снизу, а центрирование строк текста
нежелательно. Поступим иначе. В Excel запишем макрос (выделим
интересующие нас ячейки и зададим соответствующий стиль —
«центрирование»). В результате получим примерно
следующее:

Range("A1:C4").Select
With Selection
    HorizontalAlignment = xlCenter
    VerticalAlignment = xlBottom
    WrapText = False
    .Orientation = 0
    .AddIndent = False
    .ShrinkToFit = False
    .MergeCells = False
End With

В полученном макросе нас интересуют только первая и третья
строка. Вот так они будут выглядеть в Delphi:

//Выбираем диапазон ячеек "A1:C4"
Sheet.Range['A1:C4'].Select;
//Задаем центрирование текста для выбранных ячеек
exl.Selection.HorizontalAlignment := xlCenter;

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


3. Аналогичным способом для верхних трех ячеек можно задать
выделение жирным шрифтом. Добавим следующий код:

//Выбираем диапазон ячеек "A1:С1"
Sheet.Range['A1:C1'].Select;
exl.Selection.Font.Bold := True;

4. Подставляем значения:

Sheet.Cells(1,1) := 'Наименование продукта';
Sheet.Cells(1,2) := 'Приход';
Sheet.Cells(1,3) := 'Расход';
Sheet.Cells(2,1) := 'Дорожные велосипеды';
Sheet.Cells(2,2) := 121;
Sheet.Cells(2,3) := 11;
Sheet.Cells(3,1) := 'Спортивные велосипеды';
Sheet.Cells(2,2) := 75;
Sheet.Cells(2,3) := 55;
Sheet.Cells(3,1) := 'Итого:';

5. Для итоговых сумм опять воспользуемся макросом. Запишем
макрос для подсчета суммы по приходу товара (ячейка B4):

Range("B4").Select
ActiveCell.FormulaR1C1 = "=SUM(R[-2]C:R[-1]C)"

Обратим внимание еще на одну особенность. В строке для редактирования
Excel формула выглядит иначе:
=СУММ(B2:B3) (для Русской версии
Excel).

Итак, следующие строки программы могут выглядеть так:

Sheet.Range['B4'].Select;
exl.ActiveCell.FormulaR1C1 := '=SUM(R[-2]C:R[-1]C)';
Sheet.Range['C4'].Select;
exl.ActiveCell.FormulaR1C1 := '=SUM(R[-2]C:R[-1]C)';

Существует еще несколько вариантов подстановки формулы в
ячейки. Например, скопировав формулу в одной ячейке, мы могли
бы ее вставить в другую(гие). Можно применить и следующий
код:

Sheet.Cells(3,2) := '=SUM(R[-2]C:R[-1]C)';
Sheet.Cells(3,3) := '=SUM(R[-2]C:R[-1]C)';

6. И в заключение обведем ячейки таблицы одной толстой рамкой.
Посмотрим как будет выглядеть макрос:

Range("A1:C4").Select
Selection.Borders(xlDiagonalDown).LineStyle = xlNone
Selection.Borders(xlDiagonalUp).LineStyle = xlNone
With Selection.Borders(xlEdgeLeft)
  .LineStyle = xlContinuous
  .Weight = xlMedium
  .ColorIndex = xlAutomatic
End With
With Selection.Borders(xlEdgeTop)
  .LineStyle = xlContinuous
  .Weight = xlMedium
  .ColorIndex = xlAutomatic
End With
With Selection.Borders(xlEdgeBottom)
  .LineStyle = xlContinuous
  .Weight = xlMedium
  .ColorIndex = xlAutomatic
End With
With Selection.Borders(xlEdgeRight)
  .LineStyle = xlContinuous
  .Weight = xlMedium
  .ColorIndex = xlAutomatic
End With
Selection.Borders(xlInsideVertical).LineStyle = xlNone
Selection.Borders(xlInsideHorizontal).LineStyle = xlNone

Выберем интересующие нас строки и добавим следующий программный
код:

Sheet.Range['A1:C4'].Select;
exl.Selection.Borders[хlEdgeLeft].LineStyle := xlContinuous;
exl.Selection.Borders[хlEdgeLeft].Weight := xlMedium;
exl.Selection.Borders[xlEdgeTop].LineStyle := xlContinuous;
exl.Selection.Borders[xlEdgeTop].Weight := xlMedium;
exl.Selection.Borders[xlEdgeBottom].LineStyle := xlContinuous;
exl.Selection.Borders[xlEdgeBottom].Weight := xlMedium;
exl.Selection.Borders[xlEdgeRight].LineStyle := xlContinuous;
exl.Selection.Borders[xlEdgeRight].Weight := xlMedium;

И опять обратим внимание, что синтаксис изменился.


А теперь попробуем объединить все программные строки в одной
процедуре по созданию Excel-таблицы, приведенной выше:

procedure OutSampleTable;
const
  xlCenter = $FFFFFFF4;
  хlEdgeLeft = $00000007;
  xlEdgeTop = $00000008;
  xlEdgeBottom = $00000009;
  xlEdgeRight = $0000000A;
  xlMedium = $FFFFFFD6;
  xlContinuous = $00000001;
var
  exl: OleVariant;
  WorkBook, Sheet: Variant;
  fileName: String;
begin
  try
    fileName:= ExtractFilePath(Application.EXEName)
      +'SampleTable.xls';
    exl := CreateOleObject('Excel.Application');
    WorkBook := exl.Application.WorkBooks.Add;
    Sheet := WorkBook.WorkSheets[1];
    //Устанавливаем ширину колонок
    Sheet.Columns['1:1'].ColumnWidth := 121;
    Sheet.Columns['2:2'].ColumnWidth := 94;
    Sheet.Columns['3:3'].ColumnWidth := 70;
    //Форматирование таблицы
    Sheet.Range['A1:C4'].Select;
    exl.Selection.HorizontalAlignment := xlCenter;
    Sheet.Range['A1:C1'].Select;
    exl.Selection.Font.Bold := True;
   //Подстановка значений
    Sheet.Cells(1,1) := 'Наименование продукта';
    Sheet.Cells(1,2) := 'Приход';
    Sheet.Cells(1,3) := 'Расход';
    Sheet.Cells(2,1) := 'Дорожные велосипеды';
    Sheet.Cells(2,2) := 121;
    Sheet.Cells(2,3) := 11;
    Sheet.Cells(3,1) := 'Спортивные велосипеды';
    Sheet.Cells(2,2) := 75;
    Sheet.Cells(2,3) := 55;
    Sheet.Cells(3,1) := 'Итого:';
   //Подстановка формул
    Sheet.Cells(3,2) := '=SUM(R[-2]C:R[-1]C)';
    Sheet.Cells(3,3) := '=SUM(R[-2]C:R[-1]C)';
   //Обводка рамкой
    Sheet.Range['A1:C4'].Select;
    exl.Selection.Borders[хlEdgeLeft].LineStyle := 
        xlContinuous;
    exl.Selection.Borders[хlEdgeLeft].Weight := xlMedium;
    exl.Selection.Borders[xlEdgeTop].LineStyle := 
        xlContinuous;
    exl.Selection.Borders[xlEdgeTop].Weight := xlMedium;
    exl.Selection.Borders[xlEdgeBottom].LineStyle := 
        xlContinuous;
    exl.Selection.Borders[xlEdgeBottom].Weight := xlMedium;
    exl.Selection.Borders[xlEdgeRight].LineStyle := 
        xlContinuous;
    exl.Selection.Borders[xlEdgeRight].Weight := xlMedium;
    exl.Application.ActiveWorkBook.Saveas(fileName);
  finally
    exl.Application.Quit;
  end;
end;

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

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