2008-10-14 7 views
4

Предположим, что я создаю Sub (не функцию), чья миссия в жизни состоит в том, чтобы взять активную ячейку (т. Е. Выбор) и установить смежную ячейку для некоторого значения. Это прекрасно работает.Выполнение функций Excel влияет на «другие» ячейки

При попытке преобразовать этот Sub в функцию и попытаться оценить его из таблицы (т. Е. Установить его формулу в «= MyFunction()») Excel будет лаять на то, что вы пытаетесь повлиять на значение неактивную ячейку и просто заставляют функцию возвращать #VALUE, не касаясь соседней ячейки.

Можно ли отключить это защитное поведение? Если нет, то, что это хороший способ обойти это? Я ищу что-то, что компетентный разработчик мог бы выполнить за 1-2 недели, если это возможно.

С уважением, Алан.

Примечание: Я использую 2002, поэтому я бы предпочел решение, которое будет работать для этой версии. Сказав это, если будущие версии значительно облегчат это, я хотел бы узнать об этом.

+1

Вы можете попробовать [этот пример] (http://stackoverflow.com/a/23232311/2165759), в котором показано, как использовать UDF без ограничений. – omegastripes 2014-04-23 11:28:45

ответ

9

Это не может быть сделано, что имеет смысл, потому что:

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

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

Лучшее, что вы можете сделать, это один из:

  • Обрабатывать событие SheetChange. Если ячейка, содержащая вашу функцию, изменяется, измените соседнюю ячейку.

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

Update

Что касается комментария: «Я хотел бы эту функцию, чтобы работать на„пустой“таблицы, так что я не могу полагаться на SelectionChange случае таблиц, которые не могут но существует, но нужно будет назвать эту функцию »:

  • Можете ли вы поместить свою функцию в надстройку XLA? Затем ваша надстройка XLA может обрабатывать событие Application SheetChange (*) для всех книг, которые открываются в этом экземпляре Excel?

Что касается комментария: «Тем не менее, если вы держите Excel в CalculationMode = xlManual и заполнить только значения, вы должны быть очень хорошо»

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

(*) Я изначально написал SelectionChange выше, теперь исправлено - конечно, правильное событие - это SheetChange для объектов Workbook или Application или Change для объекта Worksheet.

Update 2 Некоторые замечания AlanR's post описывающие, как «своего рода» заставить его работать с помощью таймера:

  • Это не ясно, как функция таймера («Woohoo») будет знать, какие клетки в Обновить. У вас нет информации о том, какая ячейка содержит формулу, вызвавшую таймер.

  • Если формула существует в более чем одной ячейке (в тех же или разных книгах), тогда UDF будет вызываться несколько раз во время перерасчета, переписывая timerId. В результате вам не удастся тайно уничтожить таймер и утечка ресурсов Windows.

+0

Я бы хотел, чтобы эта функция работала над «пустой» таблицей, поэтому я не могу положиться на событие SelectionChange, которое может еще не существовать, но ему нужно будет вызвать эту функцию. – AlanR 2008-10-14 16:50:30

+0

Я вижу, что вы говорите о том, как перерасчет таблицы может привести к плохим проблемам, если вы попытаетесь поместить формулы в смежные ячейки, вызывая циклические ссылки и т. Д. Тем не менее, если вы сохраните Excel в CalculationMode = xlManual и заполните только значения, вы должны быть в порядке. – AlanR 2008-10-14 16:59:09

2

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

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

This article также содержит много информации о том, как Excel выполняет перерасчет. Но он никогда не заявляет, что другие ячейки заморожены.

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

Пример:

Public Function Bar(r As Range) As Integer 
    If r.Value = 2 Then 
    Bar = 0 
    Else 
    Bar = 128 
    End If 
End Function 
+0

1) Это выполнимо. Я видел надстройки, которые это делают, например надстройка Bloomberg. 2) Я пытаюсь установить значение смежной ячейки программно. – AlanR 2008-10-14 15:26:21

