Почему мой условный формат смещается при добавлении VBA?

Я пытался добавить условные форматы следующим образом:

Если выражение =($G5<>""), то сделать внутреннюю часть набора зелёной, использовать это для $A$5:$H$25.

Попробовал это, сработало нормально, как и ожидалось, затем попытался адаптировать это как код VBA со следующим кодом, который работает, но не так, как ожидалось:

With ActiveSheet.UsedRange.Offset(1)
  .FormatConditions.Delete
  'set used row range to green interior color, if "Erledigt Datum" is not empty
  With .FormatConditions.Add(Type:=xlExpression, _
                             Formula1:="=($" & cstrDefaultProgressColumn & _
                                                      .row & "<>"""")")
        .Interior.ColorIndex = 4
      End With
End With

Проблема в том, что .row предоставляет правильную строку во время отладки, однако моя добавленная условная формула, похоже, не соответствует одной или нескольким строкам - в зависимости от моего решения для установки строки. Итак, я получаю условное форматирование со смещением к строке, которая должна была быть отформатирована.

В диалоге это =($G6<>"") или G3 или G100310 или что-то в этом роде. Но не мой желанный G5.

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

Я подозревал, что у меня With устройство, но оно не решило эту проблему.

редактировать: Чтобы быть более конкретным, это НЕ проблема с UsedRange, с такой же проблемой:

Dim rngData As Range
Set rngData = ActiveSheet.Range("A:H") 'ActiveSheet.UsedRange.Offset(1)

rngData.FormatConditions.Delete

With rngData.FormatConditions.Add(Type:=xlExpression, _
                                  Formula1:="=($" & cstrDefaultProgressColumn & _
                                                  1 & "<>"""")")
    .Interior.ColorIndex = 4
End With

Мои данные выглядят так:

1 -> empty cells
2 -> empty cells
3 -> empty cells
4 -> TitleCols -> A;B;C;...;H
5 -> Data to TitleCols
. .
. .
. .
25

Когда я выполняю этот отредактированный код в Excel 2007 и ищу формулу в условном диалоговом окне, это =($G1048571<>"") - должно быть =($G1<>""), тогда все работает нормально.

Что еще более странно - это отредактированная версия отлично работающего кода, который раньше добавлял условные форматы для каждой строки. Но потом я понял, что можно написать выражение, которое форматирует целую строку или ее часть - думал, что это будет адаптировано за минуту, и теперь это ^^

Изменить: Дополнительная информация о задаче

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

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

Чтобы они были минимальными, я использую NamedRanges для определения правильного смещения и определения правильного DefaultProgessColumn.

GetTitleRow используется для определения строки заголовка по NamedRange или содержимому заголовка.

With ActiveSheet.UsedRange.Offset(GetTitleRow(ActiveSheet.UsedRange) - _
                                ActiveSheet.UsedRange.Rows(1).row + 1)

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

