(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Перенос VBA-макросов в Delphi (исходники)

Александр Шабля

Запись макроса (меню 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

Файлы для загрузки


 Распечатать »
 Правила публикации »
  Обсудить материал в конференции Micro Focus/Borland »
Написать редактору 
 Рекомендовать » Дата публикации: 23.03.2007 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
SmartBear TestComplete Platform - Node-Locked License - (Includes 1 year Maintenance)
Avira Antivirus Pro - Business Edition, 5 ПК, 1 год
Crowd Commercial 50 Users
VideoStudio Ultimate 2018 ML. Электронный ключ
CorelDRAW Graphics Suite 2018. Электронный ключ
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование на Microsoft Access
CASE-технологии
СУБД Oracle "с нуля"
Новые материалы
Delphi - проблемы и решения
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
Обсуждения в форумах
Пишу программы на заказ профессионально (2133)
Пишу программы на заказ на языках Pascal (численные методы, списки, деревья, прерывания) под...
 
3D редакторы, плюсы и минусы (2)
Одно из многих сотен бестолковых обобщений. Прости их господи, Васей Пупкиных
 
Программы на заказ (инженеры-профессионалы, по договору) + мобильные приложения, админ-панели, ИС (1)
Несколько инженеров-профессионалов примут заказы на разработку: 1. нативных мобильных...
 
Куплю программу (5)
Нужно сделать программу для рулетки в онлайн-казино. Должна показывать в виде таблицы выпадающие...
 
Пишу программы на Lazarus (16)
Предлагаю разработку программ на заказ. Примеры программ: www.itproportal.ru
 
 
 



    
rambler's top100 Rambler's Top100