Excel関数を「他の」セルに影響させる
質問
アクティブなセル(つまり選択)を取得し、隣接するセルを何らかの値に設定することが人生の使命であるSub(関数ではない)を作成するとしましょう。これは正常に機能します。
そのSubを関数に変換し、スプレッドシートから評価しようとすると(つまり、その数式を<!> quot; = MyFunction()<!> quot;に設定しようとすると)、Excelは、非アクティブなセルの値に影響を与えようとしており、隣接するセルに触れずに関数に#VALUEを強制的に返します。
この保護動作をオフにすることは可能ですか?そうでない場合、それを回避する良い方法は何ですか?可能であれば、有能な開発者が1〜2週間で達成できることを探しています。
よろしく、 アラン。
注:2002年を使用しているため、そのバージョンで機能するソリューションを優先します。そうは言っても、将来のバージョンでこれが大幅に簡単になったら、それについても知りたいです。
解決
できません。理由は次のとおりです。
-
ワークシート関数が呼び出されるとき、関数を含むセルは必ずしもアクティブなセルではありません。そのため、隣接するセルを確実に見つけることができません。
-
Excelがワークシートを再計算するとき、セル間の依存関係を維持する必要があります。したがって、ワークシート関数で他のセルを任意に変更することはできません。
できる最善のことは、次のいずれかです。
-
SheetChangeイベントを処理します。関数を含むセルが変更されている場合、隣接するセルを変更します。
-
隣接するセルにワークシート関数を挿入して、必要な値を返します。
更新
コメントについて:<!> quot;この関数を「空白」のスプレッドシートで動作させたいので、まだ存在しないかもしれないスプレッドシートのSelectionChangeイベントに本当に頼ることはできませんが、この関数を呼び出す<!> quot;:
- XLAアドインに関数を配置できますか? XLAアドインは、Excelのそのインスタンスで開かれているすべてのワークブックのApplication SheetChange(*)イベントを処理できますか?
コメントについて:<!> quot;それでも、ExcelをCalculationMode = xlManualに保ち、値だけを入力する場合は、問題ないはずです<!> quot;
- CalculationModeがxlManualの場合でも、Excelはセル間の参照の依存関係ツリーを維持して、正しい順序で計算できるようにする必要があります。そして、関数の1つが任意のセルを更新できる場合、これは順序を台無しにします。これがおそらくExcelがこの制限を課している理由です。
(*)最初に上記のSelectionChangeを書きましたが、今修正しました-もちろん正しいイベントはWorkbookまたはApplicationオブジェクトのSheetChange、またはWorksheetオブジェクトのChangeです。
更新2 AlanRの投稿でいくつかの発言があります。タイマーを使用する:
-
タイマー関数(<!> quot; Woohoo <!> quot;)がどのセルを更新するかをどのように知るかは明確ではありません。どのセルにタイマーをトリガーした数式が含まれているかを示す情報がありません。
-
数式が複数のセル(同じまたは異なるワークブック)に存在する場合、UDFは再計算中に複数回呼び出され、timerIdを上書きします。その結果、タイマーを確実に破棄できず、Windowsリソースがリークします。
他のヒント
UDFの制限
- 次を含むセル(または範囲)以外のセルに値を配置できません 式。言い換えれば、UDFは <!> quot; formulas <!> quot;として使用するためのものであり、 必ず<!> quot; macros <!> quot;。
つまり、できないように見えます。
Excel 2007を使用していますが、機能しません。 Excelは、循環参照を作成すると述べています。関数から他のセルを変更できるとは思わず、値を返すだけです。
これは一種の関数型プログラミングであり、副作用はありません。関数内の他のセル(ワークシートから使用)を変更するだけでは、セルが変更された場合にExcelで順序と再計算対象を知る方法はありません。
この記事には多くの情報も含まれていますExcelが再計算を行う方法に関する情報。しかし、他のセルが凍結しているとは決して言いません。
あなたが何をしようとしているのかわかりませんが、最初のセルをパラメータとして使用する隣接セルに別の関数を配置しないのはなぜですか?
例:
Public Function Bar(r As Range) As Integer
If r.Value = 2 Then
Bar = 0
Else
Bar = 128
End If
End Function
ご回答いただきありがとうございます。これを行うことは可能です!ちょっと。技術的に言えば「機能」は周囲の細胞に影響を与えないため、「ちょっと」と言います。ただし、実際には、ユーザーはその違いを見分けることができませんでした。
秘trickは、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
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
追加のしわがあります:渡されたセルを取得する関数はセルの値を渡されるだけなので、それを回避するために、分母に1セルのセル範囲が渡されるようにします。
Excelに完全に適合しないスプレッドシートで異常なことをしようとしている場合、またはPythonのパワーを使用してスプレッドシートデータを操作することに興味がある場合は、Resolver Oneをご覧ください。
これは、動作する簡単なVBAの回避策です。この例では、新しいExcelブックを開き、次のコードをSheet1
のコード領域にコピーします(ThisWorkbook
またはVBA Module
ではありません)。次に<=>に移動して、ワークシートの左上のセルの1つに何かを入力します。数字を入力して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
サブルーチンは、シート内のすべてのセル変更イベントをキャプチャします。行と列が両方とも<!> lt; = 10の場合、値が数値の場合、右側のセルは変更されたセルの4倍に設定されます。そうでない場合、右側のセルはクリアされます。