Гость
Форумы / Visual Basic [игнор отключен] [закрыт для гостей] / Проект статьи для FAQ про избавление от Select / 25 сообщений из 30, страница 1 из 2
07.04.2012, 23:11
    #37743458
Shocker.Pro
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
Выношу на суд общественности, принимаются предложения, поправки, дополнения.
Думаю, что также нужно больше примеров.
------------------------------------------------------------------------------------

Заглавное утверждение этой статьи:
Уберите из своего кода Select, Selection, Activate
а также то, что начинается с Active (ActiveSheet, ActiveDocument, ActiveWorkbook, ActiveCell и т.п.)


Теперь разберем подробнее.

А почему нужно от всего этого избавиться?

Во-первых – код можно сильно сократить, его будет легче читать, разбирать, редактировать, он будет работать быстрее.

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

Откуда все берется?

Ну прежде всего из записи макросов. Стандартная и разумная рекомендация – если не знаешь, как сделать что-то – запусти запись макроса и сделай нужные действия. Но только макрос ведь пишет все действия пользователя, а пользователь сначала выделяет объект, а потом с ним что-то делает. Поэтому и получается, что макрос сначала записывает команду Select, а потом какие-то действия над Selection.
Записав макрос, дальше стоит его разобрать, вычленить необходимые методы и функции и убрать лишнее. Или посмотреть, как записался макрос, а дальше писать свою программу.

Как избавиться?

Рассмотрим на примере Экселя. Прежде всего сама книга. В большинстве случаев ActiveWorkbook надо заменить на ThisWorkbook. ActiveWorkbook – это активная книга (которую, как я упоминал, пользователь или какие-то обстоятельства могут изменить). А ThisWorkbook – это книга, в которой находится ваша программа и она всегда одна и та же, даже если активная книга поменялась.
Иногда ActiveWorkbook используется для работы с другой книгой, открытой или созданной из текущей. В этом случае надо поступить следующим образом:

Код: vbnet
1.
2.
3.
4.
5.
6.
7.
'объявляем переменную, которую будем использовать
'для ссылки на нужную книгу
Dim OtherWorkbook As Workbook
'затем создаем новую книгу:
Set OtherWorkbook = Workbooks.Add
'... или открываем существующую:
Set OtherWorkbook = Workbooks.Open("c:\blablabla.xls") 



С полученной переменной можно делать то, что раньше делалось через ActiveWorkbook. Например закрыть эту книгу:
Код: vbnet
1.
OtherWorkbook.Close



А как избавиться от Selection?

К примеру, мы покрасим текст в нескольких ячейках на другом листе. Записанный макрос будет выглядеть так:
Код: vbnet
1.
2.
3.
Sheets("Лист3").Select
Range("C17:D20").Select
Selection.Font.ColorIndex = 3



Но это запись действий пользователя. А нам совсем необязательно повторять все за пользователем. Мы можем покрасить текст напрямую:
Код: vbnet
1.
Sheets("Лист3").Range("C17:D20").Font.ColorIndex = 3



А если это нужно сделать в ранее созданной (см. выше) другой книге, то так:
Код: vbnet
1.
OtherWorkbook.Sheets("Лист3").Range("C17:D20").Font.ColorIndex = 3



А когда работа с Select, Active и прочим все же требуется?

В немногих случаях. Во-первых, когда требуется взаимодействие с пользователем. Например, пользователь на свое усмотрение выделил какой-то диапазон ячеек, а нам его нужно программой обработать. Тогда мы как раз и обращаемся к Selection. Или когда пользователю после завершения работы макроса требуется что-то отобразить или выделить. Тогда мы используем Activate или Select (как правило, как последнюю команду в работе программы).

Во-вторых, есть все-таки ситуации, в которых не обойтись без Select. Я пока столкнулся с одной ситуацией, в которой не нашел другого пути (впрочем, это не значит, что его нет), а именно программное закрепление областей в Экселе:
Код: vbnet
1.
2.
Range("B2").Select
Windows(1).FreezePanes = True
...
Рейтинг: 0 / 0
08.04.2012, 09:11
    #37743643
White Owl
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
Замени Sheets на Worksheets. Sheets это не только таблицы, это любые листы.

Упомяни что никогда нельзя писать просто Range() (как это сделано в твоем примере про закрепление областей).
Если Range() ни к чему не привязан, то он неявно берется от текущего объекта с фокусом, а это может быть все что угодно, в том числе и кнопка на листе у которой Range в принципе быть не может и макрос умрет.
...
Рейтинг: 0 / 0
08.04.2012, 09:33
    #37743648
qwerty112
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
Shocker.ProА как избавиться от Selection?

