どうして、サンプル2じゃダメなのか教えていただけますか?
-------------------------------------------------------------
Sub サンプル1()
'フォームがひとつも開いてない時に実行してもエラーにならない。
Dim intCnt As Integer
'全ての開いているフォームのループ
For intCnt = Forms.Count - 1 To 0 Step -1
'コレクションの intCnt番目のフォームを閉じる
DoCmd.Close acForm, Forms(intCnt).Name
Next intCnt
End Sub
Sub サンプル2()
'フォームがひとつも開いてない時に実行してもエラーにならない。
Dim intCnt As Integer
For intCnt = 1 To Forms.Count
DoCmd.Close acForm, Forms(intCnt).Name
Next
End Sub
-------------------------------------------------------------
サンプル1を実行すると、問題なく全てのフォームを閉じる事が出来ますが、
サンプル2だと、二つ開いているフォームのうち、1つ目は閉じる事が出来るのですが
2つ目のフォームを閉じようとすると、
実行時エラー 2456 "フォームを参照するときに使っている番号が正しくありません"
となってしまいます。
私としては、サンプル1もサンプル2も同じ動きをするものだと思ってるのですが
どうやら違うようです。
どうして、サンプル2じゃダメなのか教えていただけますか?
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 まで処理を進めるので、動きは完璧に近くなると思います。
(大半の動作では問題ないような気がします)
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 でエラーを無視すれば・・・
以上 私が思っていた事を前提とした内容になっているので、真偽は確かめてください。
No.6
- 回答日時:
No.2のDexMachinaです。
まず、前掲のURL
http://technet.microsoft.com/ja-jp/subscriptions …
からの再引用になりますが、
> コレクションで付けられたインデックスは変わる可能性がある
とあり、この際に順序が保持されるとの公式見解をみたことも
私はありませんので、1つのフォームを閉じるのに連動して
閉じられるフォームがある場合、For Next文によるループを
加算で回すか減算で回すかは、(発生頻度の差はあれ)
本質的な違いはなく、「0」からのスタートを危険とするならば、
「Step -1」も当然危険と考えるべきかと思います。
また、DoCmdのOpenFormメソッドではなく「Set New」による
インスタンス生成によってフォームを表示する場合、メモリの
解放を明示的に行うべきであること(→特に外部MDBで定義
のフォームの場合)、及び変数の解放によってフォームが閉じる
ことから、変数の解放によって閉じる(=DoCmd.OpenFormで
表示したフォームと処理を混同しない)、とするべきと考えます。
ということで、
・すべてのフォームをFor Nextなどのループで閉じるのは、
展開中のフォームで、別のフォームが閉じる処理を行って
いない場合に限る
・オブジェクト変数(配列を含む)に「Set New」を使用して
インスタンスを生成しているフォームは、その変数の定義
元に変数解放用のコードを記述し、それによってフォーム
を閉じる
というのが、普段私がとっている手法であり、推奨する方法とも
なります。
・・・以上、「どうして、サンプル2じゃダメなのか」とのご質問の
趣旨から脱線してしまって申し訳ありませんが(汗)、参考まで。
No.5
- 回答日時:
#3です
追記)以下の様な状況があるのなら検討に値すると私は思います。
Access 初心者、中級、上級・・・・このレベルはわかりませんが、
1つのフォームを条件を変えながら2つ3つ同時に表示したい・・・・
こういう要求は出てきてもおかしくはないと思います。
同じフォームを複数表示する その2
http://hatenachips.blog34.fc2.com/blog-entry-5.h …
ここで、実際の動きを感じれるサンプルの入手もできます。
そのサンプルのメニューフォームで、「支社別社員一覧」をクリックすると、
同じフォームで表示内容が異なる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 でのループは危険だと私は思います。
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 していた
これらの時は、閉じるのはあきらめて次を閉じましょう・・・
というものになっています。
No.2
- 回答日時:
【要旨】
Formsコレクションの最小Indexは「0」なので、ループ内での
最小値も「0」とする必要があります。
【詳細】
> どうして、サンプル2じゃダメなのか教えていただけますか?
FormsコレクションのIndexは、
最小値は「0」
最大値は「開いているフォームの数-1」
です。
http://technet.microsoft.com/ja-jp/subscriptions …
なので、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
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
分かってもらえただろうか?説明へたくそ。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Visual Basic(VBA) ユーザーフォーム「frm_基本❶」を立ち上げると新規で入力する行数を右下のNoとして表示しています。 1 2023/03/16 19:02
- Visual Basic(VBA) パーソナルXLSBのfuctionを呼び出すと「Functionが定義されていません」のエラーになる 2 2022/08/22 22:51
- Visual Basic(VBA) VBA Userformで一部別シートに転記がしたいのですが 2 2023/05/24 13:08
- Visual Basic(VBA) エクセルVBA(実行時エラー438)の対処法を教えてもらえないでしょうか 3 2023/04/22 13:43
- Visual Basic(VBA) エクセルのマクロについて教えてください。 2 2023/07/15 15:48
- Visual Basic(VBA) VBA Bookの表示、非表示 1 2022/09/16 20:44
- Access(アクセス) Dlookupにエラーがでてしまう 1 2022/10/31 14:35
- Visual Basic(VBA) マクロVBA 1シートをまとめる 閉じ方 初心者 SOS! 1 2022/06/17 14:54
- Visual Basic(VBA) ①ExcelVBAでカレンダーを作り、別のユザーフォームで日付を入力したいのですがエラーになります。 1 2023/02/17 18:39
- Visual Basic(VBA) vbaのエラー対応(実行時エラー7:メモリが不足しています) 4 2023/04/24 00:20
このQ&Aを見た人はこんなQ&Aも見ています
-
性格の違いは生まれた順番で決まる?長男長女・中間子・末っ子・一人っ子の性格の傾向
同じ環境で生まれ育っても、生まれ順で性格は違うものなのだろうか。家庭教育研究家の田宮由美さんに教えてもらった。
-
アクセスVBA現在開いている全てのテーブルを閉じる
Access(アクセス)
-
ACCESSのEXEを作るのは可能ですか?
その他(データベース)
-
Access フォームのテキストボックスに半角英字のみで入力する設定は
Access(アクセス)
-
-
4
EXCEL VBAで全選択範囲の解除
Excel(エクセル)
-
5
エクセルのラベルの値(文字列)を垂直方向で中央揃えにするには?
Excel(エクセル)
-
6
VBA:ユーザーフォームのマルチページに色を付けたい。
Word(ワード)
-
7
エクセルVBAでマルチページの切り替え方法の件で
Excel(エクセル)
-
8
Accessを開くと「排他モードじゃないので変更しても保存できない」との旨の表示が出てしまう。
Access(アクセス)
-
9
テーブルに主キーを作らないデメリットは?
その他(Microsoft Office)
-
10
エクセルVBAでUserFormを起動した時
Excel(エクセル)
-
11
VBAでユーザーフォームが自動的に消える
Visual Basic(VBA)
-
12
VBA テキストボックスを選択状態にしたい
その他(プログラミング・Web制作)
-
13
UserForm1.Showでエラーになります。
工学
-
14
ブックをCloseまたはQuitで閉じると他のユーザーフォームが消えてしまう。
Excel(エクセル)
-
15
Accessでナビゲーションウィンドウを開かないようにする方法はありますか?
その他(Microsoft Office)
-
16
Excel VBA ユーザーフォーム 複数のユーザーフォームの閉じ方。
Visual Basic(VBA)
関連するカテゴリからQ&Aを探す
おすすめ情報
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
ユーザーフォームのテキストボ...
-
ユーザーフォームを表示中にシ...
-
クリックイベントなのに、2回ク...
-
ExcelVBAのユーザーフォームの...
-
MSGBOXのフォント大きさ変更
-
モーダルフォームとモードレス...
-
テキストボックスやラベルのクリア
-
ユーザーフォーム上に現在日時...
-
エクセルVBAのフォームを最...
-
フォームのテキストボックスな...
-
Form_Load と Form_Activate の...
-
VBAでユーザーフォームを再表示...
-
【VBAユーザーフォームで閉じる...
-
コントロールの存在確認
-
複数モニタ使用時のフォームの...
-
Excelにて、ユーザーフォームで...
-
Hideについて(.NET)
-
ブックをCloseまたはQuitで閉じ...
-
ユーザーフォームのラベルに時...
-
EXCEL VBA ユーザーフォームの...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
ユーザーフォームを表示中にシ...
-
ExcelVBAのユーザーフォームの...
-
エクセルVBAのフォームを最...
-
クリックイベントなのに、2回ク...
-
ユーザーフォームのテキストボ...
-
モーダルフォームとモードレス...
-
Form_Load と Form_Activate の...
-
VBAでユーザーフォームを再表示...
-
Hideについて(.NET)
-
ACCESSのフォーム、開くんです...
-
MSGBOXのフォント大きさ変更
-
Excelにて、ユーザーフォームで...
-
【VBAユーザーフォームで閉じる...
-
ユーザーフォーム上に現在日時...
-
VBA(エクセル)のユーザー...
-
access2021 更新前に処理をしたい
-
Accessで、一つのフォーム画面...
-
コントロールの存在確認
-
Microsoft Formsの「個人情報や...
-
複数モニタ使用時のフォームの...
おすすめ情報