Страницы: 1
RSS
Как без цикла инвертировать выделение?
 
В топике "Как расформатировать весь лист кроме выделенного диапазона?" ТУХАЧЕВСКИЙ посоветовал мне посмотреть как инвертировать Selection по ссылке:  http://www.dailydoseofexcel.com/archives/2004/07/13/inverting-the-selection/  
приведённый по ссылке макрос:  
 
Sub InvertSelection()  
  Dim rBig As Range, rSmall As Range, iCell As Range, rNew As Range  
  If Not TypeName(Selection) = "Range" Then Exit Sub  
  Set rSmall = Selection  
  'Set rBig = ActiveSheet.Cells ' все ячейки листа  
  'Set rBig = ActiveSheet.UsedRange ' ячейки листа в UsedRange  
  Set rBig = Selection.Parent.UsedRange   ' ячейки листа в UsedRange  
  For Each iCell In rBig.Cells   ' цикл по всем ячейкам rBig  
     If Intersect(iCell, rSmall) Is Nothing Then  
        If rNew Is Nothing Then  
           Set rNew = iCell  
        Else  
           Set rNew = Union(rNew, iCell)  
        End If  
     End If  
  Next iCell  
  If rNew Is Nothing Then Exit Sub  
  rNew.Select  
End Sub  
 
работает не со всем листом, а только с UsedRange.  
 
Если же вместо    
Set rBig = Selection.Parent.UsedRange    
сделать Set rBig = ActiveSheet.Cells  
то Ёксель задумывается очень надолго, т.к. там будет цикл по всем ячейкам листа...  
 
И это в 2003-ем, а что будет в 2007-ом?  
Неужели кроме цикла по всем ячейкам никак нельзя выделить ячейки, не входящие в Selection ?
С уважением, Алексей (ИМХО: Excel-2003 - THE BEST!!!)
 
пардон, забыл файлик-пример приаттачить:
С уважением, Алексей (ИМХО: Excel-2003 - THE BEST!!!)
 
В той теме шёл разговор про расформатирование всего, что вне Selection  
И насколько я понял, вы как таковой диапазон вне Selection целиком не определяете, а вычисляете по очереди диапазоны столбцов и строк, лежащих вне Selection и расформатируете каждый из них.  
А как такового диапазона "Outside_Selection" в вашем примере не формируется...  
Или я не так понял?
С уважением, Алексей (ИМХО: Excel-2003 - THE BEST!!!)
 
The_Prist  
честно говоря, на последних строках кода вашего макроса по расформатированию:  
   Union(Range(Cells(1, lClearCols), Cells(1, lClearEndCol)).EntireColumn, Range(Cells(lClearRows, 1), Cells(lClearEndRow, 1)).EntireRow).ClearFormats  
   Range(Cells(1, lClearStartCol), Cells(1, lClearCol)).EntireColumn.ClearFormats  
   Range(Cells(lClearStartRow, 1), Cells(lClearRow, 1)).EntireRow.ClearFormats  
у меня при попытке разобраться чуть не "съехала крыша" и поэтому с не смог сформировать целиком диапазон вне выделения и выделить его...  
Вас не очень затруднит модифицировать свой макрос    
'---------------------------------------------------------------------------------------  
' Procedure : UnFormat_Invert_Selecton  
' Author    : The_Prist  
' URL       : http://www.planetaexcel.ru/forum.php?thread_id=13916, post_104142.xls  
' DateTime  : 27.02.2010 16:31  
' Purpose   : сброс форматирования ячеек вне области выделения  
'---------------------------------------------------------------------------------------  
в макрос Select_Outside?  
 
А новая тема потому, что та расширилась и стала не совсем соответствовать её названию. А название темы к сожалению движок форума не позволяет менять даже её топик-стартеру. А это не только затрудняет поиск, но и приводит к обидам знатоков типа "Я сделал точно то что просили, а вы теперь оказывается хотите другого".  
К стати, а индексация поиска что-то не идёт или идёт с большим запозданием?  
Позавчерашние темы ещё некоторые не проиндексированы, что вызывает дублирование вопросов.
С уважением, Алексей (ИМХО: Excel-2003 - THE BEST!!!)
 
{quote}{login=Alex_ST}{date=02.03.2010 03:41}{thema=Как без цикла инвертировать выделение?}{post}В топике "Как расформатировать весь лист кроме выделенного диапазона?" ... приведённый по ссылке макрос:  
Sub InvertSelection()  
...  
End Sub  
работает не со всем листом, а только с UsedRange.{/post}{/quote}  
Alex_ST, не имеет смысла очищать форматы вне диапазона UsedRange, потому что там нет никаких форматов. Может, уточните с какой целью Вам требуется выделять ячейки и вне диапазона UsedRange, вдруг это тоже не имеет смысла? :)
 
Ну, например, я всем ячейкам листа, кроме выделенных, хочу задать какой-нибудь хитро-выпендренный формат...  
А в общем случае, если процедура "выбора не выбранного" будет отработана, то её можно будет применить в том числе и для расформатирования всех ячеек вне выделения
С уважением, Алексей (ИМХО: Excel-2003 - THE BEST!!!)
 
