Excel vba for cells value

In this Article

  • VBA Loop Quick Examples
    • For Each Loops
    • For Next Loops
    • Do While Loops
    • Do Until Loops
  • VBA Loop Builder
  • VBA For Next Loop
    • For Loop Syntax
    • For Loop Step
    • For Loop Step – Inverse
    • Nested For Loop
    • Exit For
    • Continue For
  • VBA For Each Loop
    • For Each Cell in Range
    • For Each Worksheet in Workbook
    • For Each Open Workbook
    • For Each Shape in Worksheet
    • For Each Shape in Each Worksheet in Workbook
    • For Each – IF Loop
  • VBA Do While Loop
    • Do While
    • Loop While
  • VBA Do Until Loop
    • Do Until
    • Loop Until
  • Exit Do Loop
  • End or Break Loop
  • More Loop Examples
    • Loop Through Rows
    • Loop Through Columns
    • Loop Through Files in a Folder
    • Loop Through Array
  • Loops in Access VBA

To work effectively in VBA, you must understand Loops.

Loops allow you to repeat a code block a set number of times or repeat a code block on a each object in a set of objects.

First we will show you a few examples to show you what loops are capable of. Then we will teach you everything about loops.

VBA Loop Quick Examples

For Each Loops

For Each Loops loop through every object in a collection, such as every worksheet in workbook or every cell in a range.

Loop Through all Worksheets in Workbook

This code will loop through all worksheets in the workbook, unhiding each sheet:

Sub LoopThroughSheets()
Dim ws As Worksheet
 
    For Each ws In Worksheets
        ws.Visible = True
    Next
 
End Sub

Loop Through All Cells in Range

This code will loop through a range of cells, testing if the cell value is negative, positive, or zero:

Sub If_Loop()
Dim Cell as Range
 
  For Each Cell In Range("A2:A6")
    If Cell.Value > 0 Then
      Cell.Offset(0, 1).Value = "Positive"
    ElseIf Cell.Value < 0 Then
      Cell.Offset(0, 1).Value = "Negative"
    Else
      Cell.Offset(0, 1).Value = "Zero"
     End If
  Next Cell
 
End Sub

vba else if statement

For Next Loops

Another type of “For” Loop is the For Next Loop.  The For Next Loop allows you to loop through integers.

This code will loop through integers 1 through 10, displaying each with a message box:

Sub ForLoop()
    Dim i As Integer
    For i = 1 To 10
        MsgBox i
    Next i
End Sub

Do While Loops

Do While Loops will loop while a condition is met. This code will also loop through integers 1 through 10, displaying each with a message box.

Sub DoWhileLoop()
    Dim n As Integer
    n = 1
    Do While n < 11
        MsgBox n
        n = n + 1
    Loop
End Sub

Do Until Loops

Conversely, Do Until Loops will loop until a condition is met. This code does the same thing as the previous two examples.

Sub DoUntilLoop()
    Dim n As Integer
    n = 1
    Do Until n >= 10
        MsgBox n
        n = n + 1
    Loop
End Sub

We will discuss this below, but you need to be extremely careful when creating Do While or Do Until loops so that you don’t create a never ending loop.

VBA Loop Builder

vba loop builder

This is a screenshot of the “Loop Builder” from our Premium VBA Add-in: AutoMacro. The Loop Builder allows you to quickly and easily build loops to loop through different objects, or numbers. You can perform actions on each object and/or select only objects that meet certain criteria.

The add-in also contains many other code builders, an extensive VBA code library, and an assortment of coding tools. It’s a must have for any VBA developer.

Now we will cover the different types of loops in depth.

VBA Coding Made Easy

Stop searching for VBA code online. Learn more about AutoMacro — A VBA Code Builder that allows beginners to code procedures from scratch with minimal coding knowledge and with many time-saving features for all users!

automacro

Learn More

VBA For Next Loop

For Loop Syntax

The For Next Loop allows you to repeat a block of code a specified number of times. The syntax is:

[Dim Counter as Integer]

For Counter = Start to End [Step Value]
    [Do Something]
Next [Counter]

Where the items in brackets are optional.

  • [Dim Counter as Long] – Declares the counter variable. Required if Option Explicit is declared at the top of your module.
  • Counter – An integer variable used to count
  • Start – The start value (Ex. 1)
  • End – The end value (Ex. 10)
  • [Step Value] – Allows you to count every n integers instead of every 1 integer. You can also go in reverse with a negative value (ex. Step -1)
  • [Do Something] – The code that will repeat
  • Next [Counter] – Closing statement to the For Next Loop. You can include the Counter or not. However, I strongly recommend including the counter as it makes your code easier to read.

If that’s confusing, don’t worry. We will review some examples:

Count to 10

This code will count to 10 using a For-Next Loop:

Sub ForEach_CountTo10()

Dim n As Integer
For n = 1 To 10
    MsgBox n
Next n

End Sub

For Loop Step

Count to 10 – Only Even Numbers

This code will count to 10 only counting even numbers:

Sub ForEach_CountTo10_Even()

Dim n As Integer
For n = 2 To 10 Step 2
    MsgBox n
Next n

End Sub

Notice we added “Step 2”. This tells the For Loop to “step” through the counter by 2.  We can also use a negative step value to step in reverse:

VBA Programming | Code Generator does work for you!

For Loop Step – Inverse

Countdown from 10

This code will countdown from 10:

Sub ForEach_Countdown_Inverse()

Dim n As Integer
For n = 10 To 1 Step -1
    MsgBox n
Next n
MsgBox "Lift Off"

End Sub

Delete Rows if Cell is Blank

I’ve most frequently used a negative step For-Loop to loop through ranges of cells, deleting rows that meet certain criteria.  If you loop from the top rows to the bottom rows, as you delete rows you will mess up your counter.

This example will delete rows with blank cells (starting from the bottom row):

Sub ForEach_DeleteRows_BlankCells()

Dim n As Integer
For n = 10 To 1 Step -1
    If Range("a" & n).Value = "" Then
        Range("a" & n).EntireRow.Delete
    End If
Next n

End Sub

Nested For Loop

You can “nest” one For Loop inside another For Loop. We will use Nested For Loops to create a multiplication table:

Sub Nested_ForEach_MultiplicationTable()

Dim row As Integer, col As Integer

For row = 1 To 9
    For col = 1 To 9
        Cells(row + 1, col + 1).Value = row * col
    Next col
Next row

End Sub

vba nested for loop

Exit For

The Exit For statement allows you to exit a For Next loop immediately.

You would usually use Exit For along with an If Statement, exiting the For Next Loop if a certain condition is met.

For example, you might use a For Loop to find a cell. Once that cell is found, you can exit the loop to speed up your code.

This code will loop through rows 1 to 1000, looking for “error” in column A. If it’s found, the code will select the cell, alert you to the found error, and exit the loop:

Sub ExitFor_Loop()

Dim i As Integer
 
For i = 1 To 1000
    If Range("A" & i).Value = "error" Then
        Range("A" & i).Select
        MsgBox "Error Found"
        Exit For
    End If
Next i

End Sub

Important: In the case of Nested For Loops, Exit For only exits the current For Loop, not all active Loops.

Continue For

VBA does not have the “Continue” command that’s found in Visual Basic. Instead, you will need to use “Exit”.

AutoMacro | Ultimate VBA Add-in | Click for Free Trial!

VBA For Each Loop

The VBA For Each Loop will loop through all objects in a collection:

  • All cells in a range
  • All worksheets in a workbook
  • All shapes in a worksheet
  • All open workbooks

You can also use Nested For Each Loops to:

  • All cells in a range on all worksheets
  • All shapes on all worksheets
  • All sheets in all open workbooks
  • and so on…

The syntax is:

For Each Object in Collection
[Do Something]
Next [Object]

