• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:現在開いている全てのフォームを閉じるVBA)

VBAで全てのフォームを閉じる方法

このQ&Aのポイント
  • VBAで全てのフォームを閉じる方法について説明します。
  • サンプル1では、全ての開いているフォームをループで処理して順番に閉じることができます。
  • 一方、サンプル2では、フォームを参照する番号が正しくないため、エラーが発生し閉じることができません。

質問者が選んだベストアンサー

  • ベストアンサー
  • 30246kiku
  • ベストアンサー率73% (370/504)
回答No.8

#7です Index の最小値は 0 で、途中閉じた場合は 0 スタートの連番で作り直される。 ここまでは良いでしょうか。 復習で、サンプル2の記述を 0 スタートに変更して、フォーム4つの時の動きを追ってみます。 Sub サンプル2()   Dim intCnt As Integer   For intCnt = 0 To Forms.Count - 1     DoCmd.Close acForm, Forms(intCnt).Name   Next End Sub フォームが4つなので For 文は 0 ~ 3 までループします。 (左側 Index、右側フォーム名) 0 フォームA 1 フォームB 2 フォームC 3 フォームD intCnt = 0 で閉じると 0 フォームB 1 フォームC 2 フォームD intCnt = 1 で閉じると 0 フォームB 1 フォームD intCnt = 2 で閉じようとすると、対象の Index が無いのでエラー これで、何故エラーとなるのか、ダメなのかの説明は終了です。 標題「現在開いている全てのフォームを閉じるVBA」について、 For intCnt = 1 To Forms.Count を使って、正常時だけを考えれば、以下の様な書き方もできますね。   For intCnt = 1 To Forms.Count     DoCmd.Close acForm, Forms(Forms.Count - 1).Name   Next どの回答も、標題から脱線したものはないと私は思います。 Index の小さい方から削除すると、こういう事が考えられますよって言ってただけで・・・ #1さんはチョッとした間違いはありましたが・・・・同じ記述でしたからね。 (たぶん、その時 Collection での最小値 1 が一瞬頭に浮かんだ・・・とか) (あまり記述しない 1 スタート For 文の 1 をそのまま使ってしまった・・・とか) Index の小さい方から実現しようとすると、かなり面倒です。 質問者さん これから作り込んでいかれるのなら、推奨というものがあるようですので参考にしてください。 また、ループを使って閉じる時の条件もあるようですので、注意して作り込んでください。 以前ある方に教えてもらったんです・・・・ ・処理等を提示する際には、前提条件を記述しないといけない 意味のないものだったんですね・・・・ その方に連絡できれば良いのですが、急激に目が悪くなったようで・・・ 中身がどうなっているのか分からないものを相手にしているのであれば、 Index は、 0 ~ の連番を維持するように作り直される・・・を理解され、 素直に Step -1 の方法に、エラーを無視する On Error Resume Next 等を組み込んだ上で、 使われた方が良いと私は思います。 ・閉じれなかったものは、そのままに・・・ ・一気に複数消えたら、一時的に Index 参照エラーになることも・・・ でも、Index = 0 まで処理を進めるので、動きは完璧に近くなると思います。 (大半の動作では問題ないような気がします)

usictyhb
質問者

お礼

ありがとうございました。

すると、全ての回答が全文表示されます。

その他の回答 (6)

  • 30246kiku
  • ベストアンサー率73% (370/504)
回答No.7

