VBA Назначить значение ячейке внутри функции? - PullRequest
2 голосов
/ 20 февраля 2010

Можно ли в любом случае назначить значение / имя ячейке в функции?

Спасибо

EDIT

Извините за неясность, вот мое требование.

У меня есть пользовательская функция (= MyFunction ()), которую можно вызывать из таблицы Excel. Таким образом, у меня также есть кнопка меню, где мне нужно вызывать все вызовы функций для = MyFunction (), когда пользователь нажимает кнопку.

Мой план состоит в том, чтобы внутри MyFunction () назначить ссылку на имя для вызывающей ячейки и сохранить ее внутри vba. Так что я мог бы иметь массив имен ячеек. Затем я могу вспомнить эти ссылки на ячейки при нажатии кнопки меню.

Пожалуйста, помогите мне достичь этого. Есть ли лучший способ сохранить ссылки на ячейки?

Ответы [ 2 ]

1 голос
/ 20 февраля 2010

РЕДАКТИРОВАНИЕ:

Ах, теперь я вижу. Самый простой способ сделать это - создать фиктивный аргумент: MyFunction(ByVal r As Variant), и всякий раз, когда вы используете эту функцию на листе, укажите точно такую ​​же ячейку, что и аргумент: =MyFunction(A1). Когда щелкнет пункт меню, измените значение в A1 на любое другое, и все MyFunctions будут пересчитаны.

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

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

Option Explicit

Private RefsToCalculate As New Collection

Public Function MyFunction() As Long
  Static i As Long

  i = i + 1
  MyFunction = i

  If TypeOf Application.Caller Is Excel.Range Then
    On Error Resume Next
    RefsToCalculate.Add Application.Caller, Application.Caller.Address
    On Error GoTo 0
  End If
End Function

Public Sub MenuButtonClicked()
  Dim i As Long

  For i = 1 To RefsToCalculate.Count
    RefsToCalculate(i).Dirty
  Next
End Sub
0 голосов
/ 22 февраля 2010

"У меня есть пользовательская функция (= MyFunction ()), которую можно вызывать из листа Excel. Таким образом, у меня также есть кнопка меню, где мне нужно вызвать все функции, вызываемые для = MyFunction () , когда пользователь нажимает кнопку. "

Это легко сделать без создания кэша для хранения диапазона ячеек. Тем не менее, вы должны быть осторожны с методом расчета. Я полагаю, что приведенный ниже код гарантирует, что ваш диапазон всегда будет рассчитываться, но: (1) Если метод calc не является ручным, то Excel в конечном итоге контролирует то, что рассчитывается, когда и почему, поэтому он может пересчитать и другие ячейки. (2) Опять же, я верю, что это гарантирует пересчет всех ячеек с вашей функцией независимо от метода calc, но я не проверял приведенный ниже код для таблиц и полуавтоматического метода вычисления.

Код ниже предлагает два подхода:

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

(2) - Создайте интересующий диапазон и пересчитайте этот диапазон: недостатком является то, что построение этого диапазона может потребовать серьезных вычислительных усилий. Преимущество состоит в том, что если для метода calc задано ручное управление, я ВЕРИМ, что Excel будет только пересчитывать ячейку в этом диапазоне.

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

"Мой план состоит в том, чтобы внутри MyFunction () назначить ссылку на имя для вызывающей ячейки и сохранить ее в vba. Поэтому я мог бы иметь массив имен ячеек. Тогда я могу вызвать эти ссылки на ячейки, когда кнопка меню щелкнул ".

Если вы действительно хотите следовать этому подходу или если вам определенно необходимо создать кэш ячеек для цели, которую вы описываете, то это можно сделать и, хотя и элементарно, его можно даже построить таким образом, чтобы оно сохраняется между сессиями Excel. Тем не менее, это требует дополнительной работы, более продвинутого подхода, и это все еще будет довольно элементарно. ИМО, явное излишество для этой проблемы. Что еще хуже, вам придется вызывать код каждый раз при обновлении ячейки, чтобы гарантировать, что кэш обновляется, что может существенно повлиять на производительность. Что касается предложения GSerg: этот подход - как он сам упоминает - не дает вам никакого реального контроля над жизнью самого кэша. Это означает, что каждый раз, когда вы достигаете кеша, вам нужно будет проверить, уничтожил ли его Excel и, если это так, перестроить его.

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

ЗАМЕЧАНИЕ: реализовано и протестировано в Excel 2003. Символы комментариев в стиле C # включены для форматирования.

Option Explicit

Public Sub ReEvaluateMyFunction()

    On Error GoTo Handle_Exception

    Dim targetCells As Range
    Dim targetCell As Range
    Dim rangeToRecalc As Range

    /*'Workbook and worksheet names
    'hard-coded for the example*/

    Set targetCells = Application _
    .Workbooks("Book1") _
    .Worksheets("Sheet1").UsedRange _
    .SpecialCells(xlCellTypeFormulas)

    If targetCells Is Nothing Then Exit Sub

    /*'You can narrow down the range if you know
    'more about the function's return type, e.g.:

    '.SpecialCells(xlCellTypeFormulas, xlNumbers)
    '.SpecialCells(xlCellTypeFormulas, xlTextValues)

    'OPTION 1: re-calculate all cells in the range

    'Remark: unless calc method is set to "Manual", which
    'should give you full control, I think there's no
    'guarantee that other cells will not be recalculated*/

    If Application.Calculation = xlCalculationManual Then
        //'Use to force recalculation if calc mode is manual
        targetCells.Calculate
    Else
        //'Use this to force recalculation in other cases
        targetCells.Dirty
    End If

    Set targetCells = Nothing
    Exit Sub

    /*'OPTION 2: create a range specific to your
    'function and recalculate that range*/

    For Each targetCell In targetCells

        If targetCell.Formula = "=MyFunction()" Then

            If rangeToRecalc Is Nothing Then
                Set rangeToRecalc = targetCell
            Else
                Set rangeToRecalc = Union(rangeToRecalc, targetCell)
            End If

        End If

    Next targetCell

    //'Same comments as before
    If Application.Calculation = xlCalculationManual Then
        rangeToRecalc.Calculate
    Else
        rangeToRecalc.Dirty
    End If

    Set rangeToRecalc = Nothing
    Set targetCell = Nothing
    Set targetCells = Nothing

    Exit Sub

Handle_Exception:

    Set rangeToRecalc = Nothing
    Set targetCell = Nothing
    Set targetCells = Nothing

    MsgBox "An error has been found: " + Err.Description, vbCritical

End Sub

Public Function MyFunction() As String

    MyFunction = Application.Caller.Address

End Function
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...