Страницы: 1
RSS
Обращаемся к ячейкам :)
 
Наша повседневная работа написания кода так или иначе связана с обращением к ячейкам.
Хотелось бы написать про методы обращения к ячейкам. Если что-то пропустил - не ругайте.  :D  
Итак, поехали...  :)  

Код
Sub CellsSelection()
    
    Dim cell As Range
    Dim severalCells As Range
    Dim rng As Range

    '=======================================
    'Выделяем ячейку F6
    '=======================================
    Set cell = Range("F6")
    Set cell = Cells(6, 6) 
    Set cell = Cells(6, "F")
    Set cell = Cells(81926) ' (16384 * 5) + 6
    Set cell = Cells.Item(6, 6)
    Set cell = Cells.Cells(6, 6)

    '=======================================
    '1. Выделяем диапазон D4:G10
    '=======================================
    Set rng = Range("D4:G10")
    Set rng = Range(Cells(4, "D"), Cells(10, "G"))
    Set rng = Range(Cells(4, 4), Cells(10, 7))
    Set rng = Range("D4", Cells(10, 7))
    Set rng = Range("D4", "G10")
    Set rng = Range(Range("D4"), "G10")
    Set rng = Range("D4").Resize(7, 4)
    'Обращаемся к диапазону без привязки к объекту Worksheet
    Set rng = Range("Лист1!D4:G10")
    
    
    'Во всех нижеприведённых методах необходимо
    'представить диапазон D4:G10 как лист
    'с верхней левой ячейкой A1, то есть мысленно
    'переносим D4:G10 в A1. :)
    
    '========================================
    '2. Выделяем ячейку F6 в диапазоне D4:G10
    '========================================
    Set cell = rng.Range("C3")
    Set cell = rng(3, 3)
    Set cell = rng(11)
    Set cell = rng.Item(3, 3)
    Set cell = rng.Item(11)
    Set cell = rng.Cells(11)
    Set cell = rng.Cells(3, 3)
    
    '===========================================
    '3. Выделяем ячейку E11 ВНЕ диапазона D4:G10
    '===========================================
    Set cell = rng(30)
    Set cell = rng.Item(30)
    Set cell = rng.Cells(30)
    Set cell = rng.Range("B8")
    Set cell = rng.Cells(8, 2)
    Set cell = rng(8, 2)
    Set cell = rng.Item(8, 2)
    Set cell = rng.Cells(8, 2)
    
    '=============================================
    '4. Выделяем диапазон E8:F9 в диапазоне D4:G10
    '=============================================
    Set severalCells = rng.Range("B5:C6")
    Set severalCells = rng.Range("B5", "C6")
    'В общем, принцип такой же, что и в пункте 2. :)
    
End Sub
There is no knowledge that is not power
 
Я бы еще добавил:
Цитата

сышышь, ячейка, ходьсуда!


Код
[A1]
Изменено: SkyPro - 17.12.2013 18:57:00
 
VBA-справку – на мыло примеры?

1. Помимо Cells можно еще вспомнить про Rows и Columns и Areas:
Цитата
Johny: Наша повседневная работа написания кода...  
Не наша и не повседневная работа:
Скрытый текст


По понедельникам:
Скрытый текст


Для пятниц:
Скрытый текст


2. Так как Range бывает разным: Cells, Columns, Rows, Areas
то и For - Each работает иначе
Скрытый текст


3. Можно вспомнить и про Offset, CurrentRegion, SpecialsCells, UsedRange и проч.
Но VBA-справка все же покороче будет    :)
Изменено: ZVI - 17.12.2013 21:19:03
 
@ZVI
Владимир, "For Each cell In rng" - это понятно. Просто хотел показать, что есть некоторые способы, которые могли бы облегчить обработку ячеек, так как некоторые способы плохо документированы или вообще недокументированы на MSDN.  :)  
Кстати, по поводу Rows. Есть очень интересный момент. Например, возьмём диапазон A1  :D  4. Пройдёмся по Rows и возьмём первую ячейку каждой строки через Item:

