Visual basic 2010 excel 2007

Время на прочтение
7 мин

Количество просмотров 312K

Приветствую всех.

В этом посте я расскажу, что такое VBA и как с ним работать в Microsoft Excel 2007/2010 (для более старых версий изменяется лишь интерфейс — код, скорее всего, будет таким же) для автоматизации различной рутины.

VBA (Visual Basic for Applications) — это упрощенная версия Visual Basic, встроенная в множество продуктов линейки Microsoft Office. Она позволяет писать программы прямо в файле конкретного документа. Вам не требуется устанавливать различные IDE — всё, включая отладчик, уже есть в Excel.

Еще при помощи Visual Studio Tools for Office можно писать макросы на C# и также встраивать их. Спасибо, FireStorm.

Сразу скажу — писать на других языках (C++/Delphi/PHP) также возможно, но требуется научится читать, изменять и писать файлы офиса — встраивать в документы не получится. А интерфейсы Microsoft работают через COM. Чтобы вы поняли весь ужас, вот Hello World с использованием COM.

Поэтому, увы, будем учить Visual Basic.

Чуть-чуть подготовки и постановка задачи

Итак, поехали. Открываем Excel.

Для начала давайте добавим в Ribbon панель «Разработчик». В ней находятся кнопки, текстовые поля и пр. элементы для конструирования форм.

Появилась вкладка.

Теперь давайте подумаем, на каком примере мы будем изучать VBA. Недавно мне потребовалось красиво оформить прайс-лист, выглядевший, как таблица. Идём в гугл, набираем «прайс-лист» и качаем любой, который оформлен примерно так (не сочтите за рекламу, пожалуйста):

То есть требуется, чтобы было как минимум две группы, по которым можно объединить товары (в нашем случае это будут Тип и Производитель — в таком порядке). Для того, чтобы предложенный мною алгоритм работал корректно, отсортируйте товары так, чтобы товары из одной группы стояли подряд (сначала по Типу, потом по Производителю).

Результат, которого хотим добиться, выглядит примерно так:

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

Кодим

Для начала требуется создать кнопку, при нажатии на которую будет вызываться наша програма. Кнопки находятся в панели «Разработчик» и появляются по кнопке «Вставить». Вам нужен компонент формы «Кнопка». Нажали, поставили на любое место в листе. Далее, если не появилось окно назначения макроса, надо нажать правой кнопкой и выбрать пункт «Назначить макрос». Назовём его FormatPrice. Важно, чтобы перед именем макроса ничего не было — иначе он создастся в отдельном модуле, а не в пространстве имен книги. В этому случае вам будет недоступно быстрое обращение к выделенному листу. Нажимаем кнопку «Новый».

И вот мы в среде разработки VB. Также её можно вызвать из контекстного меню командой «Исходный текст»/«View code».

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

Sub FormatPrice()End Sub

Напишем Hello World:

Sub FormatPrice()
    MsgBox "Hello World!"
End Sub

И запустим либо щелкнув по кнопке (предварительно сняв с неё выделение), либо клавишей F5 прямо из редактора.

Тут, пожалуй, следует отвлечься на небольшой ликбез по поводу синтаксиса VB. Кто его знает — может смело пропустить этот раздел до конца. Основное отличие Visual Basic от Pascal/C/Java в том, что команды разделяются не ;, а переносом строки или двоеточием (:), если очень хочется написать несколько команд в одну строку. Чтобы понять основные правила синтаксиса, приведу абстрактный код.

Примеры синтаксиса

' Процедура. Ничего не возвращает
' Перегрузка в VBA отсутствует
Sub foo(a As String, b As String)
    ' Exit Sub ' Это значит "выйти из процедуры"
    MsgBox a + ";" + b