Алекс, но ведь форматированная ячейка уже попадает в UsedRange. А если ячейка не форматирована - зачем её расформатировать? :-)
 
к стати, некто :),  
UsedRange почему-то не всегда корректно обрабатывает ячейки на краях диапазона если они пустые, но имеют какое-нибудь отличное от стандартного форматирование.  
Это проявляется обычно не сразу, а после нескольких операций удаления/добавления крайних ячеек  UsedRange
С уважением, Алексей (ИМХО: Excel-2003 - THE BEST!!!)
 
Уж не помню, на каком форуме (пришел туда по ссылке из одной из тем Планеты), но там сравнивались достоинства и недостатки различных методов определения последней ячейки листа (UsedRange, xlLastCells и т.д., точно не помню...).    
И там как раз и писАлось про такой недостаток UsedRange
С уважением, Алексей (ИМХО: Excel-2003 - THE BEST!!!)
 
{quote}{login=Alex_ST}{date=02.03.2010 11:07}{thema=}{post}Уж не помню, на каком форуме...{/post}{/quote}  
Даю гарантию на $100, что вне диапазона UsedRange нет ни одной ячейки с форматом. Недостатки UsedRange в данном случае являются преимуществом. Если Вы захватите еще и ячейки вне пределов UsedRange, то время обработки увеличится значительно, особенно в Excel 2007.
 
Ну, может быть оно и так, но всё равно, даже UsedRange может быть очень большим.  
Поэтому вопрос про инверсию селекции БЕЗ ЦИКЛА всё равно остаётся открытым.  
 
Ведь есть же такая удобная команда Intersect ...  
Эх! Была бы в VBA команда, обратная Intersect (NonIntersect :-), возвращающая диапазон, состоящий из непересекающихся частей диапазонов-аргументов, проблем бы не было во многих случаях... Но мечтать не вредно.
С уважением, Алексей (ИМХО: Excel-2003 - THE BEST!!!)
 
Вот 2 варианта без цикла:  
 
Sub InverseSelection1()  
 Dim a As String  
 Application.ScreenUpdating = False  
 Application.EnableEvents = False  
 ActiveSheet.Copy  
 With ActiveSheet  
   .UsedRange.Value = 1  
   Selection.Formula = "=0"  
   a = .UsedRange.SpecialCells(xlCellTypeConstants).Address(0, 0)  
   .Parent.Close False  
 End With  
 Range(a).Select  
 Application.EnableEvents = True  
 Application.ScreenUpdating = True  
End Sub  
 
Sub InverseSelection2()  
 Dim a As String, Sh As Worksheet  
 Set Sh = ActiveSheet  
  With Application  
   .ScreenUpdating = False  
   .EnableEvents = False  
   .DisplayAlerts = False  
   ActiveSheet.Copy Before:=Sheets(1)  
   With ActiveSheet  
     .UsedRange.Value = 1  
     Selection.Formula = "=0"  
     a = .UsedRange.SpecialCells(xlCellTypeConstants).Address(0, 0)  
     .Delete  
   End With  
   Sh.Activate  
   Range(a).Select  
   .DisplayAlerts = True  
   .EnableEvents = True  
   .ScreenUpdating = True  
 End With  
End Sub
 
Попробуйте на листе проставить границы ВСЕХ ячеек и увидите, что с UsedRange всё-таки глючит...  
Я сегодня попробовал собирать диапазоны выше, ниже, правее и левее Selection при помощи Union:  
Sub FastInvertSelection()  
  Dim rAbove As Range, rBelow As Range, rLeft As Range, rRight As Range   ' диапазоны выше, ниже, правее и левее Selection  
  Dim rOutside As Range   ' диапазон "вне Selection"  
  Dim selLeft As Long, selTop As Long, selRight As Long, selBottom As Long   ' границы Selection  
  Dim iLastRow As Long, iLastClm As Long   ' последние ряд и столбец листа  
  ' определяем границы  
  iLastRow = ActiveSheet.Rows.Count  
  iLastClm = ActiveSheet.Columns.Count  
  With Selection  
     selLeft = .Column: selRight = .Column + .Columns.Count  
     selTop = .Row: selBottom = .Row + .Rows.Count  
  End With  
  ' устанавливаем диапазоны выше, ниже, правее и левее Selection  
  If selLeft > 1 Then   ' если Selection не "прижато" к левой границе ActiveSheet  
     Set rLeft = Range(Cells(1, 1), Cells(iLastRow, selLeft - 1))  
  End If  
  If selTop > 1 Then   ' если Selection не "прижато" к верхней границе ActiveSheet  
     Set rAbove = Range(Cells(1, 1), Cells(selTop - 1, iLastClm))  
  End If  
  If selRight < ActiveSheet.Columns.Count Then   ' если Selection не "прижато" к правой границе ActiveSheet  
     Set rRight = Range(Cells(1, selRight), Cells(iLastRow, iLastClm))  
  End If  
  If selBottom < ActiveSheet.Rows.Count Then   ' если Selection не "прижато" к нижней границе ActiveSheet  
     Set rBelow = Range(Cells(selBottom, 1), Cells(iLastRow, iLastClm))  
  End If  
  ' собираем из кусочков диапазон "вне Selection"  
  Set rOutside = Union(rAbove, rLeft, rBelow, rRight)  
 
  rOutside.Select  