К примеру, мы покрасим текст в нескольких ячейках на другом листе. Записанный макрос будет выглядеть так:
Код: vbnet
1.
2.
3.
Sheets("Лист3").Select
Range("C17:D20").Select
Selection.Font.ColorIndex = 3



Но это запись действий пользователя. А нам совсем необязательно повторять все за пользователем. Мы можем покрасить текст напрямую:
Код: vbnet
1.
Sheets("Лист3").Range("C17:D20").Font.ColorIndex = 3


имхо, когда речь о "переделке" записанного макроса,
удобно (и быстро) переписать через with / end with
Код: vbnet
1.
2.
3.
4.
5.
with Sheets("Лист3")     ' Sheets("Лист3").Select
  with .Range("C17:D20") ' Range("C17:D20").Select
    .Font.ColorIndex = 3 ' Selection.Font.ColorIndex = 3
  end with
end with


что в случае нескольких команд в конечном with (там где .Font.ColorIndex = 3) , ещё и плюс по скорости выполнения
...
Рейтинг: 0 / 0
08.04.2012, 10:57
    #37743676
Shocker.Pro
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
White OwlЗамени Sheets на Worksheets. Sheets это не только таблицы, это любые листы.я-то это знаю, но именно так записывает макрос. В случае, когда обращаешься через имя листа, этот момент ведь не имеет значения, или я не прав?
qwerty112имхо, когда речь о "переделке" записанного макроса,
удобно (и быстро) переписать через with / end withДело в том, что те, кто хорошо владеет объектным программированием, это все и так знает. Я-то ориентируюсь на тех, кто в этом плавает и хочу объяснить как можно проще и не забивать голову дополнительной информацией. ИМХО, поначалу тогда нужно показать, как упростить путь к объекту через объектную переменную, а потом уже через With
White OwlУпомяни что никогда нельзя писать просто Range()да, это, пожалуй, стоит расписать. Что касается коллекции Worksheets и т.п. - она при обращении без указания книги всегда относится к ThisWorkbook или могут быть варианты?
...
Рейтинг: 0 / 0
08.04.2012, 13:29
    #37743789
Antonariy
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
Еще можно упомянуть, что если обращение идет к конкретной ячейке, а не диапазону, то лучше использовать Cells, а не Range, Cells работает быстрее.

Shocker.ProЧто касается коллекции Worksheets и т.п. - она при обращении без указания книги всегда относится к ThisWorkbook или могут быть варианты?Без вариантов.
...
Рейтинг: 0 / 0
08.04.2012, 20:35
    #37744240
White Owl
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
Shocker.ProWhite OwlЗамени Sheets на Worksheets. Sheets это не только таблицы, это любые листы.я-то это знаю, но именно так записывает макрос. В случае, когда обращаешься через имя листа, этот момент ведь не имеет значения, или я не прав?В принципе прав, но если человек попытается сделать перебор "все листы книги", а такие вопросы возникают часто. То Sheets при наличии листов другого типа породит кучку артефактов... С другой стороны, если человек напорется, то может быть он научиться? Или я слишком многого хочу?

Shocker.ProИМХО, поначалу тогда нужно показать, как упростить путь к объекту через объектную переменную, а потом уже через WithВот-вот.

Shocker.ProЧто касается коллекции Worksheets и т.п. - она при обращении без указания книги всегда относится к ThisWorkbook или могут быть варианты?не знаю. Но по моему оно тоже должно работать на основе объекта с фокусом. Впрочем, пути микрософта неисповедимы.
...
Рейтинг: 0 / 0
08.04.2012, 21:58
    #37744314
Antonariy
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
White OwlНо по моему оно тоже должно работать на основе объекта с фокусом.Объект с фокусом это Active, а This — объект, в контексте которого исполняется код.
...
Рейтинг: 0 / 0
09.04.2012, 02:53
    #37744455
White Owl
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
AntonariyWhite OwlНо по моему оно тоже должно работать на основе объекта с фокусом.Объект с фокусом это Active, а This — объект, в контексте которого исполняется код. Неверно.
...
Рейтинг: 0 / 0
09.04.2012, 08:26
    #37744516
HandKot
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
White OwlAntonariyпропущено...
Объект с фокусом это Active, а This — объект, в контексте которого исполняется код. Неверно.

а поподробнеее пожалуйста
...
Рейтинг: 0 / 0
09.04.2012, 08:37
    #37744525
Antonariy
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
Да уж, заинтриговал.
...
Рейтинг: 0 / 0
09.04.2012, 18:54
    #37745693