End Sub' Функция. Вовращает Integer
Function LengthSqr(x As Integer, y As IntegerAs Integer
    ' Exit Function
    LengthSqr = x * x + y * y
End FunctionSub FormatPrice()
    Dim s1 As String, s2 As String
    s1 = "str1"
    s2 = "str2"
    If s1 <> s2 Then
        foo "123""456" ' Скобки при вызове процедур запрещены
    End IfDim res As sTRING ' Регистр в VB не важен. Впрочем, редактор Вас поправит
    Dim i As Integer
    ' Цикл всегда состоит из нескольких строк
    For i = 1 To 10
        res = res + CStr(i) ' Конвертация чего угодно в String
        If i = 5 Then Exit For
    Next iDim x As Double
    x = Val("1.234"' Парсинг чисел
    x = x + 10
    MsgBox xOn Error Resume Next ' Обработка ошибок - игнорировать все ошибки
    x = 5 / 0
    MsgBox xOn Error GoTo Err ' При ошибке перейти к метке Err
    x = 5 / 0
    MsgBox "OK!"
    GoTo ne

Err:
    MsgBox 

"Err!"

ne:

On Error GoTo 0 ' Отключаем обработку ошибок

    ' Циклы бывает, какие захотите
    Do While True
        Exit DoLoop 'While True
    Do 'Until False
        Exit Do
    Loop Until False
    ' А вот при вызове функций, от которых хотим получить значение, скобки нужны.
    ' Val также умеет возвращать Integer
    Select Case LengthSqr(Len("abc"), Val("4"))
    Case 24
        MsgBox "0"
    Case 25
        MsgBox "1"
    Case 26
        MsgBox "2"
    End Select' Двухмерный массив.
    ' Можно также менять размеры командой ReDim (Preserve) - см. google
    Dim arr(1 to 10, 5 to 6) As Integer
    arr(1, 6) = 8Dim coll As New Collection
    Dim coll2 As Collection
    coll.Add "item""key"
    Set coll2 = coll ' Все присваивания объектов должны производится командой Set
    MsgBox coll2("key")
    Set coll2 = New Collection
    MsgBox coll2.Count
End Sub

Грабли-1. При копировании кода из IDE (в английском Excel) есь текст конвертируется в 1252 Latin-1. Поэтому, если хотите сохранить русские комментарии — надо сохранить крокозябры как Latin-1, а потом открыть в 1251.

Грабли-2. Т.к. VB позволяет использовать необъявленные переменные, я всегда в начале кода (перед всеми процедурами) ставлю строчку Option Explicit. Эта директива запрещает интерпретатору заводить переменные самостоятельно.

Грабли-3. Глобальные переменные можно объявлять только до первой функции/процедуры. Локальные — в любом месте процедуры/функции.

Еще немного дополнительных функций, которые могут пригодится: InPos, Mid, Trim, LBound, UBound. Также ответы на все вопросы по поводу работы функций/их параметров можно получить в MSDN.

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

Кодим много и под Excel

В этой части мы уже начнём кодить нечто, что умеет работать с нашими листами в Excel. Для начала создадим отдельный лист с именем result (лист с данными назовём data). Теперь, наверное, нужно этот лист очистить от того, что на нём есть. Также мы «выделим» лист с данными, чтобы каждый раз не писать длинное обращение к массиву с листами.

Sub FormatPrice()
    Sheets("result").Cells.Clear
    Sheets("data").Activate
End Sub

Работа с диапазонами ячеек

Вся работа в Excel VBA производится с диапазонами ячеек. Они создаются функцией Range и возвращают объект типа Range. У него есть всё необходимое для работы с данными и/или оформлением. Кстати сказать, свойство Cells листа — это тоже Range.

Примеры работы с Range

Sheets("result").Activate
Dim r As Range
Set r = Range("A1")
r.Value = "123"
Set r = Range("A3,A5")
r.Font.Color = vbRed
r.Value = "456"
Set r = Range("A6:A7")
r.Value = "=A1+A3"

Теперь давайте поймем алгоритм работы нашего кода. Итак, у каждой строчки листа data, начиная со второй, есть некоторые данные, которые нас не интересуют (ID, название и цена) и есть две вложенные группы, к которым она принадлежит (тип и производитель). Более того, эти строки отсортированы. Пока мы забудем про пропуски перед началом новой группы — так будет проще. Я предлагаю такой алгоритм:

  1. Считали группы из очередной строки.
  2. Пробегаемся по всем группам в порядке приоритета (вначале более крупные)
    1. Если текущая группа не совпадает, вызываем процедуру AddGroup(i, name), где i — номер группы (от номера текущей до максимума), name — её имя. Несколько вызовов необходимы, чтобы создать не только наш заголовок, но и всё более мелкие.
  3. После отрисовки всех необходимых заголовков делаем еще одну строку и заполняем её данными.

Для упрощения работы рекомендую определить следующие функции-сокращения:

Function GetCol(Col As IntegerAs String
    GetCol = Chr(Asc("A") + Col)
End FunctionFunction GetCellS(Sheet As String, Col As Integer, Row As IntegerAs Range
    Set GetCellS = Sheets(Sheet).Range(GetCol(Col) + CStr(Row))
End FunctionFunction GetCell(Col As Integer, Row As IntegerAs Range
    Set GetCell = Range(GetCol(Col) + CStr(Row))
End Function

Далее определим глобальную переменную «текущая строчка»: Dim CurRow As Integer. В начале процедуры её следует сделать равной единице. Еще нам потребуется переменная-«текущая строка в data», массив с именами групп текущей предыдущей строк. Потом можно написать цикл «пока первая ячейка в строке непуста».

Глобальные переменные

Option Explicit ' про эту строчку я уже рассказывал
Dim CurRow As Integer
Const GroupsCount As Integer = 2
Const DataCount As Integer = 3

FormatPrice

Sub FormatPrice()
    Dim I As Integer ' строка в data
    CurRow = 1
    Dim Groups(1 To GroupsCount) As String
    Dim PrGroups(1 To GroupsCount) As String

    Sheets(

"data").Activate
    I = 2
    Do While True
        If GetCell(0, I).Value = "" Then Exit Do
        ' ...
        I = I + 1
    Loop
End Sub

Теперь надо заполнить массив Groups:

На месте многоточия

Dim I2 As Integer
For I2 = 1 To GroupsCount
    Groups(I2) = GetCell(I2, I)
Next I2
' ...
For I2 = 1 To GroupsCount ' VB не умеет копировать массивы
    PrGroups(I2) = Groups(I2)
Next I2
I =  I + 1

И создать заголовки:

На месте многоточия в предыдущем куске

For I2 = 1 To GroupsCount
    If Groups(I2) <> PrGroups(I2) Then
        Dim I3 As Integer
        For I3 = I2 To GroupsCount
            AddHeader I3, Groups(I3)
        Next I3
        Exit For
    End If
Next I2

Не забудем про процедуру AddHeader:

Перед FormatPrice

Sub AddHeader(Ty As Integer, Name As String)
    GetCellS("result", 1, CurRow).Value = Name
    CurRow = CurRow + 1
End Sub

Теперь надо перенести всякую информацию в result

For I2 = 0 To DataCount - 1
    GetCellS("result", I2, CurRow).Value = GetCell(I2, I)
Next I2

Подогнать столбцы по ширине и выбрать лист result для показа результата

После цикла в конце FormatPrice

Sheets("Result").Activate
Columns.AutoFit

Всё. Можно любоваться первой версией.

Некрасиво, но похоже. Давайте разбираться с форматированием. Сначала изменим процедуру AddHeader:

Sub AddHeader(Ty As Integer, Name As String)
    Sheets("result").Range("A" + CStr(CurRow) + ":C" + CStr(CurRow)).Merge
    ' Чтобы не заводить переменную и не писать каждый раз длинный вызов
    ' можно воспользоваться блоком With
    With GetCellS("result", 0, CurRow)
        .Value = Name
        .Font.Italic = True
        .Font.Name = "Cambria"
        Select Case Ty
        Case 1 ' Тип
            .Font.Bold = True
            .Font.Size = 16
        Case 2 ' Производитель
            .Font.Size = 12
        End Select
        .HorizontalAlignment = xlCenter
    End With
    CurRow = CurRow + 1
End Sub

Уже лучше:

Осталось только сделать границы. Тут уже нам требуется работать со всеми объединёнными ячейками, иначе бордюр будет только у одной:

Поэтому чуть-чуть меняем код с добавлением стиля границ:

Sub AddHeader(Ty As Integer, Name As String)
    With Sheets("result").Range("A" + CStr(CurRow) + ":C" + CStr(CurRow))
        .Merge
        .Value = Name
        .Font.Italic = True
        .Font.Name = "Cambria"
        .HorizontalAlignment = xlCenterSelect Case Ty
        Case 1 ' Тип
            .Font.Bold = True
            .Font.Size = 16
            .Borders(xlTop).Weight = xlThick
        Case 2 ' Производитель
            .Font.Size = 12
            .Borders(xlTop).Weight = xlMedium
        End Select
        .Borders(xlBottom).Weight = xlMedium ' По убыванию: xlThick, xlMedium, xlThin, xlHairline
    End With
    CurRow = CurRow + 1
End Sub

Осталось лишь добится пропусков перед началом новой группы. Это легко:

В начале FormatPrice

Dim I As Integer ' строка в  data
CurRow = 0 ' чтобы не было пропуска в самом начале
Dim Groups(1 To GroupsCount) As String

В цикле расстановки заголовков

If Groups(I2) <> PrGroups(I2) Then
    CurRow = CurRow + 1
    Dim I3 As Integer

В точности то, что и хотели.

Надеюсь, что эта статья помогла вам немного освоится с программированием для Excel на VBA. Домашнее задание — добавить заголовки «ID, Название, Цена» в результат. Подсказка: CurRow = 0 CurRow = 1.

Файл можно скачать тут (min.us) или тут (Dropbox). Не забудьте разрешить исполнение макросов. Если кто-нибудь подскажет человеческих файлохостинг, залью туда.

Спасибо за внимание.

Буду рад конструктивной критике в комментариях.

UPD: Перезалил пример на Dropbox и min.us.

UPD2: На самом деле, при вызове процедуры с одним параметром скобки можно поставить. Либо использовать конструкцию Call Foo(«bar», 1, 2, 3) — тут скобки нужны постоянно.

Tuesday, January 12, 2010
Peltier Technical Services, Inc., Copyright © 2023, All rights reserved.
 

Today I am pleased to present a guest post written by my colleague Nicholas Hebb. Nick appears online frequently, commenting on Excel-related blogs and corresponding in the various forums.

By Nicholas Hebb

With the release of Office 2007, Microsoft rewrote the drawing tools from the ground up. On the plus side, this meant new shapes, new styles, and the addition of SmartArt. On the downside, they didn’t have time to incorporate AutoShape operations in their macro recorder prior to release. Thankfully, Excel 2010 has added macro support back, but like all macros the code generated is often bloated and relies heavily on the Selection object, which tends to hide the core objects in use. For the most part, the online help is good, but there is some information that doesn’t get explained in detail. This article attempts to provide a basic overview of working with AutoShapes using VBA and touch on some areas that are not covered extensively in the help documentation.

Definitions

Two properties of the Shape object will be used in the code samples below – Shape.Type and Shape.AutoShapeType. Excel has a broad range of shape Types consisting not only of AutoShapes, but also connectors, lines, pictures, charts, comments, and many other graphical items. For AutoShapes, the AutoShapeType property lets you get/set the type of shape as shown in the gallery image below.

Excel 2010 Autoshapes Gallery

Knowing when to check the Shape.Type property versus the Shape.AutoShapeType is very useful. For example, if the AutoShapeType value is -2, then for all practical purposes the shape is not an AutoShape. If the value is greater than 1, then the shape is one of the types display in the Shapes gallery. The tricky part comes when the AutoShapeType is 1, which equals the AutoShape constant msoShapeRectangle. It could be a Rectangle AutoShape, but it could also be anything shaped like a rectangle, such as a text box, a comment, or even a picture. So if the AutoShapeType evaluates to 1, then you also need to check the Type property.

Callouts are another special type of shape that can cause confusion. They are discussed more in the Miscellaneous Issues section below.

Accessing a Shape Object

Each worksheet contains a Shapes collection consisting of Shape objects. Like other collections in VBA, the Shape object is accessed either via its name or index number, as in:

ActiveSheet.Shapes("SHAPE_NAME")

or

ActiveSheet.Shapes(1)

Or, using the For…Each syntax:

Dim shp as Shape
For Each shp in ActiveSheet.Shapes
  MsgBox shp.Name
Next

Adding an AutoShape

The syntax for adding a shape is:

Worksheet.Shapes.AddShape(AutoShapeType, Left, Top, Width, Height)

The AutoShapeType is a constant that ranges from 1 to 137 for Excel 2003 and earlier versions. Excel 2007 added shapes 139 through 183. AutoShapeTypes 125-136 are special AutoShapes. The online help file states that they support mouse over and click events, but that only applies when they are used in PowerPoint presentations. You can use them in Excel but they don’t have any special properties.

To see what the AutoShapeType constant is for each AutoShape, you can copy and paste the following code into the Excel Visual Basic Editor and run it (or download the sample file and run the macro). Not all the AutoShapes are available in the Shapes gallery, so this will also give you a look at some of the hidden ones.

Sub CreateAutoshapes()
  Dim i As Integer
  Dim t As Integer
  Dim shp As Shape

  t = 10
  For i = 1 To 137
    Set shp = ActiveSheet.Shapes.AddShape(i, 100, t, 60, 60)
    shp.TextFrame.Characters.Text = i
    t = t + 70
  Next
  ' skip 138 - not supported
  If CInt(Application.Version) >= 12 Then
    For i = 139 To 183
      Set shp = ActiveSheet.Shapes.AddShape(i, 100, t, 60, 60)
      shp.TextFrame.Characters.Text = i
      t = t + 70
    Next
  End If
End Sub

The Left, Top, Width, and Height parameters of AddShape() are specified in points. The origin is the top left corner of the worksheet, with the Left and Top values increasing to the right and down, respectively. Dealing with points on a worksheet isn’t intuitive, so if you prefer you can add a shape to a given range address by using code like this:

Function AddShapeToRange(ShapeType As MsoAutoShapeType, sAddress As String) As Shape
  With ActiveSheet.Range(sAddress)
    Set AddShapeToRange = ActiveSheet.Shapes.AddShape(ShapeType, _
        .Left, .Top, .Width, .Height)
  End With
End Function

Adding Text to an AutoShape

The Shape object has both a TextFrame and TextFrame2 members. The TextFrame2 member was added in Excel 2007 and gives better control over the formatting of the text. Because it is not backward compatible, I would recommend using the TextFrame object, as shown in the following code.

Sub AddFormattedTextToShape(oShape As Shape, sText As String)
  If Len(sText) > 0 Then
    With oShape.TextFrame
      .Characters.Text = sText
      .Characters.Font.Name = "Garamond"
      .Characters.Font.Size = 12
      .HorizontalAlignment = xlHAlignCenter
      .VerticalAlignment = xlVAlignCenter
    End With
  End If
End Sub

Setting Border and Fill Styles

If you take advantage of the built-in styles for Excel 2007 and Excel 2010, setting the AutoShape formatting is ridiculously easy compared to Excel 2003 and previous versions. Excel 2007 introduced the ShapeStyle property with the 42 preset styles shown below.

The style numbers can be set using a simple line of code:

Shape.ShapeStyle = msoShapeStylePresetXX

Where Shape is the shape object and XX is the style number. The style numbers are shown in the image gallery in order from left to right, top to bottom. For example, the red button in the second row is msoShapeStylePreset10.

Adding Connectors and Lines

Connectors and lines are different objects in Excel. Connectors are special lines  that “connect” to shapes, and if the shape is moved the connector stays connected and reroutes accordingly. Connectors cannot connect to other connectors, but they can connect to the end point of a line.

The syntax for adding a line is straightforward:

Worksheet.Shapes.AddLine(BeginX, BeginY, EndX, EndY)

…with all coordinates as Singles. Adding a connector is a bit more complex, since you typically want it to connect two shapes. The code below calculates the begin and end points, creates the connector, attaches the connector to the two shapes, then finally does a reroute to ensure the shortest path.

Function AddConnectorBetweenShapes(ConnectorType As MsoConnectorType, _
    oBeginShape As Shape, oEndShape As Shape) As Shape

  Const TOP_SIDE As Integer = 1
  Const BOTTOM_SIDE As Integer = 3
  Dim oConnector As Shape
  Dim x1 As Single
  Dim x2 As Single
  Dim y1 As Single
  Dim y2 As Single

  With  oBeginShape
    x1 = .Left + .Width / 2
    y1 = .Top + .Height
  End With
  With oEndShape
    x2 = .Left + .Width / 2
    y2 = .Top
  End With
  If CInt(Application.Version) < 12 Then
    x2 = x2 - x1
    y2 = y2 - y1
  End If

  Set oConnector = ActiveSheet.Shapes.AddConnector(ConnectorType, x1, y1, x2, y2)
  oConnector.ConnectorFormat.BeginConnect oBeginShape, BOTTOM_SIDE
  oConnector.ConnectorFormat.EndConnect oEndShape, TOP_SIDE
  oConnector.RerouteConnections

  Set AddConnectorBetweenShapes = oConnector

  Set oConnector = Nothing 
End Function

Several points worth mentioning are:

  • The ConnectorType can be one of three constants – msoConnectorCurve, msoConnectorElbow, or msoConnectorStraight.
  • The calculations for the beginning and ending points are not normally needed. You could put any values in for the AddConnector() function because once you call BeginConnect and EndConnect, the connector is attached to the shapes and the begin and end points get set automatically.
  • How the end coordinates are specified is not consistent between Excel versions. Prior to Excel 2007, the end coordinates were relative to the begin coordinates. Starting in Excel 2007, the function now uses absolute coordinates.
  • When you route a Connector to an AutoShape, you need to specify the side using a connection site constant. The constants are different for each AutoShape type, but generally they start with the top side = 1 and go counter-clockwise. For example, most rectangular shapes have connection site constants where top = 1, left = 2, bottom = 3, and right = 4.
  • When you call the RerouteConnections() function, it sets the connection sides automatically in order to create the shortest path between the two shapes. So, unless you want a specific routing, you can get usually just guess at the connection site values then call RerouteConnections().

Formatting Connectors and Lines

Like AutoShapes, formatting Connectors and Lines is fairly straightforward in Excel 2007 and 2010. Here is a comparison of two formatting routines for older versions of Excel versus the newer versions:

Sub FormatConnector2003(oConnector As Shape)
  With oConnector
    If .Connector Or .Type = msoLine Then
      ' rough approximation of the Excel 2007 preset line style #17
      .Line.EndArrowheadStyle = msoArrowheadTriangle
      .Line.Weight = 2
      .Line.ForeColor.RGB = RGB(192, 80, 77)
      .Shadow.Type = msoShadow6
      .Shadow.IncrementOffsetX -4.5
      .Shadow.IncrementOffsetY -4.5
      .Shadow.ForeColor.RGB = RGB(192, 192, 192)
      .Shadow.Transparency = 0.5
      .Visible = msoTrue
    End If
  End With
End Sub

Sub FormatConnector2007(oConnector As Shape)
  With oConnector
    If .Connector Or .Type = msoLine Then
      .Line.EndArrowheadStyle = msoArrowheadTriangle
      .ShapeStyle = msoLineStylePreset17
    End If
  End With
End Sub

The Connector property, used above, returns a Boolean indicating whether the shape is a connector. The Type=msoLine statement checks if the shape is a line. In this case the code will format both connectors and lines the same way, but at times you may want handle them separately. (NB: The Insert Shapes gallery of Excel 2007 only lets you add Connectors, not Lines. So unless you are dealing with legacy files or add Lines via code, testing Type=msoLine may never be an issue for you.)

Like the shape styles, you can format the line style by setting the ShapeStyle to one of the msoLineStylePresetXX values, where XX matches the order they appear in the style gallery (below) from left to right, top to bottom.

The Line object has several other members worth mentioning. In addition to the EndArrowheadStyle shown above, there is a corresponding BeginArrowheadStyle property, a DashStyle property, and also a Style property that lets you create double lines.

Miscellaneous Issues

Here are a few Excel 2007 issues with AutoShapes that are good to be aware of :

  • If you copy a set of shapes, older versions of Office gave the option to Paste Special as editable AutoShapes in other Office applications. This option no longer exists in Office 2007. I haven’t tested this in Office 2010 beta yet.
  • In Excel 2007 and the 2010 beta, changing the AutoShape type will disconnect any incoming or outgoing Connectors to a shape.
  • Some print drivers (including PDF export handlers) do not handle printing thick Connectors well, e.g., elbow connectors may straighten. If this happens, either change the line thickness or try grouping all the shapes together prior to printing.
  • The Arc is an AutoShape but needs to be treated as a line when setting the ShapeStyle property.
  • Most of the styles are backward compatible except for styles 37-42 (the glossy button look in the bottom row of the style gallery). Styles 31-36 (the second row from bottom) do not render very well in Excel 2000.
  • You can add Callouts using the AddShape() or AddCallout() methods, but the AddCallout() method will only let you add four types, two of which are actually the same. Callouts have AutoShapeType values in the range of 105-124. Even though they have AutoShapeType values, the Shape.Type property will return msoCallout – not msoAutoShape. Confused? Wait, there’s more. The callouts with AutoShapeTypes from 105-108 actually return a Shape.Type = msoAutoShape – not msoCallout.

Sample File

The sample file includes three demo sheets. The ShapeDemo sheet contains a macro to add two shapes, format them, then add a connector and format it. The Animation sheet has a simple macro showing how to move a shape around the sheet. The CreateAutoShapes sheet has a macro to create all AutoShapes available in your version of Excel.

The ShapeDemo routine has two function calls that are commented out – IlluminateShapeText2003() and IlluminateShapeText2007(). These subs add some gaudy formatting to the first letter of each text block, but they serve to highlight some of the differences between Excel 2007 and previous versions. Two parts of the code worth looking at are the HasText property and the formatting properties of TextFrame2. With the old TextFrame object, you would have to try accessing the TextFrame.Characters.Count property, which throws an error if no text exists. As for the formatting, the font colors in Excel 2003 and previous were limited to the colors in the pallet. In Excel 2007 and 2010, you can add reflections, drop shadows, and glow as well as set the color to whatever RGB value your heart desires.

Lastly, there is a module MCommon containing a sub that deletes all the AutoShapes on a sheet. In order not to delete the command buttons on the sheet (which are shapes too), it creates a ShapeRange of the AutoShapes and deletes that. The online help file shows the syntax for creating a ShapeRange when you know the names of the shapes at design time, but the syntax is a bit tricky when creating one dynamically. The DeleteAllAutoShapes() sub in the sample file shows you how to do this.

Click Here to Download the Sample File

About the Author

FlowBreeze Flowchart SoftwareNicholas Hebb is the founder of BreezeTree Software and author of FlowBreeze Flowchart Software, an add-in that automates flowcharting in Excel.

As typical small-business user, when my laptop broke I HAD to upgrade to Office 2010 (2007 isn’t on market anymore). Now I have a bunch of quick-and-dirty VBA to go with my old worksheets. None of them were programming masterpieces, but those worked and got work done with excel, and office 2007.

Now I tried to use my old stuff on Office 2010, and everything is just little bit off to be usable. For two days I searched web, trying to find any documentation for the end user, no luck. I found a lot of blogs aimed to professional programmers who are hoping to make bullet proof code to serve anything from Office 0 to Office2010, 32 bits to 64 bits (No 8 bits here?), but none of the articles talked about the main business, The End User with whole lot of homemade coding to survive a day.

How do I make my old 2007 VBA to work with 2010?

No, I don’t need it to backwards compatible with anything, it is enough that it works with my new computer, and new software (In my case it is 64 bits, if anyone wonders). I just need to know why my code doesn’t work anymore, and what to do about it.
What I really want is some pointers to real information, if there is any!
Of course I could copy-paste every piece of my code to programmers discussion forums, get laughed at, and after some weeks I might get some of them to work, but what I really need is real information written to basic VBA user. I need to learn this.

So What Is Your Problem?

What has changed?

Calendar active-X component was missing. I found solution: http://answers.microsoft.com/en-us/office/forum/officeversion_other-customize/missing-calendar-control/03ad5d05-ca3f-4081-9989-e757223ebdde now I just have to redo every calendar on all my forms… Thanks.

Textbox.Text wasn’t working, I found and run Microsoft Excel Code Compatibility Inspector (CII), and it showed that I have few thousand of those Textbox.Text elements that are “Deprecated” – “Potentially contains removed items the object model” – What?

It keeps getting better, same Microsoft software stated:
“TYPE: DEPRECATION
ITEM: [xls]SmartTagRecognizer.FullName
URL: http://go.microsoft.com/?linkid=9719761
CODE: MyFullName = ThisWorkbook.FullName””
No explanation… (Btw. only way to stop the Inspector and see the results was CRT-ALT-DEL –Stop…)

I visited the links given by the Inspector, no help there.

I run the Inspector few times, and It gave different results every time. Now that is interesting.

I hear your problem, now show me your problem

Where do I start. Here is one.
I have a form where user can make a new ‘appointment’, when saving, VBA creates piece of code and new Shape into calendar worksheet (and to Outlook, but that is not the point here). When user comes back and clicks that shape – button – Button runs a piece of code that was created. here is the code:

Private Sub myMacro2001_Click()
    Dim meetingId As Integer
    meetingId = 2001
    Load formHours
    Call formHours.selectMeeting(meetingId)
    formHours.Show vbModeless
End Sub

Which is supposed, and did back in 2007, open up form formHours with information stored for that meeting 2001 (meetingId)

And the code in formHours starts as:

Sub selectMeeting (ByRef IdNo As Integer) ’Bring in the meeting ID
Meeting = IdNo
    ….

Not very elegant, but it worked. Doesn’t do that anymore.

If you can help me with this case, I appreciate, But it would be even better if you inform me where was I supposed to find the answer.
What has changed? Worked before, doesn’t anymore. What to do?

(PS. I know my code is crappy, but it worked… And I just would like to find out what has changed, and how do I need to change.

Ps.Ps Yes I know I should be asking Microsoft, but you know how that is…)

FOLLOWUP

After some kind advice from Barrowc and others I managed to fix some of problems. Changing Text.Text to Text.Value created bunch of new problems, some of Text-Values were used on formulas, and now I needed to change them Val(FooTextBox.Value)

What is really surprising is that 2010 seems very SLOW! I ran 2007 and 2010 side by side at office, and 2007 won hands down. One of my workers had already invoiced a customer using 2007, when 2010 was still opening up! Funny, since 2007 was running on AMD Athlon X2 Dual-Core with limited memory, and 2010 was on my new laptop with Core i7-740QM, 6 GB, both on win7-64. I surfed the net and didn’t find any complaints that VBA7 on Office 2010 is so much slower than VBA6 on Office2007. I don’t know if this is just my problem, but my employees voted for 2007 single minded…

  1. 11-28-2013, 08:46 AM


    #1

    AdLoki is offline


    Registered User


    Exclamation VBA compatibility issues: Excel 2007, 2010, 2013

    Hi All,

    I have been working for months on a series of projects that are due to be submitted imminently. The agreement with the client was that the VBA functionality will be compatible with Excel 2007, 2010 and 2013, both 32-bit and 64-bit versions.

    To date, I have been working and testing in 2010 and 2013 (32-bit) and have had no problems but the tools have just been run on a client’s machine running 2007 and it’s started throwing up a load of errors I haven’t encountered before now. My problem is that I don’t have a copy of Excel 2007 to test it with, making identifying the cause of the conflicts very difficult. I have included error handling routines in the code so I am able to identify which subs the problems arise in and the type of error code being generated, but there are hundreds of subroutines and some of them are quite long so pinning the issues to specific lines is proving tricky.

    My question is: is there any way of testing VBA code for compatibility with specific versions of Excel? Or at the very least, is it possible to use some form of emulator to replicate earlier versions of the software? I have an Office 365 subscription for what it’s worth…

    Any suggestions would be very welcomed!


  2. 11-28-2013, 09:41 AM


    #2

    Re: VBA compatibility issues: Excel 2007, 2010, 2013

    I have never come across such a thing but I am surprised you have so many errors between those versions. Are you using API calls in these workbooks?


  3. 11-28-2013, 09:56 AM


    #3

    Re: VBA compatibility issues: Excel 2007, 2010, 2013

    Usually the best approach when writing cross version code is to start with the oldest version, but that ship has already sailed

    You will need a copy of each version/bit application in order to test.
    Note 32 and 64 bit can not be installed together. So you will also need a separate windows environment for that part, which you can create via a Virtualized pc.

    I assume you included costs for all those licences?


  4. 11-28-2013, 10:07 AM


    #4

    AdLoki is offline


    Registered User


    Re: VBA compatibility issues: Excel 2007, 2010, 2013

    I am using API calls but the errors do not derive from the module that contains them. It seems that (at least some of) the errors are stemming from the following subroutine:

    Essentially (all the fiddly details aside) the routine takes an image control in a userform and loads a picture to it which is saved in a worksheet, embedded in a chart frame; the chart frame is first re-sized to match the dimensions of the control (this had to be done to accommodate different local resolution settings). The image in the worksheet is then exported to a static image file («~TempImg.xxx»), which is loaded to the control and then the temporary file is deleted. All pretty standard and straight forward and not a problem in Excel 2010 and 2013 but it 2007 it falls down.

    It seems to fall down specifically when it is being called multiple times in quick succession — the same routine is used without a problem to load images at other points in the code. My suspicion is that the code is running faster than the excel 2007 system can export the image file, load it and delete it. I have tried using a sleep procedure to add a pause in the code to allow the system to «catch up», I have tried adding DoEvents (which, incidentally, I have never found to make a difference in any scenario) but to no avail.

    The most common errors are 70 (permission denied) and 75 (path/file access error), which I think lends weight to my suspicion. Any thoughts?


  5. 11-28-2013, 10:16 AM


    #5

    Re: VBA compatibility issues: Excel 2007, 2010, 2013

    You could try using the Temporary folder to export to in case the folder has protect priviliges.

    You could try testing to make sure the file exists before loading it.


  6. 11-28-2013, 10:36 AM


    #6

    AdLoki is offline


    Registered User


    Re: VBA compatibility issues: Excel 2007, 2010, 2013

    Hi Andy,

    I have 2010 and 2013 running on the same machine but that’s it. The cost of the licenses were not included (to be honest I have been shafted from beginning to end in terms of workload/money but that’s another issue and won’t be repeated!).

    I guess my only solution would be to install Excel 2007 on top of the other two versions but I know that’s going to give me no end of trouble and is not really feasible now on the time scales involved. To be honest, when I started working on the project everyone was using 2007 or 2010, now a lot of the user base has upgraded to 2013, some of them 64-bit (stupid idea if you ask me but there we are) and I have spent a lot of time fighting fires in those versions — it’s all very frustrating to now be finding issues in 2007 (and inconsistent issues at that!).

    I founded my company off the back of work similar to this, creating very complex interfaces in VBA to accommodate Excel statistical models (public sector work). I’m about to start transferring all of my products over to web platforms so hopefully my days of battling with these compatibility issues in Excel are drawing to a close…

    Thanks, as always, for your valuable input.
    AdLoki


  7. 11-28-2013, 10:44 AM


    #7

    Re: VBA compatibility issues: Excel 2007, 2010, 2013

    Virtual PC will probably make testing, without messing with your pc, easier.
    http://www.microsoft.com/en-gb/downl…s.aspx?id=3702


  8. 11-28-2013, 10:55 AM


    #8

    AdLoki is offline


    Registered User


    Re: VBA compatibility issues: Excel 2007, 2010, 2013

    Hi, yeah I had previously included the line

    but it didn’t help. The directory in question should be ok as the user will have previously saved the parent file there.

    I am now of the opinion that the procedure of exporting the image to a file, uploading to the VBA control and deleting the temporary file is causing a conflict because of system lag — the VBA is running faster than windows can process the file creation/deletion, resulting in the code effectively being bottlenecked and crashing. It could actually have more to do with the computer than the version of Excel. Interestingly, I just remoted onto a machine where the error was occurring every time and it doesn’t happen while I am connected via WebEx. I reckon the additional delay caused by the remote operation is just enough to allow the system to catch up. Does that sound feasible?


  9. 11-28-2013, 11:00 AM


    #9

    Re: VBA compatibility issues: Excel 2007, 2010, 2013

    Maybe.

    You could try FileExists test with a loop, you would need to pause (use the Sleep API for millisecond control) before additional checks. Also control the number of times the loop happens before your code continues with the display or error reports to the user.


  10. 11-28-2013, 11:06 AM


    #10

    AdLoki is offline


    Registered User


    Re: VBA compatibility issues: Excel 2007, 2010, 2013

    That’s a really good idea, I might just give that a go — thanks.


  11. 11-29-2013, 07:01 AM


    #11

    AdLoki is offline


    Registered User


    Re: VBA compatibility issues: Excel 2007, 2010, 2013

    Andy, I added the following code

    It seems to have done the job, so thanks for the suggestion!


Понравилась статья? Поделить с друзьями:
  • Visual basic 2010 and excel
  • Vista and microsoft word
  • Visiting interesting places word
  • Visio конвертировать в excel
  • Visio drawing in word