#5です なぜエラーとなるかは、#1さんが2段目で説明されていたので、 質問者さんの置かれている状況 ・新規で1から作っている ・既存のものを手直ししようとしている がわからなかったもので、削除する際の他の方法についてをメインに回答してみました。 私の提示した方法でも、Step -1 での方法でも対処できないものもあります。 例えば、 ・フォームAは自分では閉じれない状況で表示されている ・フォームAがフォームBを起動 ・フォームAがフォームCを起動 ・フォームCを閉じた時、フォームAを閉じる 簡単に言えば、起動されたフォームが閉じられる時、起動元も閉じる。 あまりこういう構成は取らないですかね。 起動された方は、自分の処理が終わったら情報を起動元に返すとかして、自分だけを閉じる・・ この動きが前提でありました。 Forms 内の情報ですが、私は以下の様に考えてます。 「フォームA」「フォームB」を順に表示した時は、 (左側がIndexで、右側がフォーム名) 0 フォームA 1 フォームB ここで、「フォームC」を表示した時は 0 フォームA 1 フォームB 2 フォームC となり 0 フォームC 1 フォームA 2 フォームB とか 0 フォームA 1 フォームC 2 フォームB ではないと思ってました。 また、サンプル2で何故エラーになるかの説明になると思いますが 0 フォームA 1 フォームB 2 フォームC の状況下で、フォームAを閉じたら 0 フォームB 1 フォームC になり 0 フォームC 1 フォームB にはならないと思ってました。 そこで、For のループを進んで Index = 2 を参照したところでエラーになる・・・・ Step - 1 でIndex の大きい方から閉じるのであれば、その Index 部分が消えるだけで、 前にある Index 部分に影響はないので、全部処理することが出来ます。 フォームを表示していくと ・現在の Index + 1 した所に情報が置かれる ・途中がなくなれば、0 スタートで順に繰り上がる と思ってます。なので、 インデックスが変わる可能性については、閉じた時の話であると私は解釈しています。 これを前提とするならば、 Index の小さい方に「親」と呼ばれるフォームが存在することになります。 「親」から処理するか「子」から処理するか、大きな違いがあるように私は思います。 (#5で紹介したように親が子を管理している場合もあるので) 前述した > 起動された方は、自分の処理が終わったら情報を起動元に返すとかして、自分だけを閉じる・・ を前提としたら、Step - 1 の方法であった方が少しは安全かと私は思います。 「親」も一緒に閉じる様な事をしていたら、On Error Resume Next でエラーを無視すれば・・・ 以上 私が思っていた事を前提とした内容になっているので、真偽は確かめてください。

usictyhb
質問者

お礼

ありがとうございました。

すると、全ての回答が全文表示されます。
  • DexMachina
  • ベストアンサー率73% (1287/1744)
回答No.6

No.2のDexMachinaです。 まず、前掲のURL http://technet.microsoft.com/ja-jp/subscriptions/dd589727%28v=office.11%29.aspx からの再引用になりますが、 > コレクションで付けられたインデックスは変わる可能性がある とあり、この際に順序が保持されるとの公式見解をみたことも 私はありませんので、1つのフォームを閉じるのに連動して 閉じられるフォームがある場合、For Next文によるループを 加算で回すか減算で回すかは、(発生頻度の差はあれ) 本質的な違いはなく、「0」からのスタートを危険とするならば、 「Step -1」も当然危険と考えるべきかと思います。 また、DoCmdのOpenFormメソッドではなく「Set New」による インスタンス生成によってフォームを表示する場合、メモリの 解放を明示的に行うべきであること(→特に外部MDBで定義 のフォームの場合)、及び変数の解放によってフォームが閉じる ことから、変数の解放によって閉じる(=DoCmd.OpenFormで 表示したフォームと処理を混同しない)、とするべきと考えます。 ということで、  ・すべてのフォームをFor Nextなどのループで閉じるのは、   展開中のフォームで、別のフォームが閉じる処理を行って   いない場合に限る  ・オブジェクト変数(配列を含む)に「Set New」を使用して   インスタンスを生成しているフォームは、その変数の定義   元に変数解放用のコードを記述し、それによってフォーム   を閉じる というのが、普段私がとっている手法であり、推奨する方法とも なります。 ・・・以上、「どうして、サンプル2じゃダメなのか」とのご質問の 趣旨から脱線してしまって申し訳ありませんが(汗)、参考まで。

usictyhb
質問者

お礼

ありがとうございました。

すると、全ての回答が全文表示されます。
  • 30246kiku
  • ベストアンサー率73% (370/504)
回答No.5