Код
Sub EnumerateRows()

    Dim rngRow As Range
    
    For Each rngRow In Range("A1:D4").Rows
        Debug.Print rngRow(1)
    Next

End Sub
Работать не будет.  :(  А вот так будет:
Код
Debug.Print rngRow.Cells(1) 
Debug.Print rngRow.Cells(1, 1) 
Debug.Print rngRow.Range("A1")
Почему не работает Item - не знаю.  :)  

Цитата
Но VBA-справка все же покороче будет  :)
И что - все эти способы есть в справке? А можно посмотреть на эту справку? :)
Изменено: Johny - 18.12.2013 08:27:05
There is no knowledge that is not power
 
раздел How to Reference Cells and Ranges
в 2003-м
фрилансер Excel, VBA - контакты в профиле
"Совершенствоваться не обязательно. Выживание — дело добровольное." Э.Деминг
 
@Саша
Про 2003-ий офис я забыл в 2007, поэтому его нема. :)
А можно скинуть этот хелп? И там прямо все-все способы прописаны? :)
There is no knowledge that is not power
 
да ну нет, конечно же.
но основные есть:

Which way do you want to reference cells?
Referring to cells and ranges using A1 notation
Referring to cells using index numbers
Referring to rows and columns
Referring to cells using shortcut notation
Referring to named ranges
Referring to cells relative to other cells
Referring to cells using a Range object
Referring to all the cells on the worksheet
Referring to multiple ranges

(там не одна страничка, как скинуть - не знаю :)
в закладках есть ссыль на www.msoffice.nm.ru - помнится, там были практически все способы (даже ненужные), но сегодня мой AVG ругается на вирусы и отказывается открывать эту ссылку :(
фрилансер Excel, VBA - контакты в профиле
"Совершенствоваться не обязательно. Выживание — дело добровольное." Э.Деминг
 
ikki, не могу ни одну из ваших ссылок открыть
 
сорри.
это внутренние ссылки.
из автономной справки vba.
копипаст виноват.
фрилансер Excel, VBA - контакты в профиле
"Совершенствоваться не обязательно. Выживание — дело добровольное." Э.Деминг
 
Цитата
ikki, не могу ни одну из ваших ссылок открыть
Уважаемый, а в чём проблема?  Первая же ссылка в поисковике ведёт на такой же список тем.
 
@Johny
Евгений, идея собрать варианты в одном месте хорошо, тема полезная, кому-то может очень даже пригодиться.
А по поводу Rows, так там все работает штатно, просто Item у Rows это тоже Rows
Код
Sub EnumerateRows()
    Dim rngRow As Range
    For Each rngRow In Range("A1:D4").Rows
        Debug.Print rngRow.Item(1).Address
    Next
End Sub 

При Set rng =  Range( "адрес" )  Excel подразумевает по умолчанию Set rng =   Range( "адрес" ).Cells , тогда и rng.Item(i) является rng.Cells( i )
Но если явно прописано .Rows то и его .Item(i) будет с той же "фамилией" .Rows( i )
Изменено: ZVI - 19.12.2013 05:56:38
 
Цитата
Уважаемый, а в чём проблема?
Уважаемый, да ни в чём, просто сказал, что ссылки не работают, вот и все. ;)
 
Цитата
ZVI: Но если явно прописано .Rows то и его .Item(i) будет с той же "фамилией" .Rows( i )
@Владимир, нет такого объекта "Row", а есть Range.  :)  
В одной авторитетной книге написано:

