ACCESSとEXCELの連携について教えてください。
メインフォームA(単票)の中にサブフォームB(単票)があり、更にサブフォームB(単票)の中にサブフォーム(メインからみると孫フォーム)C(帳票)があるという構成のフォームがあります。各フォームにはそれぞれテキストボックスtext_X,text_Y,text_Zがあり、メインフォームにコマンドボタンQがあります。いま、このコマンドボタンQをクリックすることによってtext_X,text_Y,text_Zの内容をEXCELの特定ファイルの特定セル(例えばL1、M1、N1~10)に反映させたいと思っているのですが、メインフォーム(A)にあるtext_Xとサブフォーム(B)にあるtext_YはEXCELのセル(L1、M1)に取り込むことができるのですが、孫フォーム(C)にあるtext_Zをセル(N1~10)に反映させることが出来ません。text_Zはフォームが帳票フォームであることからLOOPを使っています。コマンドボタンQのクリック時のイベントでコードの書き方が違っているらしいのです。VBAは全く素人の手探り状態です。どなたか、素人でもわかるように教えていただけたら幸いです。よろしくお願い致します。
具体的には次のようなものです。(一部抜粋)
Private Sub コマンド145_Click()
Dim oApp As Object
Dim rs As DAO.Recordset
Dim i As Long
Set rs = Me!営業入力SF.Form.RecordsetClone
Set rs = 担当(1)F.Form.RecordsetClone
Set oApp = CreateObject("Excel.Application")
oApp.Visible = True
'Only XL 97 supports UserControl Property
On Error Resume Next
oApp.UserControl = True
'指定のエクセルファイルを開く
oApp.Workbooks.Open Filename:="I:\再出発!\受注票.xlt"
'エクセルファイルへデータセットする
oApp.Range("名称").Value = Me![名称]
oApp.Range("住所").Value = Me![住所]
oApp.Range("パンフ送付").Value = Forms![営業F]![営業入力SF]![パンフ送付日]
oApp.Range("DVD送付").Value = Forms![営業F]![営業入力SF]![DVD送付日]
oApp.Range("正式見積書").Value = Forms![営業F]![営業入力SF]![正式見積書送付日]
oApp.Range("契約書送付").Value = Forms![営業F]![営業入力SF]![契約完了日]
'サブフォームの内容をエクスポートする
i = 11
Do Until rs.EOF
oApp.Range("J" & Format(i)).Value = rs!テキスト1
oApp.Range("L" & Format(i)).Value = rs!テキスト4
i = i + 1
rs.MoveNext
Loop
Set rs = Nothing
Exit_コマンド145_Click:
Exit Sub
Err_コマンド145_Click:
MsgBox Err.Description
Resume Exit_コマンド145_Click
End Sub
(WINDOWS XP ACCESS 2002 EXCEL 2002 を使用)
>多分、孫フォーム自体を認識していないの
>ではないかと思っています。
ということなので、
ほぼ、同じ方法で実際にフォームを作ってやってみましたが、
データはExcelに表示されます。
各サブフォームを表示するコントロール名は
初期値のままの埋め込み0で行なっています。
孫サブフォームのレコードソースに適当にテーブルを設定し、
そのテーブルのフィールドの二つ、名前と住所のデータを
Excelにだしました。コードの中に
>If rs.Recordcount > 0 Then
などを入れていますが、孫サブフォームに
データがなければ、孫サブフォームの
データをExcelに送る処理を中止し、
メッセージをだします。
以下は、孫サブフォームのレコードを
Excelに送り出す部分。
Private Sub コマンド1_Click()
Dim rs As DAO.Recordset
Dim oApp As Object
Dim i As Long
Set oApp = CreateObject("Excel.Application")
oApp.Visible = True
'孫サブフォームのレコードセットを設定
Set rs = Forms!フォーム1!埋め込み0!埋め込み0!.Form.RecordsetClone
oApp.UserControl = True
oApp.Workbooks.Open Filename:="C:\Documents and Settings\user\デスクトップ\myData3.xls"
i = 11
If rs.RecordCount > 0 Then
rs.MoveFirst
Do Until rs.EOF
oApp.Range("J" & Format(i)).Value = rs!名前
oApp.Range("L" & Format(i)).Value = rs!住所
i = i + 1
rs.MoveNext
Loop
Else
MsgBox "孫サブフォームにデータがありません"
End If
'後始末
rs.Close
Set rs = Nothing
Set oApp = Nothing
End Sub
なお、実際にコードが動くまでは、エラー回避
などははずしておきます。
On Error Resume Next
はコメントアウトしておいてください。
#4です
できたようでなによりです。
いろいろ方法があると思いますので、以下参考までに
> 孫フォームにあるレコード数が多すぎて、EXCELの予定表示数を越えて表示されてしまう場合もあるのですが、例えば、最新10件のレコードを表示するとか、重要なレコードのみを表示するとかということは出来ないものでしょうか?
これは、Excelへ表示する(書き出す)時のことでしょうか。
i = 11
If rs.RecordCount > 0 Then
rs.MoveFirst
Do Until rs.EOF
oApp.Range("J" & i).Value = rs!名前
oApp.Range("L" & i).Value = rs!住所
i = i + 1
rs.MoveNext
Loop
は、フォームに表示している先頭から全部処理しているので
Excel20行目を書いてやめるには、
oApp.Range("L" & i).Value = rs!住所
If (i >= 20 ) Then Exit Do ' ★判別でループ抜け
i = i + 1
とか
また、フォーム表示の最後10行分を処理対象にするのなら
If rs.RecordCount > 0 Then
i = rs.RecordCount - 10 ' ★最後から10行前を開始位置に設定
If (i < 0) Then i = 0 ' ★
rs.AbsolutePosition = i ' ★
i = 11
Do Until rs.EOF
oApp.Range("J" & i).Value = rs!名前
とか
表示最後から処理したい時は、最後に行ってから前へ前へ
If rs.RecordCount > 0 Then
rs.MoveLast ' ★
Do Until rs.BOF ' ★
oApp.Range("J" & i).Value = rs!名前
oApp.Range("L" & i).Value = rs!住所
If (i >= 20 ) Then Exit Do ' ★判別でループ抜け?
i = i + 1
rs.MovePrevious ' ★
Loop
とか
また、「重要」は、それなりの判別処理を記述する必要があると思います。
例えば
Do Until rs.EOF
If (rs!XXXX = YYYY) Then ' ★「重要」なものの判別
oApp.Range("J" & i).Value = rs!名前
oApp.Range("L" & i).Value = rs!住所
If (i >= 20 ) Then Exit Do ' ★判別でループ抜け?
i = i + 1
End If
rs.MoveNext
Loop
要は、孫フォームにどのような順で表示しているから、
どのような順で何件分書き出すか、
だと思います。
なお、孫フォームの表示自体を最新10件分とか・・・は、
フォームの作り方等が関係すると思いますので、
現在の情報下では簡単ではないと思います。
子と孫の間で、
リンク親子フィールドを設定していたら・・・
各パターンの10件分を用意しておいて??・・・とか
その10件は、どのような順??・・・
その作り方次第で、更新できる/できないものになったり??・・・
リンク親子フィールドを設定していなかったら・・・
こっちの方が簡単だと思います。
また、孫の RecordsetClone は使わない・・・
っていう方法が良くなったりして??・・・
参考になればと・・・ 自己責任にて・・・
質問者
お礼
ご回答本当にありがとうございます。
>これは、Excelへ表示する(書き出す)時のことでしょうか。
そうです。
いろいろなケースを教えていただいて感謝いたしております。
とりあえず
>フォーム表示の最後10行分を処理対象にするのなら
If rs.RecordCount > 0 Then
i = rs.RecordCount - 10 ' ★最後から10行前を開始位置に設定
If (i < 0) Then i = 0 ' ★
rs.AbsolutePosition = i ' ★
i = 11
Do Until rs.EOF
oApp.Range("J" & i).Value = rs!名前
を利用してEXCELのフォームにうまく収まりました。
一人で考えていてはとてもこうはいきません。ホント助かりました。ありがとうございました。
サブフォームに TOP10 を表示
たとえば、サブフォームに設定したテーブルに、
売上げというフィールドがあるとします。
テーブル名をTaaaとします。このテーブルを
もとに、以下のクエリを作成します。
名前を、Q孫ソース とします。
Q孫ソース
SELECT TOP 10 Taaa.名前, Sum(Taaa.売上げ) AS 売上げの合計
FROM Taaa
GROUP BY Taaa.名前 DESC;
これを、登録して孫フォームのレコードソースに
してもいいですし、このSQL文を孫フォームの
レコードソースにところに直接貼り付けても
いいです。
試してみてください。
あるいは、IDなどを利用して表示順を以下の
ようにも設定できます。
SELECT TOP 10 Taaa.ID, Taaa.名前, Sum(Taaa.売上げ) AS 売上げの合計
FROM Taaa
GROUP BY Taaa.ID, Taaa.名前
ORDER BY Taaa.ID;
雰囲気だけでいいですか。
親フォーム「FrmX」には、
テキストボックス「text_X」、ボタンQ、サブフォームコントロール「FsubX」がある。
子フォーム「FrmY」には、
テキストボックス「text_Y」、サブフォームコントロール「FsubY」がある。
また、フォームは親「FsubX」の SourceObject に指定されている。
孫フォーム「FrmZ」には、
テキストボックス「text_Z」がある。
また、フォームは子「FsubY」の SourceObject に指定されている。
とした場合、
親フォームのボタンQがクリックされた時に、
テキストボックス「text_X」「text_Y」「text_Z」の値を引っ張るには、
With Me
XX = .text_X
With .FsubX.Form
YY = .text_Y
With .FsubY.Form
ZZ = .text_Z
End With
End With
End With
のように、順にサブフォームコントロールをたどっていけばよいと思います。
孫は、帳票で全部を引っ張るのなら、
With .FsubY.Form
ZZ = .text_Z
End With
部分を RecordsetClone を扱うように変更すれば、と思います。
例えば、
With .FsubY.Form.RecordsetClone
If (.RecordCount > 0) Then
.MoveFirst
While (Not .EOF)
ZZ1 = .Fields("フィールド名1")
ZZ2 = .Fields("フィールド名2")
.MoveNext
Wend
End If
End With
上記記述部分の、XX / YY / ZZ / ZZ1 / ZZ2 に、Excelのセルを指定すればよいと思います。
※ 未検証
自己責任にて・・・
※ 孫(子)フォームが表示されてから、一度も孫(子)フォーム側ににフォーカスが移動していなかったなどで、孫(子)の Recordset をうまく扱えなかった経験があります。
そういうことがあるかもしれないということで・・・
お礼
親切なご回答ありがとうございました。出来ました!孫フォームがすべてEXCELに表示されています。piroin654さんのものを丸写ししました。ありがとうございました。 ついでと言っては申し訳ないのですが、孫フォームにあるレコード数が多すぎて、EXCELの予定表示数を越えて表示されてしまう場合もあるのですが、例えば、最新10件のレコードを表示するとか、重要なレコードのみを表示するとかということは出来ないものでしょうか? 今回の質問事項とは直接関係はありませんがもし、ご存じでしたら教えていただけると助かります。 そんなこともありますので、もうしばらく質問を締め切らずにおきます。今回は本当にありがとうございました。