#3です 追記)以下の様な状況があるのなら検討に値すると私は思います。 Access 初心者、中級、上級・・・・このレベルはわかりませんが、 1つのフォームを条件を変えながら2つ3つ同時に表示したい・・・・ こういう要求は出てきてもおかしくはないと思います。 同じフォームを複数表示する その2 http://hatenachips.blog34.fc2.com/blog-entry-5.html ここで、実際の動きを感じれるサンプルの入手もできます。 そのサンプルのメニューフォームで、「支社別社員一覧」をクリックすると、 同じフォームで表示内容が異なる3つのフォームが新しく表示されます。 この時、Forms.Count は 4 (1つのメニューフォームと3つの支社フォーム) Public Sub test1()   Dim i As Long   For i = 1 To Forms.Count     Debug.Print "Forms.Count = " & Forms.Count     DoCmd.Close acForm, Forms(0).Name, acSaveNo   Next End Sub 上記の処理を記述し、フォームを閉じていこうとしたら、 i = 1 の Forms(0).Name でメニューフォームを削除したとたん、 全ての(他の3つも)フォームが閉じられてします。 For は i = 4 までループしますが、i = 2 の時には、 開いているフォームは 0 でエラーになっていきます。 Public Sub test2()   Dim i As Long   For i = Forms.Count - 1 To 0 Step -1     Debug.Print "Forms.Count = " & Forms.Count     DoCmd.Close acForm, Forms(i).Name, acSaveNo   Next End Sub とか Public Sub test3()   While (Forms.Count > 0)     Debug.Print "Forms.Count = " & Forms.Count     DoCmd.Close acForm, Forms(0).Name, acSaveNo   Wend End Sub であれば、エラーになることもなく処理が終了します。 1つのフォームを閉じたら、閉じられるのは1つ・・・・考えものだと思います。 現状、1つ閉じたら1つ・・・・なら、余計なお世話になりますが・・・・ 提示された Sub サンプル1() の、最後から処理するか、 常に Forms.Count を確認しながら 前の方で処理するか、 どちらかになるような気がします。 For 文で 0 ~ Forms.Count -1 や、1 ~ Forms.Count でのループは危険だと私は思います。

usictyhb
質問者

お礼

ありがとうございました。

すると、全ての回答が全文表示されます。
  • 30246kiku
  • ベストアンサー率73% (370/504)
回答No.3

Access で良かったでしょうか。 #1さんと被る部分があると思いますが、説明の練習と思って頂ければと・・・ 全フォームを閉じたいのですよね。 以下を実行してみて、動きはどうなりますか Public Sub Sample3()   Dim iCnt As Integer   On Error GoTo ERR_HND   iCnt = 0   While (Forms.Count > iCnt)     DoCmd.Close acForm, Forms(iCnt).Name, acSaveNo   Wend   Exit Sub ERR_HND:   iCnt = iCnt + 1   Resume Next End Sub 上記はチョッとわかりにくいですか。エラー処理を省いた以下ではどうでしょう。 Public Sub Sample3Kai()   While (Forms.Count > 0)     DoCmd.Close acForm, Forms(0).Name, acSaveNo   Wend End Sub これは、Forms.Count が 0 になるまで、Forms(0).Name で閉じていくものになります。 Forms(0) が閉じられたと同時に、Forms(0) で見えていたものが消えます。 ですが、すぐにまた 0 ~ 参照できるように作り直されます。 開いているフォームの個数は、Forms.Count で知ることが出来ます。 For i = 0 to Forms.Count - 1 と記述すると、記述した時点の Forms.Count 分ループします。 なので、ループ途中で閉じて、Forms.Count が減っても、その値は参照されません。 フォームを開いていない時に For i = Forms.Count - 1 to 0 Step -1 とした場合、 For i = -1 to 0 Step -1 と記述したことと同じになり、1度もループ内を処理することはありません。 また、For i = 1 to 0 の記述でもループ内を処理することはありません。 Forms.Count の変化を見ながら処理したい場合は、冒頭で示した Sample3 の様に 都度判別するような記述が必要になります。 また、Sample3 の処理では、フォームを閉じようとしたけど閉じれなかった・・・ ・何らかのエラー ・「読み込み解除時」 Form_Unload(Cancel As Integer) で Cancel = True していた これらの時は、閉じるのはあきらめて次を閉じましょう・・・ というものになっています。