Цитата
Curiously, you can not replace rngRow.Cells(1) with rngRow(1), as you can with a normal Range
object, because it returns a reference to the entire row and causes a run-time error. It seems that there is
something special about the Range object referred to by the Rows and Columns properties. You may find
it helps to think of them as Row and Column objects, even though such objects do not officially exist.
Как видно, авторы предлагают думать, что .Rows/Columns возвращают некий объект Row/Column (которого на самом деле нет). А вот что "something special" в нём, неизвестно.  :)
Изменено: Johny - 19.12.2013 11:38:20
There is no knowledge that is not power
 
Цитата
Johny: @Владимир, нет такого объекта "Row", а есть Range.   :)  
Евгений, Вы обсуждаете фразу, которую я не приводил, почему тогда не есть ли жизнь на Марсе?   :)  

Cells и Rows - это свойства (properties) объекта.
В зависимости от примененного свойства может поменяться интерфейс объекта  (см. For-Each в п.2, сообщение #3 ) и его другие свойства, например, значение Item (уже пояснял в #11) или значение Count (объекта Range):
Код
Sub Test1()
  Dim Rng1 As Range, Rng2 As Range
  Set Rng1 = Range("A1:D4").Cells
  Set Rng2 = Range("A1:D4").Rows
  Debug.Print "Item(1)", Rng1(1).Address, Rng2(1).Address
  Debug.Print "Count", Rng1.Count, Rng2.Count
End Sub
Это все учтено во внутренней реализации класса для объекта Range и отражается во внешнем VBA-интерфейсе.
Изменено: ZVI - 20.12.2013 05:10:56
 
Цитата
В зависимости от примененного свойства может поменяться интерфейс объекта
Владимир, если я пишу "Dim cell As Range", то я использую интерфейс Range, а не какой-либо другой, поэтому не важно, какое свойство я вызываю - Cells или Rows - я продолжаю "общаться" с интерфейсом Range. Поэтому это утверждение неправильное.  :|  
Вот что приблизительно происходит внутри на примере C#:
Код
    interface IRange
    {
        string Address { get; set; }
        string Formula { get; set; }
        IRange Cells { get; set; }
    }

    public class Range : IRange { }
    public class Rows : IRange { }
    public class Cells : IRange { } 
     
    public void TestRange()
    {
        IRange var1 = new Range();
        IRange var2 = new Rows();
        IRange var3 = new Cells();

        Console.WriteLine(var1.Address);
        Console.WriteLine(var2.Address);
        Console.WriteLine(var3.Address);
    } 
Как видно, мы общаемся с разными классами через ОДИН интерфейс. :)
There is no knowledge that is not power
 
И что мешает в любом классе, унаследовавшем интерфейс, добавить свои свойства, методы и события?
Не говоря уже о своей реализации унаследованных.
 
Тогда получается, что в Rows свойство Item переопределено? :)
There is no knowledge that is not power
 
Евгений Вы не отвечаете на мои вопросы ;)

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

Ответ на Ваш вопрос - из примера кода в сообщении #11 видно, что свойства Item и Count реализованы по разному, разве Вы не видите отличий в выводимых сообщениях?

Предлагаю все же вернуться к Вашей первоначальной теме, ради нее и я сюда заглянул.
 
Владимир, моё сообщение #17 это подразумевало. :) Я всё понял - поэтому так кратко и написал. :)
Просто совсем забыл с этим C#'ом, что в VBA мы имеем дело с интерфейсами, а не с объектами. :)
There is no knowledge that is not power
 
Ну да, C# однозначно честнее в этом плане.

По поводу формул, в VBA справке есть раздел Range Collection.
Там вскользь упомянуто, что (подчеркнуто мною)

Range Collection represents a cell, a row, a column, a selection of cells containing one or more contiguous blocks of cells, or a 3-D range.

Using the Range Collection
The following properties and methods for returning a Range object are described in this section:  
  • Range property
  • Cells property
  • Range and Cells
  • Offset property
  • Union method
и есть некоторые примеры использования формул.
Но в Вашей коллекции примеров побольше, да и полезно в одном месте собрать их.
 