End Sub  
 
Проблема встала, что если Selection "прижато" хотя бы к одной из границ листа, соответствуюший диапазон = Nothing и Union с ним работать отказывается...  
Ну не перебирать же 16 комбинаций из 4-х диапазонов, любой из которых может быть Nothing ...  
А как применить обработчик ошибок, что-то никак не соображу....
С уважением, Алексей (ИМХО: Excel-2003 - THE BEST!!!)
 
{quote}{login=The_Prist}{date=03.03.2010 05:40}{thema=}{post}Применительно к моему примеру - см.вложение.  
 
В результате работы макроса создается обратный веделению диапазон. Для просмотра раскомментируйте необходимые строки.{/post}{/quote}  
Не знаю важно ли это, но в приведенных мною вариантах изначально могут быть выделены сразу несколько несмежных диапазонов.  
Вообще интересно было бы услышать от Alex_ST пример какого-нибудь практического применения данной задачи, или просто познаем мир?
 
Гарантия истекла - снимаю :)  
Взамен - 2 варианта инвертирования выделения.  
Работают быстро, со всеми ячейками листа, поддерживается выделение нескольких несмежных диапазонов. Первый вариант предпочтительнее, так как работает и при защищенной структуре книги.  
 
Sub InverseSelection1()  
 Dim a As String  
 a = Selection.Address  
 With ActiveSheet  
   With Workbooks.Add  
     With ActiveSheet  
       .Cells.FormatConditions.Add Type:=xlCellValue, Operator:=xlEqual, Formula1:="0"  
       .Range(a).ClearFormats  
       a = .Cells.SpecialCells(xlCellTypeAllFormatConditions).Address  
     End With  
     .Close False  
   End With  
   .Range(a).Select  
 End With  
End Sub  
 
Sub InverseSelection2()  
 Dim a As String  
 a = Selection.Address  
 With ActiveSheet  
   With Worksheets.Add  
     Application.DisplayAlerts = False  
     .Cells.FormatConditions.Add Type:=xlCellValue, Operator:=xlEqual, Formula1:="0"  
     .Range(a).ClearFormats  
     a = .Cells.SpecialCells(xlCellTypeAllFormatConditions).Address  
     .Delete  
     Application.DisplayAlerts = True  
   End With  
   .Range(a).Select  
 End With  
End Sub
 
:)  
вот теперь всё отлично работает.  
Спасибо большое.  
А о применимости - так это просто общий случай частной задачи "Расформатировать всё вне выделенного", которая иногда встаёт необходимости "окультуривания" файлов, получаемых иногда от "знатоков", которые спокойно могут вместо ограниченного диапазона (просто из лени лишних "мышкодвижений") выделить и залить цветом или проставить все границы в целом столбце или строке.  
А при использовании предложенного вами макроса можно просто выделить всё что тебе нужно, запустить макрос и спокойно делать с выделившимся диапазоном всё что угодно, а не только расформатировать как было в частном случае.
С уважением, Алексей (ИМХО: Excel-2003 - THE BEST!!!)
 
Алекс, в который раз перечитываю ветку и не могу понять - зачем выделять (инверсно) весь лист? Ведь за пределами UsedRange ЧИСТО! Что там расформатировать?
 
Я уже отвечал:  
Попробуйте на листе проставить границы ВСЕХ ячеек или дайте заливку ВСЕМ ячейкам листа и увидите, что с использованием UsedRange макрос всё-таки глючит...  
А что там есть добавление временного листа я практически сразу заметил, но только сейчас вернулся к рабочему месту: на совещание выдёргивали...
С уважением, Алексей (ИМХО: Excel-2003 - THE BEST!!!)
 
The_Prist,  
к сожалению ваш пример не учитывает возможности примыкания Selection к последней строке или столбцу (при выделении целиком столбца или строки), поэтому при таких условиях даёт ошибку...  
Я это пытался исправить сам ещё вчера (см. мой пост от 03.03.2010, 17:27 )  
но так и не смог разобраться, как командой Union можно объединить 4 диапазона, любой из которых модет оказаться  = Nothing (как раз если Selection - целиком строка или столбец)?  
НаписАл пока  
  Select Case True  
с 15-ю вариантами выбора диапазонов, которые можно объединять т.к. они не равны Nothing ...  
работает, конечно, но уж больно громоздко.  
А подправить ваш метод объединения с использованием объекта Err мне не по зубам.  
 
А в самом начале макроса, наверное, на всякий случай вставить:  
  If Not TypeName(Selection) = "Range" Then Exit Sub
С уважением, Алексей (ИМХО: Excel-2003 - THE BEST!!!)
 
Ну, раз The_Prist не отвечает, то значит придётся в Personal.xls к себе класть макрос :) с использованием временного листа/книги.
 
Дмитрий, при попытке обратного (повторного) инвертирования процедура виснет. В каком цикле виснет не проверял.
Страницы: 1
Читают тему
Наверх