usictyhb
質問者

お礼

ありがとうございました。

すると、全ての回答が全文表示されます。
  • DexMachina
  • ベストアンサー率73% (1287/1744)
回答No.2

【要旨】 Formsコレクションの最小Indexは「0」なので、ループ内での 最小値も「0」とする必要があります。 【詳細】 > どうして、サンプル2じゃダメなのか教えていただけますか? FormsコレクションのIndexは、  最小値は「0」  最大値は「開いているフォームの数-1」 です。 http://technet.microsoft.com/ja-jp/subscriptions/dd589727%28v=office.11%29.aspx なので、2つのフォームが開いているときに存在するのは  Forms(0)、Forms(1) になります。 ここで、サンプル1・サンプル2でのintCntを確認すると、  サンプル1: Forms.Count-1~0 → 1~0  サンプル2: 1~Forms.Count → 1~2 となり、サンプル2では、存在しない「Forms(2)」に対して Nameプロパティを参照してしまいます。 これが、エラーの原因です。 (なお、DoCmd.Close自体は、現在開いていないフォーム  を指定しても、エラーにはなりません。今回ご質問の  エラーは、あくまで「Forms(2)」を参照しようとした  ところで発生します) では、サンプル2のintCntの範囲が「0~1」となるように Sub サンプル2()   Dim intCnt As Integer   For intCnt = 1 To Forms.Count     DoCmd.Close acForm, Forms(intCnt).Name   Next End Sub とすればOkかというと、これもエラーとなります。 これは、(既にnicotinismさんからも説明がありますが) 1つ目のフォームが閉じられたところで、Formsコレクションの Indexは連番になるように割り当て直されてしまうためです。 (この再割当をせず、閉じた番号分が欠番のままとされて  しまうと、今度はForms.CountでIndexの最大値を取得  できない、という問題が発生するため、再割当は必須、と) ですので、サンプル2のパターンで正常動作をさせるには、 ループ内でのIndexを「0」で固定(=Formsコレクションで 管理されている先頭のフォームを常に対象)としてやれば Ok、となります。 Sub サンプル2()   Dim intCnt As Integer   For intCnt = 1 To Forms.Count     DoCmd.Close acForm, Forms(0).Name   Next End Sub

usictyhb
質問者

お礼

ありがとうございました。

すると、全ての回答が全文表示されます。
  • nicotinism
  • ベストアンサー率70% (1019/1452)
回答No.1

サンプル1はフォームのインデックス数の大きい順に閉じてます。 一方、サンプル2は最小値1から閉じようとしています。 この時に何が起きてるかというと 仮にフォームをニケ開いてるとします For intCnt = 1 To Forms.Count ’------1 DoCmd.Close acForm, Forms(intCnt).Name’--2 Next ループはForms.Countの値(=2)まで繰り返されます フォームを閉じた場合にはForms.Count は一つ減りますが For Next のループ回数は変わることはありません。固定です。 なので二回目のループに入ります。 で、Forms(intCnt) でForms(2) のフォームを閉じようとした時には Forms のIndex(数)は1しかありませんのでエラーとなります。 Sub サンプル2() 'フォームがひとつも開いてない時に実行してもエラーにならない。 Dim intCnt As Integer For intCnt = 1 To Forms.Count DoCmd.Close acForm, Forms(intCnt).Name msgbox forms.count Next End Sub や Sub サンプル3() Dim i As Integer Dim toN As Integer toN = 5 For i = 1 To toN MsgBox "i= " & i & " toN= " & toN toN = 3 Next End Sub でtoN は3に変化しても、5回繰り返されるのを確認してみてください。 Sub サンプル4() 'これならエラーにはなりません。 Dim intCnt As Integer For intCnt = 1 To Forms.Count DoCmd.Close acForm, Forms(1).Name Next End Sub 分かってもらえただろうか?説明へたくそ。

usictyhb
質問者

お礼

ありがとうございました。

すると、全ての回答が全文表示されます。

関連するQ&A