Цитата
3. Можно вспомнить и про Offset, CurrentRegion, SpecialsCells, UsedRange и проч.
Offset, CurrentRegion - c этим всё понятно.
SpecialCells интересен в плане быстрого удаления отфильтрованных данных. Напишу про это.
Часто многим требуется удалить из таблицы какие-то строки, в одном из столбцов которых находятся нужные значения.
Кто-то делает циклом, кто-то через Find. Согласен, что им есть место, но в случае больших таблиц всё-таки быстрее метод с автофильтром.
Как он работает.
Возьмём, к примеру, диапазон A1: G5000. Итак, я знаю строку, с которой начинается таблица (исключая заголовок, где, собственно, и стоит фильтр). Далее, определяю последнюю строку (получаю 5000) и храню в переменной. После этого фильтрую данные. А далее использую SpecialCells и Delete:

Код
Sub QuickRemove()

    Dim lastRow As Long, rng As Range, rngVisible As Range

    Call ResetAutoFilter(ActiveSheet)

    'Последняя строка
    lastRow = Cells(Rows.Count, 1).End(xlUp).Row
    
    'Фильтруем
    Range("A1:G1").AutoFilter Field:=3, Criteria1:=555
    
    'On Error Resume Next обязательно, так как в случае если ничего не отфильтруется,
    'то возникает ошибка, а так - мы её подавляем.
    On Error Resume Next
    'Видимые ячейки - это то, что мы отфильтровали - значит, то, что нам надо.
    'Диапазон начинается со второй строки, так как первая строка - это заголовок таблицы.
    Set rngVisible = Range("A2:G" & lastRow).SpecialCells(xlCellTypeVisible)
    'Если Err = 0, то что-то отфильтровалось.
    'Можно ещё проверить так: If Not rngVisible Is Nothing Then ...
    If Err = 0 Then
        rngVisible.Delete 3 'Удаляем все строки - миссия выполнена. :)
    Else
        MsgBox Err.Description
        Err.Clear
    End If

End Sub 

Такого же эффекта можно добиться ещё меньшим количество строк (да и код более элегантный получается  :)  ). Идея - в комментарии.

Код
Sub AnotherQuickRemove()

    Dim rngVisible As Range

    Call ResetAutoFilter(ActiveSheet)
    
    'Фильтруем
    Range("A1:G1").AutoFilter Field:=3, Criteria1:=12

    On Error Resume Next
    
    'Объёкт AutoFilter имеет свойство Range, который даёт нам диапазон,
    'находящийся "в ведомости" фильтра. :)
    'Но есть один нюанс - диапазон также содержит заголовок, который нам не нужен.
    'Данные  манипуляции избавляются от заголовка.
    With ActiveSheet.AutoFilter.Range
        Set rngVisible = .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible)
    End With
    
    If Not rngVisible Is Nothing Then
        rngVisible.Delete 3
    Else
        MsgBox Err.Description
        Err.Clear
    End If

End Sub
В процедуре используется вспомогательная процедура "ResetAutoFilter", которая сбрасывает каждый фильтр и не удаляет сам фильтр с листа (может, кому пригодится  :)  ):
Код
Sub ResetAutoFilter(sheet As Worksheet)
    Dim fs As Filters, rng As Range, i As Integer
    With sheet
        If .AutoFilterMode Then
            With .AutoFilter
                Set fs = .Filters
                Set rng = .Range
            End With
            For i = 1 To fs.Count
                If fs(i).On Then rng.AutoFilter i
            Next
        End If
    End With
End Sub
UsedRange удобно использовать для удаления "невидимого" форматирования, которое влияет на размер полос прокрутки (когда полоса прокрутки "говорит", что на листе много строк/столбцов, в то время как их очень мало).
Изменено: Johny - 20.12.2013 11:28:49 (Орфографическая ошибка :))
There is no knowledge that is not power
Страницы: 1
Наверх