こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

締切り済みの質問

Accessでサブフォームの合計をメインフォームに

Access2007で開発しています。
メインフォームにサブフォームを貼り付けており、
サブフォームに入力した「数量」「単価」から「金額」を計算して表示し、
その合計金額をメインフォームのテキストボックス(非連結)に表示しようと
しています。
サブフォームは行単位で追加、削除が可能です。
サブフォームの更新結果が正しくメインフォームに表示されずに困っています。
おわかりの方、おられましたらお教え頂きたく、よろしくお願い致します。

【サブフォーム】
  ・レコードソース:T_明細
  ・[詳細]項目:数量・・・レコードソース=T_明細・数量
       単価・・・    〃    =T_明細・単価
       金額・・・数量および単価のAfterUpdateで計算して表示。
   [フォームフッター]項目:金額計・・・コントロールソース=Sum([金額])

  ・Vbaコード:
  Private Sub Form_AfterUpdate()
  Forms![メインフォーム].合計計算
  End Sub
  Private Sub Form_Delete(Cancel As Integer)
  Forms![メインフォーム].合計計算
  End Sub

【メインフォーム】
 ・テキストボックス:「合計金額」(非連結)

Public Sub 合計計算()
   Me.サブフォーム.Requery
  方法1: Me!合計金額 =DSum("金額", "T_明細", (キー項目指定 記述省略))
  方法2: Me!合計金額 = Me![サブフォーム].[Form].[金額計]
End Sub

ここで、
方法1の場合:数量、単価の変更入力及び行追加は正しく動作するが、
         行削除の後、メインフォームの「合計金額」が再計算されず、変更前のまま。

方法2の場合:数量、単価の変更入力及び行追加すると、「合計金額」が”0”になる。
         行削除しても「合計金額」は変わらず、変更前のまま。

どちらも、サブフォオームの「金額計」は正しく表示されています。

以上、よろしくお願い致します。

投稿日時 - 2012-03-14 13:53:15

QNo.7361587

困ってます

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(7)

ANo.7

#6です

> ご提案頂いた方法を試してみますね

とのことなので、#5での注意点を。

サブフォームは帳票フォームで、レコードセレクタを使って、
「Delete」キーでレコードを削除する操作が前提になっています。

また、レコードセレクタで行を選択する際、「新規行は含まれない」ことが前提です。
新規行を含んだ選択された場合、記述を変更する必要があります。
それ用のコードも用意してましたが、必要であれば提示できます。

※ でも、Form_Delete 以降が呼ばれないのは不思議ですね。

投稿日時 - 2012-03-29 17:45:46

お礼

大変お返事が遅くなり申し訳ありません。
お教え頂いた方法、クラスを作るやりかたおよび、「キーイベント取得」を”はい”にする方法とやってみました。
どちらも、サブフォームの行削除は正常にできるのですが、親フォームの合計金額は変わりませんでした。(親フォームの合計計算モジュールは、実行されます。)
いろいろご指導いただき、本当にありがとうございました。

投稿日時 - 2012-04-12 11:48:52

ANo.6

#5です

記述の訂正を
(時間が取れない場合は、放置してください)

> ちなみに、
> Me.サブフォーム.Requery
> を
> Me.サブフォーム.From.Requery ★


★部分 From ではなく Form でした

Me.サブフォーム.Form.Requery

投稿日時 - 2012-03-19 21:00:42

お礼

わざわざロジックを考えて下さいまして、ありがとうございます!
気づくのが遅くなりました。申し訳ありません。
ご提案頂いた方法を試してみますね。ただ、今は納期が迫っており、時間がとれませんので、もう少々お待ち下さい。
重ねて、ありがとうございました。

投稿日時 - 2012-03-29 13:22:52

ANo.5

#3です

解決された様でなによりです。

土日もがいていて、それなりに推測した結果のものになります。
お手数ですが、推測があっているかどうかだけでも教えていただけないでしょうか。

サブフォームは、帳票であることが前提となっています。

> (なお、今回の質問では単純化していますが、実際は、複雑な処理も行うため、またユーザインファーフェースのことも考慮して、VBAで作成しています。)

