私はMSOfficeアプリケーション(自動化およびETLプロセス用)のVBAで数年の経験がありますが、最近までMSAccessのフォームをいじる必要はありませんでした。私が設計したデータベースのいくつかの単純なデータ抽出フォームの設計をレイアウトしていて、単純な作業のように思われることに夢中になっています。
目的:メインフォームのコントロールから動的に構築されたSQLステートメントから返されたレコードを表示するためのデータシートサブフォームが必要です。
メインフォームには、ユーザーがクリックすると、他のユーザーフォームコントロールでユーザーが指定した情報をSQLクエリにコンパイルし、そのクエリを実行してサブフォームに結果のレコードを表示するボタンがあります。
私が何をしても、これを機能させることはできません。 (とにかくほとんどの場合)Microsoft Visual Basicランタイムエラー「 '2467':入力した式が閉じているか存在しないオブジェクトを参照しています。」が表示され続けます。これは、以下に示すコードで発生するエラーです。コードが実行されたらすぐにサブフォームを開始する必要があるかどうか、または何をする必要があるのかわかりません。他のコードフォーラムでも機能しない他のコードのバリエーションをいくつか試しましたが、以下のコードが機能することを示唆するStackOverflowのスレッドを含むいくつかのフォーラムスレッドを見つけたようです。
添付の画像は、基本的なメインフォームがどのように見えるかを示しています。ユーザーがクリックして(btnDisplaySWData)、まだ含まれていないコントロールから作成されるSQLをコンパイルするボタンにラベルを付けましたが、それは問題ではありません。この問題を理解しようとして、コードスニペットに示されているようにSQLステートメントをハードコーディングしています。前述のように、dataDisplaySubformという名前のサブフォームにレコードを表示したいと思います。 「JUNK」はAccessデータベース内のテーブルであり、これを理解するまで、テスト目的で使用している以下のSQLコードを使用して合法的にクエリを実行できます。示されているデータフォーム(frmDataExtractという名前)のすべてのコードは、以下のコードウィンドウにあるもので構成されています。
Option Compare Database
Option Explicit
Public Sub btnDisplaySWData_Click()
Dim pSQL As String
pSQL = "SELECT JUNK.agency_ID, JUNK.agency_desc FROM JUNK"
Me.dataDisplaySubform.Form.RecordSource = pSQL
End Sub
以下のサブフォームが選択されたプロパティのスクリーンショットに示すように、フォームの名前はdataDisplaySubformです。
これは全体的なフォームレイアウトがどのように見えるかです
私はいくつかのフォーラムサイトを精査し、Stack Overflowを検索してさまざまな用語を試し、問題の潜在的な解決策を見つけましたが、元のスレッドが投稿者によって解決済みとマークされていても機能しませんでした。私は自分が間違っていることを理解しようとして、約2営業日という非常に多くの時間を費やしましたが、まだできていません。
私を正しい方向に導くのを手伝ってくれる人に感謝します、これは私を怒らせています。
ありがとう、-TB
TURKISHGOLDによるソリューション編集
ハンスアップは、サブフォームのソースオブジェクトに何も割り当てられていないことに言及して、私を道に導くのに役立ちましたが、私は自分でそれを理解したと思います。私の場合、ソースオブジェクトをフォームに割り当てることは、HansUpが提案していた正しい解決策ではありませんでした。代わりに、保存されたクエリは、私が望むことを実行するように思われます。
これを行うためのより良い方法があるかどうかはわかりませんが、VBAでサブフォームのソースオブジェクトを設定できるように、ダミーのほぼプレースホルダークエリを設定する必要があるようです。次のようなプレースホルダークエリ:
SELECT * FROM JUNK WHERE JUNK.agency_ID ="_";
上記のAccessクエリは「TESTQUERY」という名前で保存されます。何も表示されませんが、ソースオブジェクトを何かに割り当てる必要性を満たし、フォームビューでメインフォームを表示するときにサブフォームを本質的にインスタンス化します。したがって、プレースホルダーに保存されたクエリを使用して、次のように、メインフォームのユーザーインターフェイスコントロールを介してまとめられたSQL文字列にRecordSourceを再割り当てできます。
Public Sub btnDisplaySWData_Click()
Dim pSQL As String
pSQL = "SELECT JUNK.agency_ID, JUNK.agency_desc FROM JUNK"
Me.dataDisplaySubform.SourceObject = "Query.TESTQUERY"
Me.dataDisplaySubform.Form.RecordSource = pSQL
Me.dataDisplaySubform.Requery
End Sub
フォームが本番環境にある場合、pSQL文字列変数に格納されているハードコードされたSQLステートメントは、メインフォームのコントロールに対するユーザー入力を介してまとめられます。
したがって、btnDisplaySWDataをクリックすると、私がやろうとしていたことを実行し、レコードを表示します。
"オブジェクトが閉じているか存在しない"エラーがMe.dataDisplaySubform.Form.RecordSource
行で発生した場合、サブフォームcontrolに名前が付けられていない可能性がありますdataDisplaySubform。
この一時的なコードの変更により、フォームのすべてのサブフォームコントロールの名前を調べることができます...
'Me.dataDisplaySubform.Form.RecordSource = pSQL
Dim ctl As Control
For Each ctl In Me.Controls
If TypeName(ctl) = "SubForm" Then
Debug.Print ctl.Name, TypeName(ctl)
End If
Next
Stop
Stop
ステートメントはデバッグ(ブレーク)モードをトリガーし、フォームのサブフォームコントロールの名前を表示できるイミディエイトウィンドウに移動します。
質問に追加したスクリーンショットは、サブフォームコントロールに正しい名前を使用していることを確認しています。ただし、そのサブフォームのSource Objectプロパティには何もありません。そこにはフォームがないため、エラーメッセージの2番目の部分"does n't present"が適用されます。 Me.dataDisplaySubform.Form
が参照するフォームはありません
短くて甘い。これは、動的SQL文字列を作成し、現在のオブジェクトを閉じ(開いている場合に備えて)、一時的なクエリ定義を削除し(必要なため)、新しいSQLで新しいクエリ定義を作成し、レコードソースを変更するボタンのコードです。ボブはあなたのおじです。
Private Sub btnRunSQL_Click()
'my subform is called datasheet, i know -- dumb name.
'a dynamic sql needs to be saved in a temporoary query. I called my qtemp
Dim sql As String
sql = "select * from client order by casename asc"
'in case there is something kicking around, remove it first, otherwise we can't delete the temp query if it is still open
Me!Datasheet.SourceObject = ""
'delete our temporary query. Note, add some err checking in case it doesn't exist, you can do that on your own.
DoCmd.DeleteObject acQuery, "qtemp"
'lets create a new temporary query
Dim qdf As QueryDef
Set qdf = CurrentDb.CreateQueryDef("qtemp", sql)
'set the subform source object
Me!Datasheet.SourceObject = "query.qtemp"
'and it should work.
End Sub
CreateQueryDef
を使用してからMe.dataDisplaySubform.SourceObject = "Query.NewqueryName"
NewQueryName
は、createQueryDef
を使用して作成されたときに付けられた名前です。
他の読者のためのいくつかの明確なポイント:
詳細ビューサブフォームのsourceObjectプロパティは、表示される列/フィールドを決定します。したがって、テーブルまたはクエリに設定し、オプションでフィルターを使用してレコードを返さないようにするか(レコードセットを最初に空白にする場合)、カスタムSQLにrecordSourceを使用する代わりに使用できます。
RecordSourceには、任意のテーブル、クエリ、またはSQLを指定できますが、サブフォームには、sourceObjectのフィールドと一致する名前のフィールドのみが表示されます。これは、たとえば、sourceObjectをテーブルに設定し、次にrecordSourceを部分的に重複するフィールド名を持つクエリに設定する場合に混乱する可能性があります(Accessはすべての列を表示しますが、重複する列のみにデータが含まれます)。
任意のSELECTステートメントを表示するフォーム、またはユーザーがSELECTするテーブルを選択できるフォームを作成するには、入力を新しいクエリとして保存し(または、既存の名前付きクエリを上書きして)、sourceObjectを次のように設定します。 (新しい列を表示するには、フォームを閉じてから再度開く必要があるため、ポップアップまたは新しいタブを開いて結果を表示することをお勧めします)。