Formula1:="=(" & Cells(.row, _
           Range(strMatchCol1).Column).Address(RowAbsolute:=False) & _
           "<>"""")"

strMatchCol1 - имя диапазона.


person Jook    schedule 14.09.2012    source источник
comment
Кажется, что я должен использовать .row+2, чтобы сделать это правильно, но ПОЧЕМУ?! и почему он исправляет это только на этом конкретном листе?! - Должно быть, я что-то здесь упускаю - есть идеи?! Большое спасибо всем сторонникам!   -  person Jook    schedule 14.09.2012
comment
Будьте осторожны при использовании .UsedRange - см. stackoverflow.com/questions/ 7423022/ и, возможно, некоторые другие потоки SO   -  person JMax    schedule 14.09.2012
comment
Я не думаю, что вы получаете то, что ожидаете от UsedRange. Он не возвращает последнюю строку, как можно было бы ожидать. На что вы пытаетесь ориентироваться?   -  person Omnikrys    schedule 14.09.2012
comment
дело в том, что я не пытаюсь получить последнюю строку. Меня волнует только начало, и когда я проверяю в режиме отладки, UsedRange дает то, что я ожидал. Я хочу использовать его, чтобы мне не нужно было указывать, насколько широки или длинны мои столбцы данных, поэтому я легко адаптируюсь.   -  person Jook    schedule 14.09.2012
comment
@JMax, спасибо за напоминание, проверил, проблема не в этом.   -  person Jook    schedule 14.09.2012
comment
в некотором смысле, я решил это - адаптируя мой код, давайте посмотрим, доберется ли кто-нибудь еще до беспорядка, стоящего за этим :)   -  person Jook    schedule 14.09.2012
comment
Это что-то странное в том, как Excel обрабатывает ссылку на ячейку. Вы знаете, как он автоматически меняет ссылку с ячейки на ячейку, когда вы вставляете формулу? Ну, это делает это, но ссылка далеко. Это происходит после первого запуска. Откройте книгу, настройте тестовую страницу, запустите код... все работает. Сделайте это снова, это немного не удается. Сделайте это снова, и это не удастся еще больше. Собираюсь подобрать его и посмотреть, смогу ли я его отсортировать.   -  person Omnikrys    schedule 14.09.2012


Ответы (3)


Понял, лол. Установите ActiveCell, прежде чем выполнять черновую работу...

ActiveSheet.Range("A1").Activate

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

person Omnikrys    schedule 14.09.2012
comment
поздравляю! вот и все... ты это заслужил. Однако я не очень доволен этим, потому что это все еще похоже на обходной путь, а не на решение. Однако я думаю, что это проблема Excel, пока кто-то не докажет, что это не так. - person Jook; 14.09.2012
comment
Да, именно поэтому мой первоначальный комментарий указывал на какую-то ошибку. По крайней мере, я хочу назвать это неожиданным следствием, почему, черт возьми, он это делает? - person Omnikrys; 14.09.2012
comment
Если вы хотите углубиться в VBA как в программирование, вы заметите, что использование ActiveSheet, ActiveCell, Select и Offset является плохой привычкой по этой и другим причинам. Код VBA редко остается неизменным, и люди постоянно что-то добавляют и меняют. Если вы не называете свои объекты явно (например, set oBook = workbooks.open(smtg)), но называете их активными, это может причинить вам боль. Вместо выбора и смещения лучше определить диапазон (например, установить oRange = ‹'Range def›) и прокрутить этот диапазон. Или сначала загрузите его в массив, а затем выполните цикл. - person html_programmer; 14.09.2012
comment
@KimGysen, вы правы в некоторых частях, и я использую упомянутые методы в других случаях - здесь, однако, это кажется не оптимальным. Если вы думаете иначе, я был бы рад увидеть другое решение этой проблемы. Я тоже с вами на select (поэтому мне не нравится текущее решение), но ActiveSheet, ActiveCell и Offset обладают своими хорошими способностями и полезны во многих случаях, например, в этом, т. е. не то, чтобы select не существовало, просто им часто злоупотребляют, чтобы сделать неподходящим задачи - например, Excel часто используют как базу данных. - person Jook; 14.09.2012
comment
Ответ на ваш вопрос пока будет работать, поэтому я не буду его разбивать, если он будет полезен. Однако (и это просто примечание), если есть обычный конечный пользователь (не знающий VBA), который вставляет несколько строк сверху, чтобы добавить, скажем, дату или другую случайную информацию (я испытал, что некоторые неосведомленные конечные пользователи просто не t знаю, что они не должны), код больше не будет работать. Чтобы решить эту проблему, было бы, например, хорошей практикой сделать диапазон, определяемый именованным диапазоном; через VBA вы можете динамически расширять этот диапазон, когда это необходимо. Если затем кто-то вставит строку, то --› - person html_programmer; 14.09.2012
comment
диапазон останется нетронутым (будь то вставка строки или столбца). По моему опыту, рекомендуется всегда делать код как можно более динамичным; в случае с Excel это означает посмотреть на архитектуру с высоты птичьего полета и увидеть в уме, как могут сдвигаться диапазоны с целью устранения основных возможных ошибок. Лично я бы через условие формата со счетчиком. - person html_programmer; 14.09.2012
comment
Вкратце, я имею в виду, что выполнение кода никогда не должно зависеть от того, какой лист или ячейка вообще активны. Я напишу некоторый код того, что я имею в виду. Конечно, решение полностью за вами. - person html_programmer; 14.09.2012

Причина, по которой условное форматирование и проверка данных демонстрируют такое странное поведение, заключается в том, что используемые ими формулы находятся за пределами обычной цепочки вычислений. Они должны быть такими, чтобы можно было ссылаться на активную ячейку в формуле. Если вы находитесь в G1, вы не можете ввести =G1="", потому что вы создадите циклическую ссылку. Но в CF или DV вы можете ввести эту формулу. Эти формулы не связаны с текущей ячейкой, в отличие от реальных формул.

Когда вы вводите формулу CF, она всегда относится к активной ячейке. Если в CF сделать формулу

=ISBLANK($G2)

и вы находитесь в формате A5, Excel преобразует его в

=ISBLANK(R[-3]C7)

и когда это помещается в CF, оно оказывается относительно ячейки, к которой оно применяется. Таким образом, в строке 2 формула выходит

=ISBLANK($G655536)

(для Excel 2003). Он смещает -3 строки и переносится в нижнюю часть электронной таблицы.

Вы можете использовать Application.ConvertFormula, чтобы сделать формулу относительно какой-либо другой ячейки. Если я нахожусь в строке 5, а начало моего диапазона находится в строке 2, я делаю формулу относительно строки 8. Таким образом, R[-3] поместит формулу в A5 как $G5 (три строки вверх от A8). ).

Sub test()

    Dim cstrDefaultProgressColumn As String
    Dim sFormula As String

    cstrDefaultProgressColumn = "$G"

    With ActiveSheet.UsedRange.Offset(1)
        .FormatConditions.Delete
        'set used row range to green interior color, if "Erledigt Datum" is not empty

        'Build formula
        sFormula = "=ISBLANK(" & cstrDefaultProgressColumn & .Row & ")"

        'convert to r1c1
        sFormula = Application.ConvertFormula(sFormula, xlA1, xlR1C1)

        'convert to a1 and make relative
        sFormula = Application.ConvertFormula(sFormula, xlR1C1, xlA1, , ActiveCell.Offset(ActiveCell.Row - .Cells(1).Row))

        With .FormatConditions.Add(Type:=xlExpression, _
                                 Formula1:=sFormula)

            .Interior.ColorIndex = 4
        End With

    End With

End Sub

Я смещаю только .Cells(1) по строкам, потому что в этом примере столбец является абсолютным. Если и строка, и столбец в вашей формуле CF являются относительными, вам нужно больше смещения. Кроме того, это работает только в том случае, если активная ячейка находится ниже первой ячейки в вашем диапазоне. Чтобы сделать его более универсальным, вам нужно будет определить, где активная ячейка находится относительно диапазона, и соответствующим образом сместиться. Если смещение поместило вас выше строки 1, вам нужно будет закодировать его так, чтобы оно относилось к ячейке ближе к нижней части общего количества строк для вашей версии Excel.

Если вы думали, что выбор был чем-то вроде кладжа, я уверен, вы согласитесь, что это еще хуже. Несмотря на то, что я ненавижу ненужный выбор и активацию, условное форматирование и проверка данных — это два места, где это неизбежное зло.

person Dick Kusleika    schedule 14.09.2012

Краткий пример:

Sub Format_Range()

Dim oRange          As Range
Dim iRange_Rows     As Integer
Dim iCnt            As Integer


'First, create a named range manually in Excel (eg. "FORMAT_RANGE")
'In your case that would be range "$A$5:$H$25". 
'You only need to do this once, 
'through VBA you can afterwards dynamically adapt size + location at any time. 

'If you don't feel comfortable with that, you can create headers 
'and look for the headers dynamically in the sheet to retrieve 
'their position dynamically too. 

'Setting this range makes it independent
'from which sheet in the workbook is active
'No unnecessary .Activate is needed and certainly no hard coded "A1" cell. 
'(which makes it more potentially subject to bugs later on) 
Set oRange = ThisWorkbook.Names("FORMAT_RANGE").RefersToRange
iRange_Rows = oRange.Rows.Count

For iCnt = 1 To iRange_Rows
    If oRange(iCnt, 1) <> oRange(iCnt, 2) Then
        oRange(iCnt, 2).Interior.ColorIndex = 4
    End If
Next iCnt

End Sub

Что касается моих комментариев к другому ответу:

Если вам нужно сделать это для многих строк, то определенно быстрее загрузить весь диапазон в память (массив) и проверить условия внутри массива, после чего вы делаете запись в те ячейки, которые нужно записать (отформатированные ).
Я мог бы согласиться с тем, что этот метод не является «необходимым» в этом случае, однако это хорошая практика, потому что он гибок для многих (любых типов) настроек впоследствии и его легче отлаживать (используя непосредственные / локальные / часы window).
Я не фанат Offset, хотя и не утверждаю, что он не работает должным образом, и в некоторых ограниченных сценариях я могу сказать, что вероятность возникновения проблем «могла» быть небольшой: я испытал это на себе. некоторые бизнес-пользователи склонны использовать его постоянно (здесь смещение +3, там смещение -3, потом снова -2 и т. д.); хотя это легко писать, я могу сказать вам, что это ад пересматривать. Он также очень часто подвержен ошибкам, когда изменения вносятся конечными пользователями.
Я категорически «за» использование заголовков (хотя я также являюсь сторонником сокращения возможностей базы данных для Excel, потому что для многих это приводит к в избегании доступа), потому что это позволит вам очень гибко. Даже когда я использовал столбцы 1 и 2; лучше получить номер столбца динамически на основе расположения именованного диапазона заголовка. Если затем вставить еще один столбец, никаких ошибок не появится.

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

person html_programmer    schedule 14.09.2012
comment
Будьте осторожны, используя Integer для подсчета строк. Вместо этого рассмотрите возможность использования Long, так как количество строк может превышать количество, которое может содержать Integer. - person Dick Kusleika; 14.09.2012
comment
Согласен, в Access я тоже использую Longs. В Excel тоже, если я знаю, будет много записей. - person html_programmer; 14.09.2012
comment
@DickKusleika - спасибо за отличное объяснение в вашем ответе! KimGysen, спасибо за демонстрацию этой техники, но это решение не подходит для моей проблемы. Как уже говорилось, я также использую NamedRange и ArrayCopies, но задача здесь требует другого подхода, хотя сейчас я использую NamedRange в своей ConditionalFormula. Я добавлю объяснение этого в свой вопрос. Тем не менее, ваша конструкция цикла является интересной альтернативой моему методу LBOUND/UBOUND, спасибо за это! - person Jook; 17.09.2012