2

В соответствии с How to Create Custom User Defined Excel Functions:

Ограничения ОДС

  • Не может поместить значение в ячейке, кроме ячейки (или диапазона), содержащей в формула. Другими словами, UDF - это , предназначенные для использования как «формулы», а не обязательно «макросы».

Таким образом, похоже, что это невозможно.

1

Спасибо всем за ответ. Это можно сделать! Вроде. Я говорю «любопытно», потому что технически говоря «функция» не влияет на ячейки вокруг него. Практически, однако, ни один пользователь не мог сказать разницы.

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

Теперь я не эксперт в том, как работает COM-потоки (хотя я знаю, что VBA - это одиночная квартира с резьбой), но будьте осторожны, когда ваш таймер убегает с процессом Excel и сбрасывает его.Это действительно не то, что я предлагаю в качестве решения для всех других электронных таблиц.

просто сделать модуль с этим содержимым:

Option Explicit 

Declare Function SetTimer Lib "user32" (ByVal HWnd As Long, _ 
    ByVal IDEvent As Long, ByVal mSec As Long, _ 
    ByVal CallFunc As Long) As Long 

Declare Function KillTimer Lib "user32" (ByVal HWnd As Long, _ 
    ByVal timerId As Long) As Long 

Private timerId As Long 

Private wb As Workbook 
Private rangeName As String 
Private blnFinished As Boolean 

Public Sub RunTimer() 

    timerId = SetTimer(0, 0, 10, AddressOf Woohoo) 


End Sub 


Public Sub Woohoo() 

    Dim i As Integer 

' For i = 0 To ThisWorkbook.Names.Count - 1 
'  ThisWorkbook.Names(i).Delete 
' Next 

    ThisWorkbook.Worksheets("Sheet1").Range("D8").Value = "Woohoo" 

    KillTimer 0, timerId 

End Sub 
1

В то время как вы не можете сделать это в Excel, можно в Resolver One (хотя это по-прежнему довольно странно делать).

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

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

def safeDivide(numerator, cellRange): 
    if not isinstance(cellRange, CellRange): 
     raise ValueError('denominator must be a cell range') 
    denominator = cellRange.Value 
    if denominator == 0: 
     cell = cellRange.TopLeft 
     cell.BackColor = Color.Red 
     cell.Offset(1, 0).Value = 'Tried to divide by zero' 
     return 0 
    return numerator/denominator 

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

Если вы пытаетесь делать необычные вещи с таблицами, которые не совсем вписываются в Excel, или вы заинтересованы в использовании возможностей Python для работы с данными ваших электронных таблиц, стоит взглянуть на Resolver One ,

1

Вот простой способ обхода VBA, который работает. В этом примере откройте новую книгу Excel и скопируйте следующий код в область кода для Sheet1 (не ThisWorkbook или VBA Module). Затем перейдите в Sheet1 и поместите что-то в одну из верхних левых ячеек рабочего листа. Если вы наберете номер и нажмите Enter, ячейка справа будет обновлена ​​с 4-кратным номером, а фон ячейки станет светло-голубым. Любое другое значение заставляет следующую ячейку очищаться. Вот код:

Dim busy As Boolean 
Private Sub Worksheet_Change(ByVal Target As Range) 
    If busy Then Exit Sub 
    busy = True 
    If Target.Row <= 10 And Target.Column <= 10 Then 
    With Target.Offset(0, 1) 
     If IsNumeric(Target) Then 
     .Value = Target * 4 
     .Interior.Color = RGB(212, 212, 255) 
     Else 
     .Value = Empty 
     .Interior.ColorIndex = xlColorIndexNone 
     End If 
    End With 
    End If 
    busy = False 
End Sub 

Подпрограмма захватывает все события изменения ячейки на листе. Если строка и столбец равны < = 10, то ячейка справа устанавливается в 4 раза измененной ячейке, если значение является числовым; в противном случае ячейка справа очищается.