Where:

  • Object – Variable representing a Range, Worksheet, Workbook, Shape, etc. (ex. rng)
  • Collection – Collection of objects (ex. Range(“a1:a10”)
  • [Do Something] – Code block to run on each object
  • Next [Object] – Closing statement. [Object] is optional, however strongly recommended.

For Each Cell in Range

This code will loop through each cell in a range:

Sub ForEachCell_inRange()

Dim cell As Range

For Each cell In Range("a1:a10")
    cell.Value = cell.Offset(0,1).Value
Next cell

End Sub

For Each Worksheet in Workbook

This code will loop through all worksheets in a workbook, unprotecting each sheet:

Sub ForEachSheet_inWorkbook()

Dim ws As Worksheet

For Each ws In Worksheets
    ws.Unprotect "password"
Next ws

End Sub

For Each Open Workbook

This code will save and close all open workbooks:

Sub ForEachWB_inWorkbooks()

Dim wb As Workbook

For Each wb In Workbooks
    wb.Close SaveChanges:=True
Next wb

End Sub

AutoMacro | Ultimate VBA Add-in | Click for Free Trial!

For Each Shape in Worksheet

This code will delete all shapes in the active sheet.

Sub ForEachShape()

Dim shp As Shape

For Each shp In ActiveSheet.Shapes
    shp.Delete
Next shp

End Sub

For Each Shape in Each Worksheet in Workbook

You can also nest For Each Loops. Here we will loop through all shapes in all worksheets in the active workbook:

Sub ForEachShape_inAllWorksheets()

Dim shp As Shape, ws As Worksheet

For Each ws In Worksheets
    For Each shp In ws.Shapes
        shp.Delete
    Next shp
Next ws

End Sub

For Each – IF Loop

As we’ve mentioned before, you can use an If statement within a loop, performing actions only if certain criteria is met.

This code will hide all blank rows in a range:

Sub ForEachCell_inRange()

Dim cell As Range

For Each cell In Range("a1:a10")
    If cell.Value = "" Then _
       cell.EntireRow.Hidden = True
Next cell

End Sub

VBA Do While Loop

The VBA Do While and Do Until (see next section) are very similar. They will repeat a loop while (or until) a condition is met.

The Do While Loop will repeat a loop while a condition is met.

Here is the Do While Syntax:

Do While Condition
[Do Something]
Loop

Where:

  • Condition – The condition to test
  • [Do Something] – The code block to repeat

You can also set up a Do While loop with the Condition at the end of the loop:

Do 
[Do Something]
Loop While Condition

We will demo each one and show how they differ:

AutoMacro | Ultimate VBA Add-in | Click for Free Trial!

Do While

Here is the Do While loop example we demonstrated previously:

Sub DoWhileLoop()
    Dim n As Integer
    n = 1
    Do While n < 11
        MsgBox n
        n = n + 1
    Loop
End Sub

Loop While

Now let’s run the same procedure, except we will move the condition to the end of the loop:

Sub DoLoopWhile()
    Dim n As Integer
    n = 1
    Do
        MsgBox n
        n = n + 1
    Loop While n < 11
End Sub

VBA Do Until Loop

Do Until Loops will repeat a loop until a certain condition is met. The syntax is essentially the same as the Do While loops:

Do Until Condition
[Do Something]
Loop

and similarly the condition can go at the start or the end of the loop:

Do 
[Do Something]
Loop Until Condition

Do Until

This do Until loop will count to 10, like our previous examples

Sub DoUntilLoop()
    Dim n As Integer
    n = 1
    Do Until n > 10
        MsgBox n
        n = n + 1
    Loop
End Sub

AutoMacro | Ultimate VBA Add-in | Click for Free Trial!

Loop Until

This Loop Until loop will count to 10:

Sub DoLoopUntil()
    Dim n As Integer
    n = 1
    Do
        MsgBox n
        n = n + 1
    Loop Until n > 10
End Sub

Exit Do Loop

Similar to using Exit For to exit a For Loop, you use the Exit Do command to exit a Do Loop immediately

Exit Do

Here is an example of Exit Do:

Sub ExitDo_Loop()

Dim i As Integer
i = 1 

Do Until i > 1000
    If Range("A" & i).Value = "error" Then
        Range("A" & i).Select
        MsgBox "Error Found"
        Exit Do
    End If
    i = i + 1
Loop

End Sub

End or Break Loop

As we mentioned above, you can use the Exit For or Exit Do to exit loops:

Exit For
Exit Do

However, these commands must be added to your code before you run your loop.

If you are trying to “break” a loop that’s currently running, you can try pressing ESC or CTRL + Pause Break on the keyboard. However, this may not work.  If it doesn’t work, you’ll need to wait for your loop to end or, in the case of an endless loop, use CTRL + ALT + Delete to force close Excel.

This is why I try to avoid Do loops, it’s easier to accidentally create an endless loop forcing you to restart Excel, potentially losing your work.

More Loop Examples

AutoMacro | Ultimate VBA Add-in | Click for Free Trial!

Loop Through Rows

This will loop through all the rows in a column:

Public Sub LoopThroughRows()
 
Dim cell As Range
 
For Each cell In Range("A:A")
    If cell.value <> "" Then MsgBox cell.address & ": " & cell.Value
Next cell
 
End Sub

Loop Through Columns

This will loop through all columns in a row:

Public Sub LoopThroughColumns()

Dim cell As Range

For Each cell In Range("1:1")
    If cell.Value <> "" Then MsgBox cell.Address & ": " & cell.Value
Next cell

End Sub

Loop Through Files in a Folder

This code will loop through all files in a folder, creating a list:

Sub LoopThroughFiles ()

Dim oFSO As Object
Dim oFolder As Object
Dim oFile As Object
Dim i As Integer

Set oFSO = CreateObject("Scripting.FileSystemObject")

Set oFolder = oFSO.GetFolder("C:Demo)

i = 2

For Each oFile In oFolder.Files
    Range("A" & i).value = oFile.Name
    i = i + 1
Next oFile

End Sub

Loop Through Array

This code will loop through the array ‘arrList’:

For i = LBound(arrList) To UBound(arrList)
    MsgBox arrList(i)
Next i

The LBound function gets the “lower bound” of the array and UBound gets the “upper bound”.

Loops in Access VBA

Most of the examples above will also work in Access VBA. However, in Access, we loop through the Recordset Object rather than the Range Object.

Sub LoopThroughRecords()
   On Error Resume Next
   Dim dbs As Database
   Dim rst As Recordset
   Set dbs = CurrentDb
   Set rst = dbs.OpenRecordset("tblClients", dbOpenDynaset)
   With rst
      .MoveLast
      .MoveFirst
      Do Until .EOF = True
         MsgBox (rst.Fields("ClientName"))
        .MoveNext
     Loop
   End With
   rst.Close
   Set rst = Nothing
   Set dbs = Nothing
End Sub

Всё о работе с ячейками в Excel-VBA: обращение, перебор, удаление, вставка, скрытие, смена имени.

Содержание:

Table of Contents:

  • Что такое ячейка Excel?
  • Способы обращения к ячейкам
    • Выбор и активация
    • Получение и изменение значений ячеек
      • Ячейки открытой книги
      • Ячейки закрытой книги 
    • Перебор ячеек
    • Перебор в произвольном диапазоне
  • Свойства и методы ячеек
    • Имя ячейки
    • Адрес ячейки
    • Размеры ячейки
  • Запуск макроса активацией ячейки

2 нюанса:

  1. Я почти везде стараюсь использовать ThisWorkbook (а не, например, ActiveWorkbook) для обращения к текущей книге, в которой написан этот код (считаю это наиболее безопасным для новичков способом обращения к книгам, чтобы случайно не внести изменения в другие книги). Для экспериментов можете вставлять этот код в модули, коды книги, либо листа, и он будет работать только в пределах этой книги. 
  2. Я использую английский эксель и у меня по стандарту листы называются Sheet1, Sheet2 и т.д. Если вы работаете в русском экселе, то замените Thisworkbook.Sheets(«Sheet1») на Thisworkbook.Sheets(«Лист1»). Если этого не сделать, то вы получите ошибку в связи с тем, что пытаетесь обратиться к несуществующему объекту. Можно также заменить на Thisworkbook.Sheets(1), но это менее безопасно.

Что такое ячейка Excel?

В большинстве мест пишут: «элемент, образованный пересечением столбца и строки». Это определение полезно для людей, которые не знакомы с понятием «таблица». Для того, чтобы понять чем на самом деле является ячейка Excel, необходимо заглянуть в объектную модель Excel. При этом определения объектов «ряд», «столбец» и «ячейка» будут отличаться в зависимости от того, как мы работаем с файлом.

Объекты в Excel-VBA. Пока мы работаем в Excel без углубления в VBA определение ячейки как «пересечения» строк и столбцов нам вполне хватает, но если мы решаем как-то автоматизировать процесс в VBA, то о нём лучше забыть и просто воспринимать лист как «мешок» ячеек, с каждой из которых VBA позволяет работать как минимум тремя способами:

  1. по цифровым координатам (ряд, столбец),
  2. по адресам формата А1, B2 и т.д. (сценарий целесообразности данного способа обращения в VBA мне сложно представить)
  3. по уникальному имени (во втором и третьем вариантах мы будем иметь дело не совсем с ячейкой, а с объектом VBA range, который может состоять из одной или нескольких ячеек). Функции и методы объектов Cells и Range отличаются. Новичкам я бы порекомендовал работать с ячейками VBA только с помощью Cells и по их цифровым координатам и использовать Range только по необходимости.

Все три способа обращения описаны далее

Как это хранится на диске и как с этим работать вне Excel? С точки зрения хранения и обработки вне Excel и VBA. Сделать это можно, например, сменив расширение файла с .xls(x) на .zip и открыв этот архив.

Пример содержимого файла Excel:

Далее xl -> worksheets и мы видим файл листа

Содержимое файла:

 То же, но более наглядно:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac xr xr2 xr3" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac" xmlns:xr="http://schemas.microsoft.com/office/spreadsheetml/2014/revision" xmlns:xr2="http://schemas.microsoft.com/office/spreadsheetml/2015/revision2" xmlns:xr3="http://schemas.microsoft.com/office/spreadsheetml/2016/revision3" xr:uid="{00000000-0001-0000-0000-000000000000}">
	<dimension ref="B2:F6"/>
	<sheetViews>
		<sheetView tabSelected="1" workbookViewId="0">
			<selection activeCell="D12" sqref="D12"/>
		</sheetView>
	</sheetViews>
	<sheetFormatPr defaultRowHeight="14.4" x14ac:dyDescent="0.3"/>
	<sheetData>
		<row r="2" spans="2:6" x14ac:dyDescent="0.3">
			<c r="B2" t="s">
				<v>0</v>
			</c>
		</row>
		<row r="3" spans="2:6" x14ac:dyDescent="0.3">
			<c r="C3" t="s">
				<v>1</v>
			</c>
		</row>
		<row r="4" spans="2:6" x14ac:dyDescent="0.3">
			<c r="D4" t="s">
				<v>2</v>
			</c>
		</row>
		<row r="5" spans="2:6" x14ac:dyDescent="0.3">
			<c r="E5" t="s">
				<v>0</v></c>
		</row>
		<row r="6" spans="2:6" x14ac:dyDescent="0.3">
			<c r="F6" t="s"><v>3</v>
		</c></row>
	</sheetData>
	<pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
</worksheet>

Как мы видим, в структуре объектной модели нет никаких «пересечений». Строго говоря рабочая книга — это архив структурированных данных в формате XML. При этом в каждую «строку» входит «столбец», и в нём в свою очередь прописан номер значения данного столбца, по которому оно подтягивается из другого XML файла при открытии книги для экономии места за счёт отсутствия повторяющихся значений. Почему это важно. Если мы захотим написать какой-то обработчик таких файлов, который будет напрямую редактировать данные в этих XML, то ориентироваться надо на такую модель и структуру данных. И правильное определение будет примерно таким: ячейка — это объект внутри столбца, который в свою очередь находится внутри строки в файле xml, в котором хранятся данные о содержимом листа.

Способы обращения к ячейкам

Выбор и активация

Почти во всех случаях можно и стоит избегать использования методов Select и Activate. На это есть две причины:

  1. Это лишь имитация действий пользователя, которая замедляет выполнение программы. Работать с объектами книги можно напрямую без использования методов Select и Activate.
  2. Это усложняет код и может приводить к неожиданным последствиям. Каждый раз перед использованием Select необходимо помнить, какие ещё объекты были выбраны до этого и не забывать при необходимости снимать выбор. Либо, например, в случае использования метода Select в самом начале программы может быть выбрано два листа вместо одного потому что пользователь запустил программу, выбрав другой лист.

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

Отменить выбор  ячеек можно методом Unselect:

Selection.Unselect

Отличие выбора от активации — активировать можно только один объект из раннее выбранных. Выбрать можно несколько объектов.

Если вы записали и редактируете код макроса, то лучше всего заменить Select и Activate на конструкцию With … End With. Например, предположим, что мы записали вот такой макрос:

Sub Macro1()
' Macro1 Macro
    Range("F4:F10,H6:H10").Select 'выбрали два несмежных диапазона зажав ctrl
    Range("H6").Activate          'показывает только то, что я начал выбирать второй диапазон с этой ячейки (она осталась белой). Это действие ни на что не влияет
    With Selection.Interior       
        .Pattern = xlSolid
        .PatternColorIndex = xlAutomatic
        .Color = 65535            'залили желтым цветом, нажав на кнопку заливки на верхней панели
        .TintAndShade = 0
        .PatternTintAndShade = 0
    End With
End Sub

Почему макрос записался таким неэффективным образом? Потому что в каждый момент времени (в каждой строке) программа не знает, что вы будете делать дальше. Поэтому в записи выбор ячеек и действия с ними — это два отдельных действия. Этот код лучше всего оптимизировать (особенно если вы хотите скопировать его внутрь какого-нибудь цикла, который должен будет исполняться много раз и перебирать много объектов). Например, так:

Sub Macro11()
'
' Macro1 Macro
    Range("F4:F10,H6:H10").Select '1. смотрим, что за объект выбран (что идёт до .Select)
    Range("H6").Activate
    With Selection.Interior       '2. понимаем, что у выбранного объекта есть свойство interior, с которым далее идёт работа
        .Pattern = xlSolid
        .PatternColorIndex = xlAutomatic
        .Color = 65535
        .TintAndShade = 0
        .PatternTintAndShade = 0
    End With
End Sub



Sub Optimized_Macro()
    With Range("F4:F10,H6:H10").Interior '3. переносим объект напрямую в конструкцию With вместо Selection
' ////// Здесь я для надёжности прописал бы ещё Thisworkbook.Sheet("ИмяЛиста") перед Range,
' ////// чтобы минимизировать риск любых случайных изменений других листов и книг
' ////// With Thisworkbook.Sheet("ИмяЛиста").Range("F4:F10,H6:H10").Interior
        .Pattern = xlSolid               '4. полностью копируем всё, что было записано рекордером внутрь блока with
        .PatternColorIndex = xlAutomatic
        .Color = 55555                   '5. здесь я поменял цвет на зеленый, чтобы было видно, работает ли код при поочерёдном запуске двух макросов
        .TintAndShade = 0
        .PatternTintAndShade = 0
    End With
End Sub

Пример сценария, когда использование Select и Activate оправдано:

Допустим, мы хотим, чтобы во время исполнения программы мы одновременно изменяли несколько листов одним действием и пользователь видел какой-то определённый лист. Это можно сделать примерно так:

Sub Select_Activate_is_OK()
Thisworkbook.Worksheets(Array("Sheet1", "Sheet3")).Select 'Выбираем несколько листов по именам
Thisworkbook.Worksheets("Sheet3").Activate 'Показываем пользователю третий лист
'Далее все действия с выбранными ячейками через Select будут одновременно вносить изменения в оба выбранных листа

'Допустим, что тут мы решили покрасить те же два диапазона:
Range("F4:F10,H6:H10").Select
    Range("H6").Activate
    With Selection.Interior       
        .Pattern = xlSolid
        .PatternColorIndex = xlAutomatic
        .Color = 65535
        .TintAndShade = 0
        .PatternTintAndShade = 0
    End With

End Sub

Единственной причиной использовать этот код по моему мнению может быть желание зачем-то показать пользователю определённую страницу книги в какой-то момент исполнения программы. С точки зрения обработки объектов, опять же, эти действия лишние.

Получение и изменение значений ячеек

Значение ячеек можно получать/изменять с помощью свойства value. 

'Если нужно прочитать / записать значение ячейки, то используется свойство Value
a = ThisWorkbook.Sheets("Sheet1").Cells (1,1).Value 'записать значение ячейки А1 листа "Sheet1" в переменную "a"
ThisWorkbook.Sheets("Sheet1").Cells (1,1).Value = 1  'задать значение ячейки А1 (первый ряд, первый столбец) листа "Sheet1"

'Если нужно прочитать текст как есть (с форматированием), то можно использовать свойство .text:
ThisWorkbook.Sheets("Sheet1").Cells (1,1).Text = "1" 
a = ThisWorkbook.Sheets("Sheet1").Cells (1,1).Text

'Когда проявится разница:
'Например, если мы считываем дату в формате "31 декабря 2021 г.", хранящуюся как дата
a = ThisWorkbook.Sheets("Sheet1").Cells (1,1).Value 'эапишет как "31.12.2021"
a = ThisWorkbook.Sheets("Sheet1").Cells (1,1).Text  'запишет как "31 декабря 2021 г."

Ячейки открытой книги

К ячейкам можно обращаться:

'В книге, в которой хранится макрос (на каком-то из листов, либо в отдельном модуле или форме)
ThisWorkbook.Sheets("Sheet1").Cells(1,1).Value        'По номерам строки и столбца
ThisWorkbook.Sheets("Sheet1").Cells(1,"A").Value      'По номерам строки и букве столбца
ThisWorkbook.Sheets("Sheet1").Range("A1").Value       'По адресу - вариант 1
ThisWorkbook.Sheets("Sheet1").[A1].Value              'По адресу - вариант 2
ThisWorkbook.Sheets("Sheet1").Range("CellName").Value 'По имени ячейки (для этого ей предварительно нужно его присвоить)

'Те же действия, но с использованием полного названия рабочей книги (книга должна быть открыта)
Workbooks("workbook.xlsm").Sheets("Sheet1").Cells(1,1).Value 'По номерам строки и столбца
Workbooks("workbook.xlsm").Sheets("Sheet1").Cells(1,"A").Value                'По номерам строки и букве столбца
Workbooks("workbook.xlsm").Sheets("Sheet1").Range("A1").Value                 'По адресу - вариант 1
Workbooks("workbook.xlsm").Sheets("Sheet1").[A1].Value                        'По адресу - вариант 2
Workbooks("workbook.xlsm").Sheets("Sheet1").Range("CellName").Value           'По имени ячейки (для этого ей предварительно нужно его присвоить)

Ячейки закрытой книги

Если нужно достать или изменить данные в другой закрытой книге, то необходимо прописать открытие и закрытие книги. Непосредственно работать с закрытой книгой не получится, потому что данные в ней хранятся отдельно от структуры и при открытии Excel каждый раз производит расстановку значений по соответствующим «слотам» в структуре. Подробнее о том, как хранятся данные в xlsx см выше.

Workbooks.Open Filename:="С:closed_workbook.xlsx"    'открыть книгу (она становится активной)
a = ActiveWorkbook.Sheets("Sheet1").Cells(1,1).Value  'достать значение ячейки 1,1
ActiveWorkbook.Close False                            'закрыть книгу (False => без сохранения)

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

Код из файла:

Option Explicit
Sub get_value_from_closed_wb() 'достать значение из закрытой книги
Dim a, wb_path, wsh As String
wb_path = ThisWorkbook.Sheets("Sheet1").Cells(2, 3).Value 'get path to workbook from sheet1
wsh = ThisWorkbook.Sheets("Sheet1").Cells(3, 3).Value
Workbooks.Open Filename:=wb_path
a = ActiveWorkbook.Sheets(wsh).Cells(3, 3).Value
ActiveWorkbook.Close False
ThisWorkbook.Sheets("Sheet1").Cells(4, 3).Value = a
End Sub

Sub record_value_to_closed_wb() 'записать значение в закрытую книгу
Dim wb_path, b, wsh As String
wsh = ThisWorkbook.Sheets("Sheet1").Cells(3, 3).Value
wb_path = ThisWorkbook.Sheets("Sheet1").Cells(2, 3).Value 'get path to workbook from sheet1
b = ThisWorkbook.Sheets("Sheet1").Cells(5, 3).Value 'get value to record in the target workbook
Workbooks.Open Filename:=wb_path
ActiveWorkbook.Sheets(wsh).Cells(4, 4).Value = b 'add new value to cell D4 of the target workbook
ActiveWorkbook.Close True
End Sub

Перебор ячеек

Перебор в произвольном диапазоне

Скачать файл со всеми примерами

Пройтись по всем ячейкам в нужном диапазоне можно разными способами. Основные:

  1. Цикл For Each. Пример:
    Sub iterate_over_cells()
    
    For Each c In ThisWorkbook.Sheets("Sheet1").Range("B2:D4").Cells
    MsgBox (c)
    Next c
    
    End Sub​

    Этот цикл выведет в виде сообщений значения ячеек в диапазоне B2:D4 по порядку по строкам слева направо и по столбцам — сверху вниз. Данный способ можно использовать для действий, в который вам не важны номера ячеек (закрашивание, изменение форматирования, пересчёт чего-то и т.д.).

  2. Ту же задачу можно решить с помощью двух вложенных циклов — внешний будет перебирать ряды, а вложенный — ячейки в рядах. Этот способ я использую чаще всего, потому что он позволяет получить больше контроля над исполнением: на каждой итерации цикла нам доступны координаты ячеек. Для перебора всех ячеек на листе этим методом потребуется найти последнюю заполненную ячейку. Пример кода:
    Sub iterate_over_cells()
    
    Dim cl, rw As Integer
    Dim x As Variant
    
    'перебор области 3x3
    For rw = 1 To 3 ' цикл для перебора рядов 1-3
    
        For cl = 1 To 3 'цикл для перебора столбцов 1-3
            x = ThisWorkbook.Sheets("Sheet1").Cells(rw + 1, cl + 1).Value
            MsgBox (x)
        Next cl
    Next rw
    
    
    
    'перебор всех ячеек на листе. Последняя ячейка определена с помощью UsedRange
    'LastRow = ActiveSheet.UsedRange.Row + ActiveSheet.UsedRange.Rows.Count - 1
    'LastCol = ActiveSheet.UsedRange.Column + ActiveSheet.UsedRange.Columns.Count - 1
    'For rw = 1 To LastRow 'цикл перебора всех рядов
    '    For cl = 1 To LastCol 'цикл для перебора всех столбцов
    '        Действия 
    '    Next cl
    'Next rw
    
    
    End Sub​
  3. Если нужно перебрать все ячейки в выделенном диапазоне на активном листе, то код будет выглядеть так:
    Sub iterate_cell_by_cell_over_selection()
        Dim ActSheet As Worksheet
        Dim SelRange As Range
        Dim cell As Range
        
     
        Set ActSheet = ActiveSheet
        Set SelRange = Selection
        
        'if we want to do it in every cell of the selected range
        For Each cell In Selection
        MsgBox (cell.Value)
        
        Next cell
    
    End Sub​

    Данный метод подходит для интерактивных макросов, которые выполняют действия над выбранными пользователем областями.

  4. Перебор ячеек в ряду
    Sub iterate_cells_in_row()
        Dim i, RowNum, StartCell As Long
        
        RowNum = 3 'какой ряд
        StartCell = 0 ' номер начальной ячейки (минус 1, т.к. в цикле мы прибавляем i)
        
        For i = 1 To 10 ' 10 ячеек в выбранном ряду
        ThisWorkbook.Sheets("Sheet1").Cells(RowNum, i + StartCell).Value = i '(i + StartCell) добавляет 1 к номеру столбца при каждом повторении
        Next i
    
    End Sub
  5. Перебор ячеек в столбце
    Sub iterate_cells_in_column()
        Dim i, ColNum, StartCell As Long
        
        ColNum = 3 'какой столбец
        StartCell = 0 ' номер начальной ячейки (минус 1, т.к. в цикле мы прибавляем i)
        
        For i = 1 To 10 ' 10 ячеек
        ThisWorkbook.Sheets("Sheet1").Cells(i + StartCell, ColNum).Value = i ' (i + StartCell) добавляет 1 к номеру ряда при каждом повторении
        Next i
    
    End Sub​

Свойства и методы ячеек

Имя ячейки

Присвоить новое имя можно так:

Thisworkbook.Sheets(1).Cells(1,1).name = "Новое_Имя"

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

ActiveWorkbook.Names("Старое_Имя").Delete

Пример кода для переименования ячеек:

Sub rename_cell()

old_name = "Cell_Old_Name"
new_name = "Cell_New_Name"

ActiveWorkbook.Names(old_name).Delete
ThisWorkbook.Sheets(1).Cells(2, 1).Name = new_name
End Sub

Sub rename_cell_reverse()

old_name = "Cell_New_Name"
new_name = "Cell_Old_Name"

ActiveWorkbook.Names(old_name).Delete
ThisWorkbook.Sheets(1).Cells(2, 1).Name = new_name
End Sub

Адрес ячейки

Sub get_cell_address() ' вывести адрес ячейки в формате буква столбца, номер ряда
  '$A$1 style
  txt_address = ThisWorkbook.Sheets(1).Cells(3, 2).Address
  MsgBox (txt_address)
End Sub

Sub get_cell_address_R1C1()' получить адрес столбца в формате номер ряда, номер столбца
  'R1C1 style
  txt_address = ThisWorkbook.Sheets(1).Cells(3, 2).Address(ReferenceStyle:=xlR1C1)
  MsgBox (txt_address)
End Sub

  'пример функции, которая принимает 2 аргумента: название именованного диапазона и тип желаемого адреса 
  '(1- тип $A$1 2- R1C1 - номер ряда, столбца)
Function get_cell_address_by_name(str As String, address_type As Integer)
  '$A$1 style
  Select Case address_type
    Case 1
      txt_address = Range(str).Address
    Case 2
      txt_address = Range(str).Address(ReferenceStyle:=xlR1C1)
    Case Else
      txt_address = "Wrong address type selected. 1,2 available"
    End Select
  get_cell_address_by_name = txt_address
End Function

'перед запуском нужно убедиться, что в книге есть диапазон с названием, 
'адрес которого мы хотим получить, иначе будет ошибка
Sub test_function() 'запустите эту программу, чтобы увидеть, как работает функция
  x = get_cell_address_by_name("MyValue", 2)
  MsgBox (x)
End Sub

Размеры ячейки

Ширина и длина ячейки в VBA меняется, например, так:

Sub change_size()
Dim x, y As Integer
Dim w, h As Double

'получить координаты целевой ячейки
x = ThisWorkbook.Sheets("Sheet1").Cells(2, 2).Value
y = ThisWorkbook.Sheets("Sheet1").Cells(3, 2).Value

'получить желаемую ширину и высоту ячейки
w = ThisWorkbook.Sheets("Sheet1").Cells(6, 2).Value
h = ThisWorkbook.Sheets("Sheet1").Cells(7, 2).Value

'сменить высоту и ширину ячейки с координатами x,y
ThisWorkbook.Sheets("Sheet1").Cells(x, y).RowHeight = h
ThisWorkbook.Sheets("Sheet1").Cells(x, y).ColumnWidth = w


End Sub

Прочитать значения ширины и высоты ячеек можно двумя способами (однако результаты будут в разных единицах измерения). Если написать просто Cells(x,y).Width или Cells(x,y).Height, то будет получен результат в pt (привязка к размеру шрифта). 

Sub get_size()
Dim x, y As Integer
'получить координаты ячейки, с которой мы будем работать
x = ThisWorkbook.Sheets("Sheet1").Cells(2, 2).Value
y = ThisWorkbook.Sheets("Sheet1").Cells(3, 2).Value

'получить длину и ширину выбранной ячейки в тех же единицах измерения, в которых мы их задавали
ThisWorkbook.Sheets("Sheet1").Cells(2, 6).Value = ThisWorkbook.Sheets("Sheet1").Cells(x, y).ColumnWidth
ThisWorkbook.Sheets("Sheet1").Cells(3, 6).Value = ThisWorkbook.Sheets("Sheet1").Cells(x, y).RowHeight

'получить длину и ширину с помощью свойств ячейки (только для чтения) в поинтах (pt)
ThisWorkbook.Sheets("Sheet1").Cells(7, 9).Value = ThisWorkbook.Sheets("Sheet1").Cells(x, y).Width
ThisWorkbook.Sheets("Sheet1").Cells(8, 9).Value = ThisWorkbook.Sheets("Sheet1").Cells(x, y).Height

End Sub

Скачать файл с примерами изменения и чтения размера ячеек

Запуск макроса активацией ячейки

Для запуска кода VBA при активации ячейки необходимо вставить в код листа нечто подобное:

3 важных момента, чтобы это работало:

1. Этот код должен быть вставлен в код листа (здесь контролируется диапазон D4)

2-3. Программа, ответственная за запуск кода при выборе ячейки, должна называться Worksheet_SelectionChange и должна принимать значение переменной Target, относящейся к триггеру SelectionChange. Другие доступные триггеры можно посмотреть в правом верхнем углу (2).

Скачать файл с базовым примером (как на картинке)

Скачать файл с расширенным примером (код ниже)

Option Explicit

Private Sub Worksheet_SelectionChange(ByVal Target As Range)

        ' имеем в виду, что триггер SelectionChange будет запускать эту Sub после каждого клика мышью (после каждого клика будет проверяться:
          '1. количество выделенных ячеек и 
          '2. не пересекается ли выбранный диапазон с заданным в этой программе диапазоном.
        ' поэтому в эту программу не стоит без необходимости писать никаких других тяжелых операций

    If Selection.Count = 1 Then 'запускаем программу только если выбрано не более 1 ячейки


    'вариант модификации - брать адрес ячейки из другой ячейки:
    'Dim CellName as String
    'CellName = Activesheet.Cells(1,1).value 'брать текстовое имя контролируемой ячейки из A1 (должно быть в формате Буква столбца + номер строки)
    'If Not Intersect(Range(CellName), Target) Is Nothing Then
    'для работы этой модификации следующую строку надо закомментировать/удалить



        If Not Intersect(Range("D4"), Target) Is Nothing Then 
        'если заданный (D4) и выбранный диапазон пересекаются 
        '(пересечение диапазонов НЕ равно Nothing)

        'можно прописать диапазон из нескольких ячеек:
        'If Not Intersect(Range("D4:E10"), Target) Is Nothing Then
        'можно прописать несколько диапазонов:
        'If Not Intersect(Range("D4:E10"), Target) Is Nothing or Not Intersect(Range("A4:A10"), Target) Is Nothing Then

            Call program 'выполняем программу
        End If
    End If
End Sub

Sub program()

MsgBox ("Program Is running") 'здесь пишем код того, что произойдёт при выборе нужной ячейки


End Sub

The art of Excel VBA programming is in the manipulation of properties of objects of Excel. The more skillfully you can play with these objects and properties, the more powerful the macros you can build.

The number one object in Excel you have to process is by far the Range object. In this article, I am going to walk you through the critical skills to loop through ranges in Excel worksheets.

Loop through cells in a range

The main skill to loop through a range is with FOR-NEXT loops.  There are two FOR-NEXT loop conventions, which are both very useful when we loop through ranges. I would highly recommend to learn both skills.

Method 1: with a range variable

In the Range object, there are many Cell objects. We can therefore think of a Range as a group of Cells. We can effectively use the “For Each element In group” convention of the For-Next statement to loop through every Cell inside a Range.

A group of cells make up a range

The macro LoopCells1 loops through every cell in Range “A1:C5” and applies a sequential counter into the content of each cell.

Sub LoopCells1()
Dim cell As Range
Dim counter As Integer

'loop through each cell object element within a range
For Each cell In Range("A1:C5").Cells
    counter = counter + 1	'denotes the nth cell
    cell.Value = counter
Next
End Sub

The result after running the macro looks like this:

Results of counter loop through in Excel

Method 2: with a numeric variable

Most VBA users are more confident with the For-Next loop convention of:

For counter = start To end

This convention can also be used to loop through cells within a range. The macro “LoopCells2” demonstrates how to loop through each cell in range A1:C5 by referring to the index number of cells. The loop begins with the index number of 1 to the upper bound which is the total number of cells in the range.

Sub LoopCells2()
Dim c As Long
Dim counter As Integer

'loop through each cell within a range by calling the index number of the cells
For c = 1 To Range("A1:C5").Cells.Count
    'put the index number into cell
    Range("A1:C5").Cells(c).Value = c 
Next
End Sub

The result after running the macro “LoopCells2” looks identical to the result of the previous macro “LoopCells1”.

Identical results for second macro

Important note on numeric variable type

One limitation of this method is with the upper limit of the numeric variable being declared and used in the For-Next loop. There are a few points you need to bear in mind:

  • Avoid declaring an Integer typing variable for this purpose because the number of cells in a worksheet is far more than 32,767.
  • Declare a Long variable instead, so that the loop can process up to 2,147,483,648 cells, which serves most cases.
  • Declaring a Double variable type won’t solve the limitation.
  • In case your process exceeded the limit of even a long variable, you will have to restructure your For-Next loop to use the “For Each element In group” convention.

But even with such limitations, this is still a very useful method. It’s often used because, in most situations, the limits of the variable type won’t be reached.

Note on order of cells being processed

When using either of the two methods above, the cells in the range are being processed in the same sequence: from left to right, then from top to bottom. The picture below visualizes such sequence:

Arrows showing the order that cells are processed

If you want the cells to be processed in a different order, you need to learn other strategies which will be explained in the next few sections.

Loop through rows or columns in a range

Sometimes we want to loop through each row in a range (from top to bottom). Similar to looping through cells, we can do it with both conventions of the For-Next statement, either with a range object variable or a numeric variable.

Method 1: with a range variable

Sub LoopRows1()
Dim r As Range
Dim MyString As String

'Loop through each row, and apply a yellow colow fill
For Each r In Range("A1:C5").Rows
    r.Interior.ColorIndex = 6
Next
End Sub

To loop through columns, we just need to change the first line of the For-Next loop to go through the columns instead. In the example below, we want to loop through each column in range A1:C5, and change the column heading to Proper Case (first letter of each word in capital).

Excel table showing client number, address, and postal code

Sub LoopColumn1()
Dim c As Range
Dim MyString As String

'Loop through each column and set first cell to Proper Case
For Each c In Range("A1:C5").Columns
    c.Cells(1).Value = StrConv(c.Cells(1).Value, vbProperCase)
Next
End Sub

Method 2: with a numeric variable

In this example, we want to loop through every column in a data table. (See picture of our sample data below.) If a column contains numeric data, we set the NumberFormat to 2 decimal places.

Excel table showing orders items and unit costs

'apply 0.00 number format to columns with numeric values
Sub FormatNumericColumns()
Dim c As Integer
Dim MyString As String

With Range("A1").CurrentRegion
For c = 1 To .Columns.Count
    'test 2nd cell of column for numeric value
    If IsNumeric(.Columns(c).Cells(2).Value) Then
        .Columns(c).NumberFormat = "0.00"
    End If
Next
End Sub

The result of the macro looks like the image below. The number format of the last 3 columns with numeric data has been set to 2 decimal places.

Macro results

(The dates in the first column are not considered by the VBA IsNumeric function as numeric. Please read my other article all about IsNumeric for more detail on this topic.)

Advanced strategies

Deleting columns (or rows)

Here we want to write a macro to delete the three columns with headings with the word “delete” (the yellow columns). We can tackle this problem with a For-Next loop we learned in the sections above.

Three columns in a row in Excel are highlighted to delete

First Attempt:

We can try to loop through each column with the “For Each element In group” convention. The macro below looks simple and straight-forward enough, looping through each column (element) within the group of columns in range A1:C5.

Sub DeleteColmns1()
Dim c As Range
Dim x As Integer
    With Range("A1:E5")
        For Each c In .Columns
            If c.Cells(1).Value = "delete" Then
                c.Delete
            End If
        Next
    End With
End Sub

The result of the macro looks like the picture below. The macro has failed to delete all the three columns.

Results of deletion

Reminder: When looping through a range, if you want to apply structural change to the range, NEVER use  the “For Each element In group” convention because it may create unexpected results. In some cases, (e.g. insert columns), it will even cause an infinite loop and your Excel may be frozen and you’ll have to force quit Excel and lose your unsaved work.

Second Attempt:

Now, how about using the “For counter = start To end” convention?

Sub DeleteColmns2()
Dim tmp As Integer
Dim x As Integer
    With Range("A1:E5")
        For x = 1 To .Columns.Count
            If .Columns(x).Cells(1).Value = "delete" Then
                .Columns(x).Delete
            End If
        Next
    End With
End Sub

The result looks identical to that of the previous macro:

Results look the same

If we looked at the result more carefully, we noticed that the original 2nd and 4th column were deleted, but the original 3rd column was not. This was because when the 2nd column was deleted (when x=2), the 3rd column has become the 2nd column which has been skipped when the For-Next loop proceed to process x = 3.

Solution:

So, how do we tackle this problem? The answer is with the For-Next statement convention of “For counter = end To start step -1″, which processes the range from back to front (from the last column backward to the first column).

Sub DeleteColumnFinal()
Dim x As Integer
    With Range("A1").CurrentRegion
        For x = .Columns.Count To 1 Step -1
            If .Columns(x).Cells(1).Value = "delete" Then
                .Columns(x).Delete
            End If
        Next
    End With
End Sub

Loop though every n-th row in a range

We have a data table, and we want to apply a yellow shading (fill) to the odd number rows. And we don’t want to shade the first row which contains the field headings.

More sample order data in Excel

Solution 1:

We can use the “For counter = start To end” convention to tackle this problem. We can loop through each row beginning from the 2nd row (which bypassed the field headings). If the row number is odd, we apply a yellow color.

'Shade alternate (even) rows of data
Sub ShadeRows1()
Dim r As Long
With Range("A1").CurrentRegion
    For r = 1 To .Rows.Count
        If r / 2 = Int(r / 2) Then 'even rows
            .Rows(r).Interior.ColorIndex = 6
        End If
    Next
End With
End Sub

Results of the macro with rows highlighted

To enhance the macro to shade every n-th row, simply change the 2 in line 5 to n. For example:

If r / 3 = Int(r / 3) Then 'every 3 rows

Solution 2:

We can also use the “For counter =  start to end step 2” convention. In the macro “ShadeRows2”, the loop begins from the 2nd row and then the 4th row, then 6th row, etc.

'Shade alternate (odd) rows of data from the 3rd row
Sub ShadeRows2()
Dim r As Long
With Range("A1").CurrentRegion
    'begin from 2nd and shade every other row
    For r = 2 To .Rows.Count Step 2
        .Rows(r).Interior.ColorIndex = 6
    Next
End With
End Sub

To enhance the macro to shade every 3rd row, simple change the 2 in line 5 to 3. For example:

For r = 3 To .Rows.Count Step 3

Conclusion

We have gone through in detail the different approaches to loop through a range in VBA, including the pitfalls and a couple of special scenarios. These techniques can also be applied in combination or with other VBA techniques to achieve more powerful automation with worksheet ranges in your macros.

На чтение 18 мин. Просмотров 74.7k.

VBA Range

сэр Артур Конан Дойл

Это большая ошибка — теоретизировать, прежде чем кто-то получит данные

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

Рассматриваемые темы включают свойство смещения, чтение
значений между ячейками, чтение значений в массивы и форматирование ячеек.

Содержание

  1. Краткое руководство по диапазонам и клеткам
  2. Введение
  3. Важное замечание
  4. Свойство Range
  5. Свойство Cells рабочего листа
  6. Использование Cells и Range вместе
  7. Свойство Offset диапазона
  8. Использование диапазона CurrentRegion
  9. Использование Rows и Columns в качестве Ranges
  10. Использование Range вместо Worksheet
  11. Чтение значений из одной ячейки в другую
  12. Использование метода Range.Resize
  13. Чтение Value в переменные
  14. Как копировать и вставлять ячейки
  15. Чтение диапазона ячеек в массив
  16. Пройти через все клетки в диапазоне
  17. Форматирование ячеек
  18. Основные моменты

Краткое руководство по диапазонам и клеткам

Функция Принимает Возвращает Пример Вид
Range адреса
ячеек
диапазон
ячеек
.Range(«A1:A4») $A$1:$A$4
Cells строка,
столбец
одна
ячейка
.Cells(1,5) $E$1
Offset строка,
столбец
диапазон .Range(«A1:A2»)
.Offset(1,2)
$C$2:$C$3
Rows строка (-и) одна или
несколько
строк
.Rows(4)
.Rows(«2:4»)
$4:$4
$2:$4
Columns столбец
(-цы)
один или
несколько
столбцов
.Columns(4)
.Columns(«B:D»)
$D:$D
$B:$D

Введение

Это третья статья, посвященная трем основным элементам VBA. Этими тремя элементами являются Workbooks, Worksheets и Ranges/Cells. Cells, безусловно, самая важная часть Excel. Почти все, что вы делаете в Excel, начинается и заканчивается ячейками.

Вы делаете три основных вещи с помощью ячеек:

  1. Читаете из ячейки.
  2. Пишите в ячейку.
  3. Изменяете формат ячейки.

В Excel есть несколько методов для доступа к ячейкам, таких как Range, Cells и Offset. Можно запутаться, так как эти функции делают похожие операции.

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

Давайте начнем с самого простого метода доступа к ячейкам — с помощью свойства Range рабочего листа.

Важное замечание

Я недавно обновил эту статью, сейчас использую Value2.

Вам может быть интересно, в чем разница между Value, Value2 и значением по умолчанию:

' Value2
Range("A1").Value2 = 56

' Value
Range("A1").Value = 56

' По умолчанию используется значение
Range("A1") = 56

Использование Value может усечь число, если ячейка отформатирована, как валюта. Если вы не используете какое-либо свойство, по умолчанию используется Value.

Лучше использовать Value2, поскольку он всегда будет возвращать фактическое значение ячейки.

Свойство Range

Рабочий лист имеет свойство Range, которое можно использовать для доступа к ячейкам в VBA. Свойство Range принимает тот же аргумент, что и большинство функций Excel Worksheet, например: «А1», «А3: С6» и т.д.

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

Sub ZapisVYacheiku()

    ' Запишите число в ячейку A1 на листе 1 этой книги
    ThisWorkbook.Worksheets("Лист1").Range("A1").Value2 = 67

    ' Напишите текст в ячейку A2 на листе 1 этой рабочей книги
    ThisWorkbook.Worksheets("Лист1").Range("A2").Value2 = "Иван Петров"

    ' Запишите дату в ячейку A3 на листе 1 этой книги
    ThisWorkbook.Worksheets("Лист1").Range("A3").Value2 = #11/21/2019#

End Sub

Как видно из кода, Range является членом Worksheets, которая, в свою очередь, является членом Workbook. Иерархия такая же, как и в Excel, поэтому должно быть легко понять. Чтобы сделать что-то с Range, вы должны сначала указать рабочую книгу и рабочий лист, которому она принадлежит.

В оставшейся части этой статьи я буду использовать кодовое имя для ссылки на лист.

code name worksheet

Следующий код показывает приведенный выше пример с использованием кодового имени рабочего листа, т.е. Лист1 вместо ThisWorkbook.Worksheets («Лист1»).

Sub IspKodImya ()

    ' Запишите число в ячейку A1 на листе 1 этой книги    
     Sheet1.Range("A1").Value2 = 67

    ' Напишите текст в ячейку A2 на листе 1 этой рабочей книги
    Sheet1.Range("A2").Value2 = "Иван Петров"

    ' Запишите дату в ячейку A3 на листе 1 этой книги
    Sheet1.Range("A3").Value2 = #11/21/2019#

End Sub

Вы также можете писать в несколько ячеек, используя свойство
Range

Sub ZapisNeskol()

    ' Запишите число в диапазон ячеек
    Sheet1.Range("A1:A10").Value2 = 67

    ' Написать текст в несколько диапазонов ячеек
    Sheet1.Range("B2:B5,B7:B9").Value2 = "Иван Петров"

End Sub

Свойство Cells рабочего листа

У Объекта листа есть другое свойство, называемое Cells, которое очень похоже на Range . Есть два отличия:

  1. Cells возвращают диапазон только одной ячейки.
  2. Cells принимает строку и столбец в качестве аргументов.

В приведенном ниже примере показано, как записывать значения
в ячейки, используя свойства Range и Cells.

Sub IspCells()

    ' Написать в А1
    Sheet1.Range("A1").Value2 = 10
    Sheet1.Cells(1, 1).Value2  = 10

    ' Написать в А10
    Sheet1.Range("A10").Value2 = 10
    Sheet1.Cells(10, 1).Value2  = 10

    ' Написать в E1
    Sheet1.Range("E1").Value2 = 10
    Sheet1.Cells(1, 5).Value2  = 10

End Sub

Вам должно быть интересно, когда использовать Cells, а когда Range. Использование Range полезно для доступа к одним и тем же ячейкам при каждом запуске макроса.

Например, если вы использовали макрос для вычисления суммы и
каждый раз записывали ее в ячейку A10, тогда Range подойдет для этой задачи.

Использование свойства Cells полезно, если вы обращаетесь к
ячейке по номеру, который может отличаться. Проще объяснить это на примере.

В следующем коде мы просим пользователя указать номер столбца. Использование Cells дает нам возможность использовать переменное число для столбца.

Sub ZapisVPervuyuPustuyuYacheiku()

    Dim UserCol As Integer
    
    ' Получить номер столбца от пользователя
    UserCol = Application.InputBox("Пожалуйста, введите номер столбца...", Type:=1)
    
    ' Написать текст в выбранный пользователем столбец
    Sheet1.Cells(1, UserCol).Value2 = "Иван Петров"

End Sub

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

Чтобы использовать Range здесь, потребуется преобразовать эти значения в ссылку на
буквенно-цифровую ячейку, например, «С1». Использование свойства Cells позволяет нам
предоставить строку и номер столбца для доступа к ячейке.

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

Использование Cells и Range вместе

Как вы уже видели, вы можете получить доступ только к одной ячейке, используя свойство Cells. Если вы хотите вернуть диапазон ячеек, вы можете использовать Cells с Range следующим образом:

Sub IspCellsSRange()

    With Sheet1
        ' Запишите 5 в диапазон A1: A10, используя свойство Cells
        .Range(.Cells(1, 1), .Cells(10, 1)).Value2 = 5

        ' Диапазон B1: Z1 будет выделен жирным шрифтом
        .Range(.Cells(1, 2), .Cells(1, 26)).Font.Bold = True

    End With

End Sub

Как видите, вы предоставляете начальную и конечную ячейку
диапазона. Иногда бывает сложно увидеть, с каким диапазоном вы имеете дело,
когда значением являются все числа. Range имеет свойство Address, которое
отображает буквенно-цифровую ячейку для любого диапазона. Это может
пригодиться, когда вы впервые отлаживаете или пишете код.

В следующем примере мы распечатываем адрес используемых нами
диапазонов.

Sub PokazatAdresDiapazona()

    ' Примечание. Использование подчеркивания позволяет разделить строки кода.
    With Sheet1

        ' Запишите 5 в диапазон A1: A10, используя свойство Cells
        .Range(.Cells(1, 1), .Cells(10, 1)).Value2 = 5
        Debug.Print "Первый адрес: " _
            + .Range(.Cells(1, 1), .Cells(10, 1)).Address

        ' Диапазон B1: Z1 будет выделен жирным шрифтом
        .Range(.Cells(1, 2), .Cells(1, 26)).Font.Bold = True
        Debug.Print "Второй адрес : " _
            + .Range(.Cells(1, 2), .Cells(1, 26)).Address

    End With

End Sub

В примере я использовал Debug.Print для печати в Immediate Window. Для просмотра этого окна выберите «View» -> «в Immediate Window» (Ctrl +  G).

ImmediateWindow

ImmediateSampeText

Свойство Offset диапазона

У диапазона есть свойство, которое называется Offset. Термин «Offset» относится к отсчету от исходной позиции. Он часто используется в определенных областях программирования. С помощью свойства «Offset» вы можете получить диапазон ячеек того же размера и на определенном расстоянии от текущего диапазона. Это полезно, потому что иногда вы можете выбрать диапазон на основе определенного условия. Например, на скриншоте ниже есть столбец для каждого дня недели. Учитывая номер дня (т.е. понедельник = 1, вторник = 2 и т.д.). Нам нужно записать значение в правильный столбец.

VBA Offset

Сначала мы попытаемся сделать это без использования Offset.

' Это Sub тесты с разными значениями
Sub TestSelect()

    ' Понедельник
    SetValueSelect 1, 111.21
    ' Среда
    SetValueSelect 3, 456.99
    ' Пятница
    SetValueSelect 5, 432.25
    ' Воскресение
    SetValueSelect 7, 710.17

End Sub

' Записывает значение в столбец на основе дня
Public Sub SetValueSelect(lDay As Long, lValue As Currency)

    Select Case lDay
        Case 1: Sheet1.Range("H3").Value2 = lValue
        Case 2: Sheet1.Range("I3").Value2 = lValue
        Case 3: Sheet1.Range("J3").Value2 = lValue
        Case 4: Sheet1.Range("K3").Value2 = lValue
        Case 5: Sheet1.Range("L3").Value2 = lValue
        Case 6: Sheet1.Range("M3").Value2 = lValue
        Case 7: Sheet1.Range("N3").Value2 = lValue
    End Select

End Sub

Как видно из примера, нам нужно добавить строку для каждого возможного варианта. Это не идеальная ситуация. Использование свойства Offset обеспечивает более чистое решение.

' Это Sub тесты с разными значениями
Sub TestOffset()

    DayOffSet 1, 111.01
    DayOffSet 3, 456.99
    DayOffSet 5, 432.25
    DayOffSet 7, 710.17

End Sub

Public Sub DayOffSet(lDay As Long, lValue As Currency)

    ' Мы используем значение дня с Offset, чтобы указать правильный столбец
    Sheet1.Range("G3").Offset(, lDay).Value2 = lValue

End Sub

Как видите, это решение намного лучше. Если количество дней увеличилось, нам больше не нужно добавлять код. Чтобы Offset был полезен, должна быть какая-то связь между позициями ячеек. Если столбцы Day в приведенном выше примере были случайными, мы не могли бы использовать Offset. Мы должны были бы использовать первое решение.

Следует иметь в виду, что Offset сохраняет размер диапазона. Итак .Range («A1:A3»).Offset (1,1) возвращает диапазон B2:B4. Ниже приведены еще несколько примеров использования Offset.

Sub IspOffset()

    ' Запись в В2 - без Offset
    Sheet1.Range("B2").Offset().Value2 = "Ячейка B2"

    ' Написать в C2 - 1 столбец справа
    Sheet1.Range("B2").Offset(, 1).Value2 = "Ячейка C2"

    ' Написать в B3 - 1 строка вниз
    Sheet1.Range("B2").Offset(1).Value2 = "Ячейка B3"

    ' Запись в C3 - 1 столбец справа и 1 строка вниз
    Sheet1.Range("B2").Offset(1, 1).Value2 = "Ячейка C3"

    ' Написать в A1 - 1 столбец слева и 1 строка вверх
    Sheet1.Range("B2").Offset(-1, -1).Value2 = "Ячейка A1"

    ' Запись в диапазон E3: G13 - 1 столбец справа и 1 строка вниз
    Sheet1.Range("D2:F12").Offset(1, 1).Value2 = "Ячейки E3:G13"

End Sub

Использование диапазона CurrentRegion

CurrentRegion возвращает диапазон всех соседних ячеек в данный диапазон. На скриншоте ниже вы можете увидеть два CurrentRegion. Я добавил границы, чтобы прояснить CurrentRegion.

VBA CurrentRegion

Строка или столбец пустых ячеек означает конец CurrentRegion.

Вы можете вручную проверить
CurrentRegion в Excel, выбрав диапазон и нажав Ctrl + Shift + *.

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

Например:

Range («B3»). CurrentRegion вернет диапазон B3:D14

Range («D14»). CurrentRegion вернет диапазон B3:D14

Range («C8:C9»). CurrentRegion вернет диапазон B3:D14 и так далее

Как пользоваться

Мы получаем CurrentRegion следующим образом

' CurrentRegion вернет B3:D14 из приведенного выше примера
Dim rg As Range
Set rg = Sheet1.Range("B3").CurrentRegion

Только чтение строк данных

Прочитать диапазон из второй строки, т.е. пропустить строку заголовка.

' CurrentRegion вернет B3:D14 из приведенного выше примера
Dim rg As Range
Set rg = Sheet1.Range("B3").CurrentRegion

' Начало в строке 2 - строка после заголовка
Dim i As Long
For i = 2 To rg.Rows.Count
    ' текущая строка, столбец 1 диапазона
    Debug.Print rg.Cells(i, 1).Value2
Next i

Удалить заголовок

Удалить строку заголовка (т.е. первую строку) из диапазона. Например, если диапазон — A1:D4, это возвратит A2:D4

' CurrentRegion вернет B3:D14 из приведенного выше примера
Dim rg As Range
Set rg = Sheet1.Range("B3").CurrentRegion

' Удалить заголовок
Set rg = rg.Resize(rg.Rows.Count - 1).Offset(1)

' Начните со строки 1, так как нет строки заголовка
Dim i As Long
For i = 1 To rg.Rows.Count
    ' текущая строка, столбец 1 диапазона
    Debug.Print rg.Cells(i, 1).Value2
Next i

Использование Rows и Columns в качестве Ranges

Если вы хотите что-то сделать со всей строкой или столбцом,
вы можете использовать свойство «Rows и
Columns
» на рабочем листе. Они оба принимают один параметр — номер строки
или столбца, к которому вы хотите получить доступ.

Sub IspRowIColumns()

    ' Установите размер шрифта столбца B на 9
    Sheet1.Columns(2).Font.Size = 9

    ' Установите ширину столбцов от D до F
    Sheet1.Columns("D:F").ColumnWidth = 4

    ' Установите размер шрифта строки 5 до 18
    Sheet1.Rows(5).Font.Size = 18

End Sub

Использование Range вместо Worksheet

Вы также можете использовать Cella, Rows и Columns, как часть Range, а не как часть Worksheet. У вас может быть особая необходимость в этом, но в противном случае я бы избегал практики. Это делает код более сложным. Простой код — твой друг. Это уменьшает вероятность ошибок.

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

Sub IspColumnsVRange()

    ' Это выделит B1 и B2 жирным шрифтом.
    Sheet1.Range("A1:C2").Columns(2).Font.Bold = True

End Sub

Чтение значений из одной ячейки в другую

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

В следующем примере показано, как это сделать:

Sub ChitatZnacheniya()

    ' Поместите значение из B1 в A1
    Sheet1.Range("A1").Value2 = Sheet1.Range("B1").Value2

    ' Поместите значение из B3 в лист2 в ячейку A1
    Sheet1.Range("A1").Value2 = Sheet2.Range("B3").Value2

    ' Поместите значение от B1 в ячейки A1 до A5
    Sheet1.Range("A1:A5").Value2 = Sheet1.Range("B1").Value2

    ' Вам необходимо использовать свойство «Value», чтобы прочитать несколько ячеек
    Sheet1.Range("A1:A5").Value2 = Sheet1.Range("B1:B5").Value2

End Sub

Как видно из этого примера, невозможно читать из нескольких ячеек. Если вы хотите сделать это, вы можете использовать функцию копирования Range с параметром Destination.

Sub KopirovatZnacheniya()

    ' Сохранить диапазон копирования в переменной
    Dim rgCopy As Range
    Set rgCopy = Sheet1.Range("B1:B5")

    ' Используйте это для копирования из более чем одной ячейки
    rgCopy.Copy Destination:=Sheet1.Range("A1:A5")

    ' Вы можете вставить в несколько мест назначения
    rgCopy.Copy Destination:=Sheet1.Range("A1:A5,C2:C6")

End Sub

Функция Copy копирует все, включая формат ячеек. Это тот же результат, что и ручное копирование и вставка выделения. Подробнее об этом вы можете узнать в разделе «Копирование и вставка ячеек»

Использование метода Range.Resize

При копировании из одного диапазона в другой с использованием присваивания (т.е. знака равенства) диапазон назначения должен быть того же размера, что и исходный диапазон.

Использование функции Resize позволяет изменить размер
диапазона до заданного количества строк и столбцов.

Например:

Sub ResizePrimeri()
 
    ' Печатает А1
    Debug.Print Sheet1.Range("A1").Address

    ' Печатает A1:A2
    Debug.Print Sheet1.Range("A1").Resize(2, 1).Address

    ' Печатает A1:A5
    Debug.Print Sheet1.Range("A1").Resize(5, 1).Address
    
    ' Печатает A1:D1
    Debug.Print Sheet1.Range("A1").Resize(1, 4).Address
    
    ' Печатает A1:C3
    Debug.Print Sheet1.Range("A1").Resize(3, 3).Address
    
End Sub

Когда мы хотим изменить наш целевой диапазон, мы можем
просто использовать исходный размер диапазона.

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

Sub Resize()

    Dim rgSrc As Range, rgDest As Range
    
    ' Получить все данные в текущей области
    Set rgSrc = Sheet1.Range("A1").CurrentRegion

      ' Получить диапазон назначения
    Set rgDest = Sheet2.Range("A1")
    Set rgDest = rgDest.Resize(rgSrc.Rows.Count, rgSrc.Columns.Count)
    
    rgDest.Value2 = rgSrc.Value2

End Sub

Мы можем сделать изменение размера в одну строку, если нужно:

Sub Resize2()

    Dim rgSrc As Range
    
    ' Получить все данные в ткущей области
    Set rgSrc = Sheet1.Range("A1").CurrentRegion
    
    With rgSrc
        Sheet2.Range("A1").Resize(.Rows.Count, .Columns.Count) = .Value2
    End With
    
End Sub

Чтение Value в переменные

Мы рассмотрели, как читать из одной клетки в другую. Вы также можете читать из ячейки в переменную. Переменная используется для хранения значений во время работы макроса. Обычно вы делаете это, когда хотите манипулировать данными перед тем, как их записать. Ниже приведен простой пример использования переменной. Как видите, значение элемента справа от равенства записывается в элементе слева от равенства.

Sub IspVar()

    ' Создайте
    Dim val As Integer

    ' Читать число из ячейки
    val = Sheet1.Range("A1").Value2

    ' Добавить 1 к значению
    val = val + 1

    ' Запишите новое значение в ячейку
    Sheet1.Range("A2").Value2 = val

End Sub

Для чтения текста в переменную вы используете переменную
типа String.

Sub IspVarText()

    ' Объявите переменную типа string
    Dim sText As String

    ' Считать значение из ячейки
    sText = Sheet1.Range("A1").Value2

    ' Записать значение в ячейку
    Sheet1.Range("A2").Value2 = sText

End Sub

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

Sub VarNeskol()

    ' Считать значение из ячейки
    Sheet1.Range("A1:B10").Value2 = 66

End Sub

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

Как копировать и вставлять ячейки

Если вы хотите скопировать и вставить диапазон ячеек, вам не
нужно выбирать их. Это распространенная ошибка, допущенная новыми пользователями
VBA.

Вы можете просто скопировать ряд ячеек, как здесь:

Range("A1:B4").Copy Destination:=Range("C5")

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

Работает так:

Range("A1:B4").Copy
Range("F3").PasteSpecial Paste:=xlPasteValues
Range("F3").PasteSpecial Paste:=xlPasteFormats
Range("F3").PasteSpecial Paste:=xlPasteFormulas

В следующей таблице приведен полный список всех типов вставок.

Виды вставок
xlPasteAll
xlPasteAllExceptBorders
xlPasteAllMergingConditionalFormats
xlPasteAllUsingSourceTheme
xlPasteColumnWidths
xlPasteComments
xlPasteFormats
xlPasteFormulas
xlPasteFormulasAndNumberFormats
xlPasteValidation
xlPasteValues
xlPasteValuesAndNumberFormats

Чтение диапазона ячеек в массив

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

Range("A3:Z3").Value2 = Range("A1:Z1").Value2

Значение диапазона в этом примере считается вариантом массива. Это означает, что вы можете легко читать из диапазона ячеек в массив. Вы также можете писать из массива в диапазон ячеек. Если вы не знакомы с массивами, вы можете проверить их в этой статье.

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

Sub ChitatMassiv()

    ' Создать динамический массив
    Dim StudentMarks() As Variant

    ' Считать 26 значений в массив из первой строки
    StudentMarks = Range("A1:Z1").Value2

    ' Сделайте что-нибудь с массивом здесь

    ' Запишите 26 значений в третью строку
    Range("A3:Z3").Value2 = StudentMarks

End Sub

Имейте в виду, что массив, созданный для чтения, является
двумерным массивом. Это связано с тем, что электронная таблица хранит значения
в двух измерениях, то есть в строках и столбцах.

Пройти через все клетки в диапазоне

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

Вы можете сделать это, используя цикл For Each, показанный в следующем коде.

Sub PeremeschatsyaPoYacheikam()

    ' Пройдите через каждую ячейку в диапазоне
    Dim rg As Range
    For Each rg In Sheet1.Range("A1:A10,A20")
        ' Распечатать адрес ячеек, которые являются отрицательными
        If rg.Value < 0 Then
            Debug.Print rg.Address + " Отрицательно."
        End If
    Next

End Sub

Вы также можете проходить последовательные ячейки, используя
свойство Cells и стандартный цикл For.

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

Sub PerehodPoYacheikam()
 
    ' Пройдите клетки от А1 до А10
    Dim i As Long
    For i = 1 To 10
        ' Распечатать адрес ячеек, которые являются отрицательными
        If Range("A" & i).Value < 0 Then
            Debug.Print Range("A" & i).Address + " Отрицательно."
        End If
    Next
 
    ' Пройдите в обратном порядке, то есть от A10 до A1
    For i = 10 To 1 Step -1
        ' Распечатать адрес ячеек, которые являются отрицательными
        If Range("A" & i) < 0 Then
            Debug.Print Range("A" & i).Address + " Отрицательно."
        End If
    Next
 
End Sub

Форматирование ячеек

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

Sub FormatirovanieYacheek()

    With Sheet1

        ' Форматировать шрифт
        .Range("A1").Font.Bold = True
        .Range("A1").Font.Underline = True
        .Range("A1").Font.Color = rgbNavy

        ' Установите числовой формат до 2 десятичных знаков
        .Range("B2").NumberFormat = "0.00"
        ' Установите числовой формат даты
        .Range("C2").NumberFormat = "dd/mm/yyyy"
        ' Установите формат чисел на общий
        .Range("C3").NumberFormat = "Общий"
        ' Установить числовой формат текста
        .Range("C4").NumberFormat = "Текст"

        ' Установите цвет заливки ячейки
        .Range("B3").Interior.Color = rgbSandyBrown

        ' Форматировать границы
        .Range("B4").Borders.LineStyle = xlDash
        .Range("B4").Borders.Color = rgbBlueViolet

    End With

End Sub

Основные моменты

Ниже приводится краткое изложение основных моментов

  1. Range возвращает диапазон ячеек
  2. Cells возвращают только одну клетку
  3. Вы можете читать из одной ячейки в другую
  4. Вы можете читать из диапазона ячеек в другой диапазон ячеек.
  5. Вы можете читать значения из ячеек в переменные и наоборот.
  6. Вы можете читать значения из диапазонов в массивы и наоборот
  7. Вы можете использовать цикл For Each или For, чтобы проходить через каждую ячейку в диапазоне.
  8. Свойства Rows и Columns позволяют вам получить доступ к диапазону ячеек этих типов

“It is a capital mistake to theorize before one has data”- Sir Arthur Conan Doyle

This post covers everything you need to know about using Cells and Ranges in VBA. You can read it from start to finish as it is laid out in a logical order. If you prefer you can use the table of contents below to go to a section of your choice.

Topics covered include Offset property, reading values between cells, reading values to arrays and formatting cells.

A Quick Guide to Ranges and Cells

Function Takes Returns Example Gives

Range

cell address multiple cells .Range(«A1:A4») $A$1:$A$4
Cells row, column one cell .Cells(1,5) $E$1
Offset row, column multiple cells Range(«A1:A2»)
.Offset(1,2)
$C$2:$C$3
Rows row(s) one or more rows .Rows(4)
.Rows(«2:4»)
$4:$4
$2:$4
Columns column(s) one or more columns .Columns(4)
.Columns(«B:D»)
$D:$D
$B:$D

Download the Code

 

The Webinar

If you are a member of the VBA Vault, then click on the image below to access the webinar and the associated source code.

(Note: Website members have access to the full webinar archive.)

vba ranges video

Introduction

This is the third post dealing with the three main elements of VBA. These three elements are the Workbooks, Worksheets and Ranges/Cells. Cells are by far the most important part of Excel. Almost everything you do in Excel starts and ends with Cells.

 
Generally speaking, you do three main things with Cells

  1. Read from a cell.
  2. Write to a cell.
  3. Change the format of a cell.

 
Excel has a number of methods for accessing cells such as Range, Cells and Offset.These can cause confusion as they do similar things and can lead to confusion

In this post I will tackle each one, explain why you need it and when you should use it.

 
Let’s start with the simplest method of accessing cells – using the Range property of the worksheet.

Important Notes

I have recently updated this article so that is uses Value2.

You may be wondering what is the difference between Value, Value2 and the default:

' Value2
Range("A1").Value2 = 56

' Value
Range("A1").Value = 56

' Default uses value
Range("A1") = 56

 
Using Value may truncate number if the cell is formatted as currency. If you don’t use any property then the default is Value.

It is better to use Value2 as it will always return the actual cell value(see this article from Charle Williams.)

The Range Property

The worksheet has a Range property which you can use to access cells in VBA. The Range property takes the same argument that most Excel Worksheet functions take e.g. “A1”, “A3:C6” etc.

The following example shows you how to place a value in a cell using the Range property.

' https://excelmacromastery.com/
Public Sub WriteToCell()

    ' Write number to cell A1 in sheet1 of this workbook
    ThisWorkbook.Worksheets("Sheet1").Range("A1").Value2 = 67

    ' Write text to cell A2 in sheet1 of this workbook
    ThisWorkbook.Worksheets("Sheet1").Range("A2").Value2 = "John Smith"

    ' Write date to cell A3 in sheet1 of this workbook
    ThisWorkbook.Worksheets("Sheet1").Range("A3").Value2 = #11/21/2017#

End Sub

 
As you can see Range is a member of the worksheet which in turn is a member of the Workbook. This follows the same hierarchy as in Excel so should be easy to understand. To do something with Range you must first specify the workbook and worksheet it belongs to.

For the rest of this post I will use the code name to reference the worksheet.

code name worksheet

 
 
The following code shows the above example using the code name of the worksheet i.e. Sheet1 instead of ThisWorkbook.Worksheets(“Sheet1”).

' https://excelmacromastery.com/
Public Sub UsingCodeName()

    ' Write number to cell A1 in sheet1 of this workbook
    Sheet1.Range("A1").Value2 = 67

    ' Write text to cell A2 in sheet1 of this workbook
    Sheet1.Range("A2").Value2 = "John Smith"

    ' Write date to cell A3 in sheet1 of this workbook
    Sheet1.Range("A3").Value2 = #11/21/2017#

End Sub

You can also write to multiple cells using the Range property

' https://excelmacromastery.com/
Public Sub WriteToMulti()

    ' Write number to a range of cells
    Sheet1.Range("A1:A10").Value2 = 67

    ' Write text to multiple ranges of cells
    Sheet1.Range("B2:B5,B7:B9").Value2 = "John Smith"

End Sub

 
You can download working examples of all the code from this post from the top of this article.
 

The Cells Property of the Worksheet

The worksheet object has another property called Cells which is very similar to range. There are two differences

  1. Cells returns a range of one cell only.
  2. Cells takes row and column as arguments.

 
The example below shows you how to write values to cells using both the Range and Cells property

' https://excelmacromastery.com/
Public Sub UsingCells()

    ' Write to A1
    Sheet1.Range("A1").Value2 = 10
    Sheet1.Cells(1, 1).Value2  = 10

    ' Write to A10
    Sheet1.Range("A10").Value2 = 10
    Sheet1.Cells(10, 1).Value2  = 10

    ' Write to E1
    Sheet1.Range("E1").Value2 = 10
    Sheet1.Cells(1, 5).Value2  = 10

End Sub

 
You may be wondering when you should use Cells and when you should use Range. Using Range is useful for accessing the same cells each time the Macro runs.

For example, if you were using a Macro to calculate a total and write it to cell A10 every time then Range would be suitable for this task.

Using the Cells property is useful if you are accessing a cell based on a number that may vary. It is easier to explain this with an example.

 
In the following code, we ask the user to specify the column number. Using Cells gives us the flexibility to use a variable number for the column.

' https://excelmacromastery.com/
Public Sub WriteToColumn()

    Dim UserCol As Integer
    
    ' Get the column number from the user
    UserCol = Application.InputBox(" Please enter the column...", Type:=1)
    
    ' Write text to user selected column
    Sheet1.Cells(1, UserCol).Value2 = "John Smith"

End Sub

 
In the above example, we are using a number for the column rather than a letter.

To use Range here would require us to convert these values to the letter/number  cell reference e.g. “C1”. Using the Cells property allows us to provide a row and a column number to access a cell.

Sometimes you may want to return more than one cell using row and column numbers. The next section shows you how to do this.

Using Cells and Range together

As you have seen you can only access one cell using the Cells property. If you want to return a range of cells then you can use Cells with Ranges as follows

' https://excelmacromastery.com/
Public Sub UsingCellsWithRange()

    With Sheet1
        ' Write 5 to Range A1:A10 using Cells property
        .Range(.Cells(1, 1), .Cells(10, 1)).Value2 = 5

        ' Format Range B1:Z1 to be bold
        .Range(.Cells(1, 2), .Cells(1, 26)).Font.Bold = True

    End With

End Sub

 
As you can see, you provide the start and end cell of the Range. Sometimes it can be tricky to see which range you are dealing with when the value are all numbers. Range has a property called Address which displays the letter/ number cell reference of any range. This can come in very handy when you are debugging or writing code for the first time.

 
In the following example we print out the address of the ranges we are using:

' https://excelmacromastery.com/
Public Sub ShowRangeAddress()

    ' Note: Using underscore allows you to split up lines of code
    With Sheet1

        ' Write 5 to Range A1:A10 using Cells property
        .Range(.Cells(1, 1), .Cells(10, 1)).Value2 = 5
        Debug.Print "First address is : " _
            + .Range(.Cells(1, 1), .Cells(10, 1)).Address

        ' Format Range B1:Z1 to be bold
        .Range(.Cells(1, 2), .Cells(1, 26)).Font.Bold = True
        Debug.Print "Second address is : " _
            + .Range(.Cells(1, 2), .Cells(1, 26)).Address

    End With

End Sub

 
In the example I used Debug.Print to print to the Immediate Window. To view this window select View->Immediate Window(or Ctrl G)

 
ImmediateWindow

 
ImmediateSampeText

 
You can download all the code for this post from the top of this article.
 

The Offset Property of Range

Range has a property called Offset. The term Offset refers to a count from the original position. It is used a lot in certain areas of programming. With the Offset property you can get a Range of cells the same size and a certain distance from the current range. The reason this is useful is that sometimes you may want to select a Range based on a certain condition. For example in the screenshot below there is a column for each day of the week. Given the day number(i.e. Monday=1, Tuesday=2 etc.) we need to write the value to the correct column.

 
VBA Offset

 
We will first attempt to do this without using Offset.

' https://excelmacromastery.com/
' This sub tests with different values
Public Sub TestSelect()

    ' Monday
    SetValueSelect 1, 111.21
    ' Wednesday
    SetValueSelect 3, 456.99
    ' Friday
    SetValueSelect 5, 432.25
    ' Sunday
    SetValueSelect 7, 710.17

End Sub

' Writes the value to a column based on the day
Public Sub SetValueSelect(lDay As Long, lValue As Currency)

    Select Case lDay
        Case 1: Sheet1.Range("H3").Value2 = lValue
        Case 2: Sheet1.Range("I3").Value2 = lValue
        Case 3: Sheet1.Range("J3").Value2 = lValue
        Case 4: Sheet1.Range("K3").Value2 = lValue
        Case 5: Sheet1.Range("L3").Value2 = lValue
        Case 6: Sheet1.Range("M3").Value2 = lValue
        Case 7: Sheet1.Range("N3").Value2 = lValue
    End Select

End Sub

 
As you can see in the example, we need to add a line for each possible option. This is not an ideal situation. Using the Offset Property provides a much cleaner solution

' https://excelmacromastery.com/
' This sub tests with different values
Public Sub TestOffset()

    DayOffSet 1, 111.01
    DayOffSet 3, 456.99
    DayOffSet 5, 432.25
    DayOffSet 7, 710.17

End Sub

Public Sub DayOffSet(lDay As Long, lValue As Currency)

    ' We use the day value with offset specify the correct column
    Sheet1.Range("G3").Offset(, lDay).Value2 = lValue

End Sub

 
As you can see this solution is much better. If the number of days in increased then we do not need to add any more code. For Offset to be useful there needs to be some kind of relationship between the positions of the cells. If the Day columns in the above example were random then we could not use Offset. We would have to use the first solution.

 
One thing to keep in mind is that Offset retains the size of the range. So .Range(“A1:A3”).Offset(1,1) returns the range B2:B4. Below are some more examples of using Offset

' https://excelmacromastery.com/
Public Sub UsingOffset()

    ' Write to B2 - no offset
    Sheet1.Range("B2").Offset().Value2 = "Cell B2"

    ' Write to C2 - 1 column to the right
    Sheet1.Range("B2").Offset(, 1).Value2 = "Cell C2"

    ' Write to B3 - 1 row down
    Sheet1.Range("B2").Offset(1).Value2 = "Cell B3"

    ' Write to C3 - 1 column right and 1 row down
    Sheet1.Range("B2").Offset(1, 1).Value2 = "Cell C3"

    ' Write to A1 - 1 column left and 1 row up
    Sheet1.Range("B2").Offset(-1, -1).Value2 = "Cell A1"

    ' Write to range E3:G13 - 1 column right and 1 row down
    Sheet1.Range("D2:F12").Offset(1, 1).Value2 = "Cells E3:G13"

End Sub

Using the Range CurrentRegion

CurrentRegion returns a range of all the adjacent cells to the given range.

In the screenshot below you can see the two current regions. I have added borders to make the current regions clear.

VBA CurrentRegion

A row or column of blank cells signifies the end of a current region.

You can manually check the CurrentRegion in Excel by selecting a range and pressing Ctrl + Shift + *.

If we take any range of cells within the border and apply CurrentRegion, we will get back the range of cells in the entire area.

For example
Range(“B3”).CurrentRegion will return the range B3:D14
Range(“D14”).CurrentRegion will return the range B3:D14
Range(“C8:C9”).CurrentRegion will return the range B3:D14
and so on

How to Use

We get the CurrentRegion as follows

' Current region will return B3:D14 from above example
Dim rg As Range
Set rg = Sheet1.Range("B3").CurrentRegion

Read Data Rows Only

Read through the range from the second row i.e.skipping the header row

' Current region will return B3:D14 from above example
Dim rg As Range
Set rg = Sheet1.Range("B3").CurrentRegion

' Start at row 2 - row after header
Dim i As Long
For i = 2 To rg.Rows.Count
    ' current row, column 1 of range
    Debug.Print rg.Cells(i, 1).Value2
Next i

Remove Header

Remove header row(i.e. first row) from the range. For example if range is A1:D4 this will return A2:D4

' Current region will return B3:D14 from above example
Dim rg As Range
Set rg = Sheet1.Range("B3").CurrentRegion

' Remove Header
Set rg = rg.Resize(rg.Rows.Count - 1).Offset(1)

' Start at row 1 as no header row
Dim i As Long
For i = 1 To rg.Rows.Count
    ' current row, column 1 of range
    Debug.Print rg.Cells(i, 1).Value2
Next i

 

Using Rows and Columns as Ranges

If you want to do something with an entire Row or Column you can use the Rows or Columns property of the Worksheet. They both take one parameter which is the row or column number you wish to access

' https://excelmacromastery.com/
Public Sub UseRowAndColumns()

    ' Set the font size of column B to 9
    Sheet1.Columns(2).Font.Size = 9

    ' Set the width of columns D to F
    Sheet1.Columns("D:F").ColumnWidth = 4

    ' Set the font size of row 5 to 18
    Sheet1.Rows(5).Font.Size = 18

End Sub

Using Range in place of Worksheet

You can also use Cells, Rows and Columns as part of a Range rather than part of a Worksheet. You may have a specific need to do this but otherwise I would avoid the practice. It makes the code more complex. Simple code is your friend. It reduces the possibility of errors.

 
The code below will set the second column of the range to bold. As the range has only two rows the entire column is considered B1:B2

' https://excelmacromastery.com/
Public Sub UseColumnsInRange()

    ' This will set B1 and B2 to be bold
    Sheet1.Range("A1:C2").Columns(2).Font.Bold = True

End Sub

 
You can download all the code for this post from the top of this article.
 

Reading Values from one Cell to another

In most of the examples so far we have written values to a cell. We do this by placing the range on the left of the equals sign and the value to place in the cell on the right. To write data from one cell to another we do the same. The destination range goes on the left and the source range goes on the right.

 
The following example shows you how to do this:

' https://excelmacromastery.com/
Public Sub ReadValues()

    ' Place value from B1 in A1
    Sheet1.Range("A1").Value2 = Sheet1.Range("B1").Value2

    ' Place value from B3 in sheet2 to cell A1
    Sheet1.Range("A1").Value2 = Sheet2.Range("B3").Value2

    ' Place value from B1 in cells A1 to A5
    Sheet1.Range("A1:A5").Value2 = Sheet1.Range("B1").Value2

    ' You need to use the "Value" property to read multiple cells
    Sheet1.Range("A1:A5").Value2 = Sheet1.Range("B1:B5").Value2

End Sub

 
As you can see from this example it is not possible to read from multiple cells. If you want to do this you can use the Copy function of Range with the Destination parameter

' https://excelmacromastery.com/
Public Sub CopyValues()

    ' Store the copy range in a variable
    Dim rgCopy As Range
    Set rgCopy = Sheet1.Range("B1:B5")

    ' Use this to copy from more than one cell
    rgCopy.Copy Destination:=Sheet1.Range("A1:A5")

    ' You can paste to multiple destinations
    rgCopy.Copy Destination:=Sheet1.Range("A1:A5,C2:C6")

End Sub

 
The Copy function copies everything including the format of the cells. It is the same result as manually copying and pasting a selection. You can see more about it in the Copying and Pasting Cells section.

Using the Range.Resize Method

When copying from one range to another using assignment(i.e. the equals sign), the destination range must be the same size as the source range.

Using the Resize function allows us to resize a range to a given number of rows and columns.

For example:
 

' https://excelmacromastery.com/
Sub ResizeExamples()
 
    ' Prints A1
    Debug.Print Sheet1.Range("A1").Address

    ' Prints A1:A2
    Debug.Print Sheet1.Range("A1").Resize(2, 1).Address

    ' Prints A1:A5
    Debug.Print Sheet1.Range("A1").Resize(5, 1).Address
    
    ' Prints A1:D1
    Debug.Print Sheet1.Range("A1").Resize(1, 4).Address
    
    ' Prints A1:C3
    Debug.Print Sheet1.Range("A1").Resize(3, 3).Address
    
End Sub

 
When we want to resize our destination range we can simply use the source range size.

In other words, we use the row and column count of the source range as the parameters for resizing:

' https://excelmacromastery.com/
Sub Resize()

    Dim rgSrc As Range, rgDest As Range
    
    ' Get all the data in the current region
    Set rgSrc = Sheet1.Range("A1").CurrentRegion

      ' Get the range destination
    Set rgDest = Sheet2.Range("A1")
    Set rgDest = rgDest.Resize(rgSrc.Rows.Count, rgSrc.Columns.Count)
    
    rgDest.Value2 = rgSrc.Value2

End Sub

 
We can do the resize in one line if we prefer:

' https://excelmacromastery.com/
Sub ResizeOneLine()

    Dim rgSrc As Range
    
    ' Get all the data in the current region
    Set rgSrc = Sheet1.Range("A1").CurrentRegion
    
    With rgSrc
        Sheet2.Range("A1").Resize(.Rows.Count, .Columns.Count).Value2 = .Value2
    End With
    
End Sub

Reading Values to variables

We looked at how to read from one cell to another. You can also read from a cell to a variable. A variable is used to store values while a Macro is running. You normally do this when you want to manipulate the data before writing it somewhere. The following is a simple example using a variable. As you can see the value of the item to the right of the equals is written to the item to the left of the equals.

' https://excelmacromastery.com/
Public Sub UseVariables()

    ' Create
    Dim number As Long

    ' Read number from cell
    number = Sheet1.Range("A1").Value2

    ' Add 1 to value
    number = number + 1

    ' Write new value to cell
    Sheet1.Range("A2").Value2 = number

End Sub

 
To read text to a variable you use a variable of type String:

' https://excelmacromastery.com/
Public Sub UseVariableText()

    ' Declare a variable of type string
    Dim text As String

    ' Read value from cell
    text = Sheet1.Range("A1").Value2

    ' Write value to cell
    Sheet1.Range("A2").Value2 = text

End Sub

 
You can write a variable to a range of cells. You just specify the range on the left and the value will be written to all cells in the range.

' https://excelmacromastery.com/
Public Sub VarToMulti()

    ' Read value from cell
    Sheet1.Range("A1:B10").Value2 = 66

End Sub

 
You cannot read from multiple cells to a variable. However you can read to an array which is a collection of variables. We will look at doing this in the next section.

How to Copy and Paste Cells

If you want to copy and paste a range of cells then you do not need to select them. This is a common error made by new VBA users.

Note: We normally use Range.Copy when we want to copy formats, formulas, validation. If we want to copy values it is not the most efficient method.
I have written a complete guide to copying data in Excel VBA here.

 
You can simply copy a range of cells like this:

Range("A1:B4").Copy Destination:=Range("C5")

 
Using this method copies everything – values, formats, formulas and so on. If you want to copy individual items you can use the PasteSpecial property of range.

 
It works like this

Range("A1:B4").Copy
Range("F3").PasteSpecial Paste:=xlPasteValues
Range("F3").PasteSpecial Paste:=xlPasteFormats
Range("F3").PasteSpecial Paste:=xlPasteFormulas

 
The following table shows a full list of all the paste types

Paste Type
xlPasteAll
xlPasteAllExceptBorders
xlPasteAllMergingConditionalFormats
xlPasteAllUsingSourceTheme
xlPasteColumnWidths
xlPasteComments
xlPasteFormats
xlPasteFormulas
xlPasteFormulasAndNumberFormats
xlPasteValidation
xlPasteValues
xlPasteValuesAndNumberFormats

Reading a Range of Cells to an Array

You can also copy values by assigning the value of one range to another.

Range("A3:Z3").Value2 = Range("A1:Z1").Value2

 
The value of  range in this example is considered to be a variant array. What this means is that you can easily read from a range of cells to an array. You can also write from an array to a range of cells. If you are not familiar with arrays you can check them out in this post.  

 
The following code shows an example of using an array with a range:

' https://excelmacromastery.com/
Public Sub ReadToArray()

    ' Create dynamic array
    Dim StudentMarks() As Variant

    ' Read 26 values into array from the first row
    StudentMarks = Range("A1:Z1").Value2

    ' Do something with array here

    ' Write the 26 values to the third row
    Range("A3:Z3").Value2 = StudentMarks

End Sub

 
Keep in mind that the array created by the read is a 2 dimensional array. This is because a spreadsheet stores values in two dimensions i.e. rows and columns

Going through all the cells in a Range

Sometimes you may want to go through each cell one at a time to check value.

 
You can do this using a For Each loop shown in the following code

' https://excelmacromastery.com/
Public Sub TraversingCells()

    ' Go through each cells in the range
    Dim rg As Range
    For Each rg In Sheet1.Range("A1:A10,A20")
        ' Print address of cells that are negative
        If rg.Value < 0 Then
            Debug.Print rg.Address + " is negative."
        End If
    Next

End Sub

 
You can also go through consecutive Cells using the Cells property and a standard For loop.

 
The standard loop is more flexible about the order you use but it is slower than a For Each loop.

' https://excelmacromastery.com/
Public Sub TraverseCells()
 
    ' Go through cells from A1 to A10
    Dim i As Long
    For i = 1 To 10
        ' Print address of cells that are negative
        If Range("A" & i).Value < 0 Then
            Debug.Print Range("A" & i).Address + " is negative."
        End If
    Next
 
    ' Go through cells in reverse i.e. from A10 to A1
    For i = 10 To 1 Step -1
        ' Print address of cells that are negative
        If Range("A" & i) < 0 Then
            Debug.Print Range("A" & i).Address + " is negative."
        End If
    Next
 
End Sub

Formatting Cells

Sometimes you will need to format the cells the in spreadsheet. This is actually very straightforward. The following example shows you various formatting you can add to any range of cells

' https://excelmacromastery.com/
Public Sub FormattingCells()

    With Sheet1

        ' Format the font
        .Range("A1").Font.Bold = True
        .Range("A1").Font.Underline = True
        .Range("A1").Font.Color = rgbNavy

        ' Set the number format to 2 decimal places
        .Range("B2").NumberFormat = "0.00"
        ' Set the number format to a date
        .Range("C2").NumberFormat = "dd/mm/yyyy"
        ' Set the number format to general
        .Range("C3").NumberFormat = "General"
        ' Set the number format to text
        .Range("C4").NumberFormat = "Text"

        ' Set the fill color of the cell
        .Range("B3").Interior.Color = rgbSandyBrown

        ' Format the borders
        .Range("B4").Borders.LineStyle = xlDash
        .Range("B4").Borders.Color = rgbBlueViolet

    End With

End Sub

Main Points

The following is a summary of the main points

  1. Range returns a range of cells
  2. Cells returns one cells only
  3. You can read from one cell to another
  4. You can read from a range of cells to another range of cells.
  5. You can read values from cells to variables and vice versa.
  6. You can read values from ranges to arrays and vice versa
  7. You can use a For Each or For loop to run through every cell in a range.
  8. The properties Rows and Columns allow you to access a range of cells of these types

What’s Next?

Free VBA Tutorial If you are new to VBA or you want to sharpen your existing VBA skills then why not try out the The Ultimate VBA Tutorial.

Related Training: Get full access to the Excel VBA training webinars and all the tutorials.

(NOTE: Planning to build or manage a VBA Application? Learn how to build 10 Excel VBA applications from scratch.)

Понравилась статья? Поделить с друзьями:
  • Excel vba for cell value
  • Excel vba for cell change
  • Excel vba for append as
  • Excel vba for all shapes
  • Excel vba for all selected cells