White Owl
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
AntonariyWhite OwlНо по моему оно тоже должно работать на основе объекта с фокусом.Объект с фокусом это Active, а This — объект, в контексте которого исполняется код.У Экселя есть Active* объекты, это те которые сам Эксель считает активными. А фокус это свойство системы, это внутренний указатель самой ОС который показывает какой виджет будет получать события клавиатуры. Виджеты ОС и объекты Экселя это очень разные вещи, которые, по хорошему, должны быть либо совсем независимыми либо совершенно синхронными.
Но! Эксель имеет кучку багов овеянных временем и ставшими фичами - в частности команды Select и Activate которые в зависимости от объекта на которые вызваны могут перемещать фокус а могут не перемещать. Active* для некоторых объектов следует за фокусом, для некоторых не следует.
Когда мы к этому ералашу добавляем контекстные вызовы без явного указания контекста - получается вообще кошмар. В Экселе по существу есть три контекстно активных объекта - this, active и виджет с фокусом. Какой из них будет использоваться для вызова контекстно зависимых методов зависит от метода. Не от каких-то общих правил, а у каждого метода есть свой "предпочтительный" контекст. Если ты способен точно помнить какой контекст когда будет использоваться, и ты веришь что в другой версии Экселя эти правила не изменяться - можешь использовать Range() и Sheets() без явного указания кому они принадлежат.
Ну а если хочешь писать надежные макросы - привыкни всегда явно указывать контекст.
...
Рейтинг: 0 / 0
09.04.2012, 23:39
    #37745971
Antonariy
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
White OwlAntonariyпропущено...
Объект с фокусом это Active, а This — объект, в контексте которого исполняется код.У Экселя есть Active* объекты, это те которые сам Эксель считает активными. А фокус это свойство системы, это внутренний указатель самой ОС который показывает какой виджет будет получать события клавиатуры.Подразумевалось объект экселя с фокусом.
...
Рейтинг: 0 / 0
09.04.2012, 23:56
    #37745983
Antonariy
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
White OwlЕсли ты способен точно помнить какой контекст когда будет использоваться, и ты веришь что в другой версии Экселя эти правила не изменяться - можешь использовать Range() и Sheets() без явного указания кому они принадлежат.Без явного указания Range и Cells можно использовать ничего не опасаясь в контексте листа, а Sheets в контексте книги, и это будут ThisWorksheet и ThisWorkbook соответственно. Sheets(x) можно использовать в контексте листа и это будет ThisWorkbook. Использовать Cells и Range в контексте книги нельзя. Использовать их же и Sheets в контексте модуля нельзя.
Никаких исключений из этих правил ни в одной версии экселя нет. Следуя этим правилам, я ни разу не повстречался ни с одним из "кучи багов".
...
Рейтинг: 0 / 0
10.04.2012, 10:46
    #37746344
MX-9
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
Уважаемые гуру,

поскольку здесь создается нетленка (эпохалка)
для будущих поколений
предлагаю Ексель заменить на Excel
ибо первое носит слегка неуважительный оттенок

При всем наблюдаемом море багов от MS
все же ничего лучшего пока на горизонте не просматривается

================
...
Рейтинг: 0 / 0
10.04.2012, 11:19
    #37746426
Shocker.Pro
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
MX-9, Ok
...
Рейтинг: 0 / 0
10.04.2012, 11:51
    #37746522
Диклевич Александр
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
А кто знает как обойтись без ActiveWorkbook при копировании Worksheet из текущей книги с созданием новой.
Т.е. такое не катит
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
Option Explicit
Sub Macro1()
Dim Wb As Workbook
    Set Wb = Worksheets("Sheet2").Copy '424 - Object required
    '...
    Set Wb = Nothing
End Sub


Хотя книга является результатом Worksheets("Sheet2").Copy.
А вот такое работает
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
Option Explicit
Sub Macro1()
Dim Wb As Workbook
    Worksheets("Sheet2").Copy
    Set Wb = ActiveWorkbook
    '...
    Set Wb = Nothing
End Sub
...
Рейтинг: 0 / 0
10.04.2012, 13:30
    #37746747
Antonariy
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
А у вас тут вообще туши свет.

1) Copy это процедура, а не функция.
2) Копируя лист вы никак не получите книгу .
...
Рейтинг: 0 / 0
10.04.2012, 14:14
    #37746837
Диклевич Александр
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
Но я получаю.
По крайней мере в Excel 2010.
Руками это делается так: на листе книги правой кнопкой, потом "Move or Copy", to book "(new book)", галочка "Create a copy" - и все - в результате копирования имеем новую книгу.
Вы первый код запускали? Новая книга создается? У меня да. Но она не присваивается объектной переменной. Хотя она книга.
...
Рейтинг: 0 / 0
10.04.2012, 14:17
    #37746846
Shocker.Pro
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
Код: vbnet
1.
2.
?typename(Worksheets("Лист1").Copy)
Boolean

кто вообще сказал, что метод возвращает книгу???
...
Рейтинг: 0 / 0
10.04.2012, 14:28
    #37746866