と言う事なので、キー操作等統一する為に「クラス」を作って、そこで共通の処理を記述していないでしょうか。
また、親の関数先頭で行っている
Me.サブフォーム.Requery
は、行を移動しないで Requery したかったのでしょうか。

ちなみに、
Me.サブフォーム.Requery

Me.サブフォーム.From.Requery
に変更すると、「3246の この操作は、トランザクションには実行できません」
のエラーになったと思います。(削除の処理中なので)


クラスを作って、Form_Delete 時に自力削除(以降のイベントは起きない)例を以下

クラス「clsFrm」を新規挿入し、以下を記述します。

Option Compare Database
Option Explicit

Private Const EVENT_PROCEDURE As String = "[EVENT PROCEDURE]"
Private WithEvents frm As Form
Private iCount As Long
Private bDel As Boolean

Private Sub Class_Initialize()
  Set frm = CodeContextObject
  frm.OnDelete = EVENT_PROCEDURE
  iCount = -1
  bDel = False
End Sub

Private Sub Class_Terminate()
  Set frm = Nothing
End Sub

Private Sub frm_Delete(Cancel As Integer)
  Dim i As Long

  DoCmd.CancelEvent

  If (frm.SelHeight < 1) Then Exit Sub
  If (iCount < 1) Then iCount = frm.SelHeight
  If (iCount = frm.SelHeight) Then
    bDel = MsgBox(frm.SelHeight & "件 削除しますか?" _
        , vbQuestion + vbYesNo, "確認") = vbYes
  End If
  iCount = iCount - 1
  If (iCount = 0) Then
    If (bDel) Then
      With frm.Recordset
        For i = 1 To frm.SelHeight
          .Delete
          .MoveNext
        Next
      End With
      frm.SelHeight = 0
    End If
    bDel = False
  End If
End Sub


サブフォームに以下を記述します。

Dim frm As clsFrm ' ★

Private Sub Form_AfterDelConfirm(Status As Integer)
  MsgBox "Form_AfterDelConfirm"
End Sub

Private Sub Form_BeforeDelConfirm(Cancel As Integer, Response As Integer)
  MsgBox "Form_BeforeDelConfirm"
End Sub

Private Sub Form_Delete(Cancel As Integer)
  MsgBox "Form_Delete"
  Call Me.Parent.Req
End Sub

Private Sub Form_Load()
  Set frm = New clsFrm ' ★
End Sub

Private Sub Form_Close()
  Set frm = Nothing ' ★
End Sub

★部分がクラスを使う宣言みたいなもの


メインフォームに以下を記述します。

Public Sub Req()
  Me.FSUB.Requery
'  方法1
End Sub

※ FSUB はサブフォームコントロール名


この記述で Form_Delete しか呼ばれないものは作れました。
(細かいタイミングまではみてません。動いたっぽいレベル)
(サブフォーム Form_Delete の Cancel は意味を持たなくなります)

このようになっていると、Form_Delete のタイミングは使えないので、
クラスに制御を渡さないで、
サブフォームで「キーイベント取得」を「はい」として、例えば、

Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
  Dim i As Long

  Select Case KeyCode
    Case vbKeyDelete
      If (Me.SelHeight < 1) Then Exit Sub
      KeyCode = 0
      If (MsgBox(Me.SelHeight & "件 削除しますか?" _
          , vbQuestion + vbYesNo, "確認") = vbYes) Then
        With Me.Recordset
          For i = 1 To Me.SelHeight
            .Delete
            .MoveNext
          Next
        End With
        Me.SelHeight = 0
        Call 親の関数
      End If
  End Select
End Sub

とすればやりたいことはできそうです。
(この場合 Form_Delete は呼ばれません)
親の関数では、方法1の方が良さそうです。
ただ、処理を統一したいクラスを使わなくなるので、良いのか悪いのか。

クラス内では、キーイベント取得はやってないような気が(私の知識不足かも)
なぜなら、Form_Delete は Private で呼ばれているので、
クラスから Private を呼び出せなかったような・・・


以上、よろしくお願いいたします。

投稿日時 - 2012-03-19 17:59:15

ANo.4

間違ってメインフォームに記述しているとか。。。。

投稿日時 - 2012-03-15 14:06:39

補足

