4

人生の使命がアクティブなセル(つまり選択)を取得し、隣接するセルをある値に設定することであるサブ(関数ではない)を作成するとします。これは正常に機能します。

そのSubを関数に変換し、スプレッドシートから評価しようとすると(つまり、数式を「= MyFunction()」に設定すると)、Excelは、非アクティブの値に影響を与えようとしているという事実に吠えます。セルを選択し、隣接するセルに触れずに関数に#VALUEを返すように強制します。

この保護動作をオフにすることは可能ですか?そうでない場合、それを回避するための良い方法は何ですか?可能であれば、有能な開発者が1〜2週間で達成できることを探しています。

よろしく、アラン。

注:私は2002を使用しているので、そのバージョンで機能するソリューションを優先します。そうは言っても、将来のバージョンでこれが大幅に簡単になるのであれば、私もそれについて知りたいと思います。

4

6 に答える 6

9

次の理由により、これは理にかなっています。

  • ワークシート関数が呼び出されると、関数を含むセルがアクティブ セルであるとは限りません。したがって、隣接するセルを確実に見つけることはできません。

  • Excel がワークシートを再計算するとき、セル間の依存関係を維持する必要があります。したがって、ワークシート関数が他のセルを任意に変更することはできません。

あなたができる最善のことは、次のいずれかです。

  • SheetChange イベントを処理します。関数を含むセルが変更されている場合は、隣接するセルを変更します。

  • 隣接するセルにワークシート関数を配置して、必要な値を返します。

アップデート

コメントについて: 「この関数を '空白の' スプレッドシートで動作させたいので、まだ存在しない可能性のあるスプレッドシートの SelectionChange イベントに実際に依存することはできませんが、この関数を呼び出す必要があります」:

  • 関数を XLA アドインに入れることはできますか? 次に、XLA アドインは、Excel のそのインスタンスで開かれているすべてのブックの Application SheetChange (*) イベントを処理できますか?

コメントについて:「それでも、Excel を CalculationMode = xlManual のままにして、値だけを入力すれば問題ないはずです」

  • CalculationMode が xlManual の場合でも、Excel は正しい順序で計算できるように、セル間の参照の依存関係ツリーを維持する必要があります。また、関数の 1 つが任意のセルを更新できる場合、これは順序を台無しにします。おそらくこれが、Excel がこの制限を課す理由です。

(*) 最初に上で SelectionChange を書きましたが、現在は修正されています。もちろん、正しいイベントは Workbook または Application オブジェクトの SheetChange、または Worksheet オブジェクトの Change です。

更新 2タイマーを使用して「ちょっと」機能させる方法を説明するAlanR の投稿 に関するいくつかの発言:

  • タイマー関数 ("Woohoo") が更新するセルをどのように認識するかは明確ではありません。タイマーをトリガーした数式がどのセルに含まれているかを示す情報はありません。

  • 数式が複数のセル (同じまたは異なるブック内) に存在する場合、再計算中に UDF が複数回呼び出され、timerId が上書きされます。その結果、タイマーを確実に破棄できず、Windows リソースがリークします。

于 2008-10-14T16:42:33.933 に答える
2

カスタムユーザー定義のExcel関数を作成する方法によると:

UDF の制限

  • 数式を含むセル (または範囲) 以外のセルに値を配置することはできません。つまり、UDF は「式」として使用されることを意図しており、必ずしも「マクロ」ではありません。

ということで、出来ないようです。

于 2008-10-14T16:09:01.863 に答える
2

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
于 2008-10-14T15:05:41.633 に答える
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
于 2008-10-16T13:37:02.747 に答える
1

動作する簡単な VBA の回避策を次に示します。この例では、新しい Excel ワークブックを開き、次のコードをSheet1(notThisWorkbookまたは VBA Module) のコード領域にコピーします。次にSheet1、ワークシートの左上のセルの 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

サブルーチンは、シート内のすべてのセル変更イベントをキャプチャします。行と列が両方とも <= 10 で、値が数値の場合、右側のセルは変更されたセルの 4 倍に設定されます。それ以外の場合、右側のセルはクリアされます。

于 2009-01-06T22:51:40.640 に答える
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

余分な問題があります: セルを渡す関数は、セルの値を渡すだけなので、回避するために、分母に 1 セルのセル範囲を渡す必要があります。

Excel に適合しないスプレッドシートで通常とは異なることをしようとしている場合、または Python の機能を使用してスプレッドシート データを操作することに興味がある場合は、Resolver One を検討する価値があります。

于 2008-11-27T17:07:50.727 に答える