Диклевич Александр
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
Вот я и спрашиваю, как провернуть данную операцию без ActiveWorkbook.
Первый код приведен чисто для иллюстрации, я не говорю что он верен.
Логика такова: результатом
Код: vbnet
1.
Worksheets("Sheet2").Copy

является создание новой книги с копией листа "Sheet2". Как эту книгу "запихать" в объектную переменную без использования
Код: vbnet
1.
ActiveWorkbook

?
Я хочу быть уверен, что я "пихаю" именно эту книгу, а не какую нибудь другую, так понятно?
А то все такие умные прям.
Работай бы первый код, я бы знал наверняка, что в объекте сидит то что надо.
...
Рейтинг: 0 / 0
10.04.2012, 14:41
    #37746896
Shocker.Pro
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
Я думаю, что книга всегда добавляется в конец коллекции, поэтому можно
Код: vbnet
1.
2.
    Worksheets("Sheet2").Copy
    Set Wb = Workbooks(Workbooks.Count)
...
Рейтинг: 0 / 0
10.04.2012, 14:59
    #37746942
Диклевич Александр
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
Shocker.Pro,

Кстати, это тоже не гарантирует того что в Wb попадет книга, созданная в результате вызова метода Worksheets("Sheet2").Copy.
Мало ли что произойдет между
Код: vbnet
1.
 Worksheets("Sheet2").Copy

и
Код: vbnet
1.
Set Wb = Workbooks(Workbooks.Count)


Так что тут надо быть внимательным.
Конечно самый верный способ это
Код: vbnet
1.
Set Wb = Workbooks.Add...

и далее по тексту гонять листы как надо.
Но с Worksheets("Sheet2").Copy оказия получается, ведь в результате имеем новую книгу.
...
Рейтинг: 0 / 0
10.04.2012, 15:20
    #37746996
MX-9
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
Диклевич Александр,

после
Worksheets("Sheet2").Copy
создается не только книга - но и лист

поэтому, видимо, и не проходит
Set Wb = Worksheets("Sheet2").Copy '424 - Object required
- из-за неоднозначности результата
как указано выше - на выходе просто TRUE
...
Рейтинг: 0 / 0
10.04.2012, 15:32
    #37747027
Диклевич Александр
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
MX-9,

Да это все понятно, что создается книга с листом и все такое.
Вопрос в том как эту книгу (книга - это же объект Excel) присвоить объектной переменной Wb типа Workbook для дальнейшей работы, да так чтобы быть уверенным что я присваиваю именно эту книгу.
Set Wb = ActiveWorkbook и Set Wb = Workbooks(Workbooks.Count) не дают уверенности что я присваиваю именно то что надо, т.к. мало ли что произойдет между Worksheets("Sheet2").Copy и присваиванием. А это не хорошо.
...
Рейтинг: 0 / 0
10.04.2012, 15:34
    #37747035
Antonariy
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Проект статьи для FAQ про избавление от Select
Shocker.Proкто вообще сказал, что метод возвращает книгу???Кто сказал, что метод вообще что-то возвращает??????77

F2
Код: plaintext
1.
 Sub  Copy([Before], [After])
    Member of Excel.Worksheets


Диклевич АлександрНо я получаю.Я забыл, если не указывать параметры, то создается копия в новой книге. И никаких ссылок на новые объекты никуда не возвращается. Однако все равно не вижу проблем, багов и загадок. Вижу отсутствие понимания процессов.
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
Sub Macro1()
    MsgBox ActiveWorkbook.Name
    Worksheets(1).Copy
    MsgBox ActiveWorkbook.Name 'копия
    Worksheets(1).Copy
    MsgBox ActiveWorkbook.Name 'копия копии
    Worksheets(1).Copy
    MsgBox ActiveWorkbook.Name 'копия копии копии
End Sub


Диклевич Александр Мало ли что произойдет между Worksheets("Sheet2").Copy и Set Wb = Workbooks(Workbooks.Count)Ничего не произойдет. Ничего физически не может произойти (если только не воткнуть туда DoEvents) — работа макроса и действия пользователя обрабатываются в одном и том же потоке, а значит они не могут происходить одновременно. Все макросы всех открытых книг будут исполняться в одном потоке по очереди, а значит тоже не могут пересечься.

поэтому, видимо, и не проходит
Set Wb = Worksheets("Sheet2").Copy '424 - Object required
- из-за неоднозначности результата
как указано выше - на выходе просто TRUEОпять эта ерунда... Результат более чем однозначен. Просто вы его не понимаете.
...
Рейтинг: 0 / 0
Форумы / Visual Basic [игнор отключен] [закрыт для гостей] / Проект статьи для FAQ про избавление от Select / 25 сообщений из 30, страница 1 из 2
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]