解決しました。
Form_Deleteイベントは、
「レコードが実際に削除される前に発生するもので、この時点ではまだ削除はされていません。」
ということなので、Form_Deleteで、(サブフォームの合計金額-削除しようとしている行の金額)を計算して、メインフォームに表示する方法で解決しました。
いろいろ考えて下さってありがとうございました。

投稿日時 - 2012-03-19 10:27:39

お礼

回答ありがとうございます。
なるほど、ありそうですね。でもそれはありません。

投稿日時 - 2012-03-16 16:36:14

ANo.3

#1です

タイミング的には #2 さんの通りだと思います。

提示した参照先の画像にあるフォームに MsgBox を組み込んでみたところ、
言われているような結果にはならず、順に呼ばれました。
私はお手上げです。

簡単なメイン/サブ構成のフォームを作成し、各イベントで MsgBox のみを記述され、
確認されてはいかがでしょうか。

失礼しました。

投稿日時 - 2012-03-15 13:16:15

補足

解決しました。
教えて頂いたことがヒントになりました。
おっしゃる通りForm_Deleteイベントは、
「レコードが実際に削除される前に発生するもので、この時点ではまだ削除はされていません。」
ということなので、Form_Deleteで、(サブフォームの合計金額-削除しようとしている行の金額)を計算して、メインフォームに表示する方法で解決しました。
いろいろ考えて下さってありがとうございました。

投稿日時 - 2012-03-19 10:28:41

お礼

再度の回答、ありがとうございます。
そうですね、一度やってみます。

投稿日時 - 2012-03-16 16:34:54

ANo.2

このイベントで確認したらどうでしょう。

---
Private Sub Form_AfterDelConfirm(Status As Integer)
If (Status = acDeleteOK) Then
Forms![メインフォーム].合計計算
End If
End Sub

投稿日時 - 2012-03-14 17:18:11

お礼

早速回答いただき、ありがとうございます。
実は、いろいろ試行錯誤をしていまして、下記のロジックの場合、
Private Sub Form_AfterUpdate()
MsgBox "Form_AfterUpdate"
Forms![メインフォーム].合計計算
End Sub

Private Sub Form_BeforeDelConfirm(Cancel As Integer, Response As Integer)
MsgBox "Form_BeforeDelConfirm"
End Sub

Private Sub Form_Delete(Cancel As Integer)
MsgBox "Form_Delete"
End Sub

Private Sub Form_AfterDelConfirm(Status As Integer)
MsgBox "Form_AfterDelConfirm"
If (Status = acDeleteOK) Then
Forms![メインフォーム].合計計算
End If
End Sub

表示されるMsgboxは、"Form_Delete"のみでした。
他のイベントが発生しない理由がわからず、困っています。
他の案があれば、どうかご提案下さい。

投稿日時 - 2012-03-15 11:21:35

ANo.1

VBA は必要無いような気がします。

過去 QA で、解決されていませんが近い構成だと思います。

access2010のフォーム上で計算したいです
http://okwave.jp/qa/q7079470.html


VBA でやる場合、イベントの発生タイミングを確認されたらと思います。

削除の操作を行うと、
Form_Delete / (Form_Current) / Form_BeforeDelConfirm / Form_AfterDelConfirm
のイベントが順に発生したと思います。
何もしないと、「削除しますか?」のAccessからのメッセージは、
Form_BeforeDelConfirm 後に表示されたと思います。

なので、Form_Delete 時に合計金額を求めても元のままだと思います。

投稿日時 - 2012-03-14 17:15:11

お礼

早速回答いただき、ありがとうございます。
Form_BeforeDelConfirm、Form_Delete、Form_AfterDelConfirm
のそれぞれに Msgbox をつけて実行したところ、
行削除をしたタイミングで発生するイベントは
Form_Delete
のみでした。
ここで合計金額を求められないとすれば、タイミングがないですよね。
行削除を、別の方法で行うしかないのでしょうか。
他の提案があれば、ご教示いただきたく、よろしくお願いします。

(なお、今回の質問では単純化していますが、実際は、複雑な処理も行うため、またユーザインファーフェースのことも考慮して、VBAで作成しています。)

投稿日時 - 2012-03-15 11:31:07

あなたにオススメの質問