• ベストアンサー

DoEvents関数って何?

こんにちは。 VBAやプログラミングに詳しい皆様に 教えていただきたい質問があります。 cells(1,1)からcells(5000,1)までの値を消去するときに 処理の進行状況を表示するためにuserform上にプログレスバーを表示したいと思います。 そこで下記のようなコードを入力しました。 userform1.show for i =1 to 5000 cells(i,1)="" userform1.progressbar1.value=i/5000*100 next i unload userform1 しかしこれだとuserformの背景が真っ白になってしまい ラベルの文字も消えてしまいます。 そこで「EXCEL VBA パーフェクトマスター」という本を見たら for i =1 to 5000 cells(i,1)="" userform1.progressbar1.value=i/5000*100 DoEvents next i unload userform1 と入力すれば解決することがわかりました。 しかし「DoEvents」についてあまり詳しく書いていなかったのでDoEvents関数をヘルプで見ると、 「発生したイベントがオペレーティング システムによって処理されるように、プログラムで占有していた制御をオペレーティング システムに渡すフロー制御関数です。」 と書いてあるのですが正直、書いてあることがよくわかりません。 どなたかDoEvents関数について、 もう少しわかりやすく教えていただけませんか。 それから、最初に書いたコードで実行すると ユーザーフォームの背景が真っ白になってしまう原因も 教えていただけませんか? よろしくお願いいたします。

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

  • ベストアンサー
回答No.2

簡単に言うと、 OS に制御を渡すってことです。(ヘルプそのまんま) 時間が掛かるループ処理などの場合、ループが終わるまで制御は独占されてしまいます。 ですのでループ中は OS や Excel そのものにも再描画をさせる暇さえ与えません。 途中に DoEvents を入れると制御が OS に渡るので、OS は溜まっていた処理をそこで行うことができます。 結果、フォームの再描画などが行われることになります。 注意点ですが、 Private Sub CommandButton1_Click()   Dim i As Long   For i = 1 To 50000     DoEvents     Cells(i,1) = ""   Next i End Sub Private Sub CommandButton2_Click()   MsgBox "hoge" End Sub っていうフォームのコードがあった場合、 DoEvents を入れることによって、ループ中にユーザーがCommandButton2 を押すことによって CommandButton2 のクリック イベントも動いちゃいます。 CommandButton1 のクリック イベントではループの前に CommandButton1.Enabled = False CommandButton2.Enabled = False を書いてフォーム上の CommandButton を無効にしておき、ループが終わったら CommandButton1.Enabled = True CommandButton2.Enabled = True と書いて CommandButton を有効に戻してください。 これを工夫すれば、CommandButton2 で CommandButton1 のループを途中キャンセルする処理もすることができます。 Private Canceled As Boolean Private Sub CommandButton1_Click()   CommandButton2.Enabled = False   Dim i As Long   For i = 1 To 50000     DoEvents     If Canceled = True Then       MsgBox "キャンセルしました"       Exit Sub     End If     Cells(i, 1).Value = ""   Next i End Sub Private CommandButton2_Click()   Canceled = True End Sub コードの行頭にあるスペースは見易さのために全角スペースで作成していますので、これをこのままコピペするとエラーになるかもしれません。 コピペするなら行頭の全角スペースを半角スペースに直してください。

19746999
質問者

お礼

temtecomai2さん とても詳しい御回答どうもありがとうございました。 今回、『再描画』という意味を初めて知りました。 勉強になります! temtecomai2さんの書いていただいたコードをコピペしてみて 試してみました! おかげ様でDoEventsの効果がよくわかりました。 それとDoEventsを入れるのと入れないのでは処理速度も違うんですね。 下の例ではDoEventsを入れた方は78秒、入れなかったほうは25秒でした。 これって制御をOSに渡す時間が下のコードの例では53秒(78-25)も掛かるって事なんでしょうか?

その他の回答 (7)

  • don_go
  • ベストアンサー率31% (336/1059)
回答No.8

No.7 の件について このカテゴリーがVisual Basicの為、うっかりVBでプログラム を作ってしまっていました。 手もとにあるExcel97とExcel2000とで作成してみましたが、 テキストボックスにGotFocusプロパティ自体無く、且つ実行 してもエラーも発生しませんでした。 (Win98SE 及びWin2000) どうも 19746999 さんが使用しているExcel とはバージョン又 は実行環境が異なる為、動作が異なっている事が考えられ ます。

19746999
質問者

お礼

don_goさん こんにちは ・・しまった。。 「EXCELのVBAに関する質問」 って最初に書くの忘れていました。 ごめんなさい。これからは気をつけます・・・。 そうですね! 僕が使っているのはEXCEL2002で VBAのバージョンは Microsoft Visual Basic 6.0です。 結局違いがわからないままですが、 過去に書いていただいた方の アドバイスを参考にして 理解していこうと思います。 それではあと1,2日で質問を締め切ろうと思います。 ポイントあげる人迷うなぁ・・・

  • don_go
  • ベストアンサー率31% (336/1059)
回答No.7

間違いの例としては、あまり良いのが浮かばなかったので申し訳ありませんが... DoEvents の有無により動作の違う例を Private Sub Text1_GotFocus() MsgBox "GotFocus" End Sub Private Sub Command1_Click() Text1.Text = 1 Text1.SetFocus DoEvents MsgBox "hoge" End Sub

19746999
質問者

お礼

don_goさん こんにちは 具体的なご回答ありがとうございます。 ・・大変申し訳ないのですが ユーザーフォームを作って コマンドボタンとテキストボックスを そのユーザーフォームの上に作って (もちろんオブジェクト名は統一させました。) 教えていただいたイベントプロシージャを コピペして試してみたのですが、 よくわかりません。 と言うより「Gotfocus」イベントが働きません。 なのでDoeventsの有無にかかわらず動きが同じに感じます。 試しにシート上にテキストボックスを作って 教えていただいたコードを入力して (もちろんオブジェクト名は統一させた。) Gotfocusイベントが働くか確認するために そのテキストボックスをクリックすると、 ちゃんとGotfocusイベントが動くのですが、 コマンドボタンをクリックしてマクロを実行すると setfocusメソッドでのところで下記のエラーが出てしまいます。        ↓ オブジェクトは、このプロパティまたはメソッドをサポートしていません。(Error 438) いろいろ考えたのですが どこが間違っているのかわかりませんでした。 わかりにくい質問だと思いますが 何が間違っているのか教えていただけませんか? 何度も質問してしまって申し訳ございませんが よろしくお願いします。

  • don_go
  • ベストアンサー率31% (336/1059)
回答No.6

DoEventsは、MS-WindowsがUNIXやLinux等のマルチタスクOSではなく、 疑似マルチタスクで有るため必要になるものです。 通常マルチタスクOSでは、同時に実行される各種のプログラムに対して 処理に必要な時間と順序をOSが配分して実行しますが、MS-Windows ではプログラムから処理が戻ってから(今回ではプロシージャが終了して から)、次のプログラムやキー入力、マウス入力イベント等を実行します。 従って、時間のかかる処理や無限ループなどがあると、他の処理が実行 できなくなり、極端に全体の反応が遅くなったり、動かなくなったりします。 #画面が白くなるのは、表示の更新が行われなくなるためです。 そうなる事を防ぐ為、DoEventsを実行して、他のプログラムやイベント 処理を実行させる必要があります。 但し、Form上のコントロールに対して値をセットした後、DoEventsを実行 した場合、ChangeやGetFocusイベントが実行され、思っていなかった動作 をする事があるので、使用する場所には十分注意する必要があります。

19746999
質問者

お礼

don_goさん ご回答ありがとうございます。 たくさんのわかりやすいご回答を頂いてきましたが 更に重要な回答を投稿していただいてうれしいです。 それから、もしよろしければ >但し、Form上のコントロールに対して値をセットした後、DoEventsを実行 した場合、ChangeやGetFocusイベントが実行され、思っていなかった動作 をする事があるので、使用する場所には十分注意する必要があります。 ごめんなさい・・・この部分もう少し詳しく教えていただけないでしょうか。 面倒くさかったら無視してください^-^

  • colhan
  • ベストアンサー率31% (201/631)
回答No.5

題意と違うかも知れませんが。 For ~ Next文は、とっても強力で、 中の処理が終了するまで、他のWindowsの処理にすら移りません。 この中に重い処理を入れると、再描画が掛からなくなり、ユーザーフォームが真っ白になったりします。

19746999
質問者

お礼

colhanさん ご回答ありがとうございました。 >題意と違うかも知れませんが。 とんでもない・・・ とても参考になりましたよ^-^ もうこれ以上良い回答は頂けないかと思ってましたが 待ってみるもんですね♪

  • imogasi
  • ベストアンサー率27% (4737/17068)
回答No.4

表題の>DoEvents関数って何? について、DoEventsは関数に分類されず、「ステートメント」に分類されています。 VBAの解説書などでは、 オブジェクト プロパティ メソッド ステートメント 関数 に分類されています。念のため。 意味はWEBで照会してください。 ーー DoEventsもOKWAVEで質問して、聞くのも良いが、WEB照会したら、働きの説明も、実例も多数出てきますよ。

19746999
質問者

お礼

imogasiさん ご回答ありがとうございました >DoEventsもOKWAVEで質問して、聞くのも良いが、WEB照会したら、働きの説明も、実例も多数出てきますよ。 ・・・本当だいっぱい出てきた。 教えていただいてありがとうございます。。。

回答No.3

Windowsはイベント-なにかが起きることー (しかし、それは予期せぬものではなく約束されたもの) が発生することにより動作します。 たとえばキーが押されたとか、時間が来たとか。 CPUは1っこしかないのでタイムシェアーでいろいろな プログラムがイベントを発生させて動いています。 しかし、あなた(ユーザー)がそのことを考えずにプログラミング すると、他のプログラムは自分の番が来ないので動けなくなり、 画面が真っ白になったままになったりするわけです。 最初のコードはCPUを占領します。(そうなっちゃう) 2番目のコードはDoEvents のところで一回処理を止め、 (windows に渡し)次に自分の番がきたときに、 次のステップを続けます。 当然処理速度は桁違いに遅くなります。

19746999
質問者

お礼

otto0001ottoさん わかりやすいご回答ありがとうございました。 VBAもまだまだ未熟者ですが パソコン全体の事はもっとド素人です。 もっとパソコンについてオールラウンドな 知識を身につけることが大切だと思いました。 これからも日々勉強を怠らずに頑張ります♪ また何かわからないことがあったら よろしくお願いします。

  • hana-hana3
  • ベストアンサー率31% (4940/15541)
回答No.1

For文を使う必要があるのでしょうか? Range("A1:A5000).ClearContents 一瞬で終わるはずですよ。

19746999
質問者

お礼

hana-hana3さん おはようございます。 ご回答ありがとうございます。 そうか!clearContentsメソッドの存在をすっかり忘れてました。 質問以外のことも教えていただいてどうもありがとうございます。 これからもよろしくお願いいたします。

関連するQ&A

  • VB6 DoEventsの代わりは?

    お世話になります。 progressbarを表示するために、 重たい処理のループの中ではDoEventsを入れることで表示ができているのですが、 ループとループの間でprogressbarを更新するには、どのようにしたらよろしいでしょうか? ***** 現状 for i =1 to 9999 重い処理 Frm_ProgressBar.ProgressBar1.value = 進捗数 DoEvents next i ***** やりたいこと for i =1 to 9999 重い処理A next i Frm_ProgressBar.ProgressBar1.value = 進捗数 DoEvents for i =1 to 9999 重い処理B next i

  • VBA Progress barが動作しない

    VBA初心者です Progress barを追加したいのですが、上手く動きません というのは、 おそらく記述位置が間違っていると思われますので、 どこにどうやったら良いか教えて頂けませんか? 使用ソフト:Excel2003(32bit) i-1行目まで数える際に、Progress barを動作させたい ※Userform1にProgress bar2を追加 Sub progress_bar () Dim i As Variant Dim k As Variant Userform1.Show False DoEvents For k = 1 To i Userform1.ProgressBar2.Value = k / i * 100 '処理件数 / 総件数 * 100 'i-1行目までで必要な行(0かつブランク)を1、不要な行(それ以外)を2とフラグを付ける For i = 2 To 30000 If Cells(i, 5) = "" Then Exit For If Cells(i, 13) = 0 And Cells(i, 26) = "" Then Cells(i, 81) = 1 Else: Cells(i, 81) = 2 End If Next Next Userform1.Hide End sub

  • VBA DoEvents関数の働きと使い方を知りたい

    下記のような UserForm上の Module コードを書いてももらったのですが、DoEvents の働きが分からないのです。どなたか分かりやすく説明していただけませんでしょうか? Private i As Integer Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean) If Me.TextBox1.Value = Me.Label1.Caption Then Me.Label2.Caption = "正解です" Else Me.Label2.Caption = "不正解です" End If DoEvents If i < 20 Then i = i + 1 Label_Up Me.TextBox1.Value = "" Cancel = True Else MsgBox "終了です" End If End Sub Private Sub UserForm_Initialize() i = 1 Label_Up End Sub Private Sub Label_Up() Me.Label1.Caption = Sheets("Sheet1").Range("A1:A20").Cells(i).Value DoEvents End Sub

  • VBAでUserFormでProgressBarとLabelを同時表示できない理由は?

    VBAでUserFormをつかってProgressBarとLabelを同時に表示させる。つもりでしたが、ProgressBarが満たされた後Labelが表示されます。その理由と対策を教えて下さい。そのコードを以下に示します。 Sub a() With UserForm1 .Show vbModeless .Label1 = "始めのテキスト" End With s = 1 e = 20000 For i = s To e UserForm1.Label1 = "始めのテキスト" UserForm1.ProgressBar1.Value = i / e * 1000 Next i End Sub お願いします。

  • VBAのDoEventsが上手く動きません

    お世話になります。 ExcelのVBAで印刷処理をしているのですが、印刷枚数が多いのでDoEventsイベントを入れ、印刷中断処理を行いたいのですが、上手くできません。 印刷中ダイアログが表示されるのが原因なのでしょうか?それともコードの書き方が悪いのでしょうか?よろしくお願いします。 コードは以下のとおりです。 ************************************************ Public Can_flg As Boolean ************************************************ Private Sub CommandButton1_Click()   Can_flg = True End Sub ************************************************ Private Sub UserForm_Activate()   Dim ms As String   Dim j As integer   Can_flg = False   For j = 1 To 31    DoEvents    If Can_flg = True Then      ms = MsgBox("印刷を中止します。", vbOKCancel)        If ms = vbOK Then         Exit For        Else         Can_flg = False        End If    End If    Me.Label1.Caption = "印刷中です… (" & j & "/" & i & "ページ)"    Sheets("テスト").PrintOut   Next j   Unload Me End Sub

  • Doevents をご教授ください。

    VBでシリアルプリンタを制御するアプリケーションを開発しています。 プリンタに状態(ヘッドが開いているとかリボンがないとか)を問い合わせる関数をTimer1_Timer()で呼び出しています。 その関数内、プリンタに問い合わせてからの応答待ちにDo~Loopを使っています。 そのループ内にDoeventsがあります。 プリンタとPCを繋げていると起こらないのですが、繋げてないと、 (1)アプリケーションを終了してもタスクマネージャを覗くとプロセスが残っている (2)デバグしてみるとform.unload()時にTimer1.enabled=falseにしているのにform.unload()が終わるとTimer1.enabled=trueになってしまう (3)(2)の後、Timer1_Timer()に記述したプリンタの状態チェックする関数内のDoeventsに移動して無限ループになってしまう というような現象が起こります。 多分Doeventsの使い方が悪いと思われます。 これを回避する方法を教えてください。

  • エクセルVBAでEvaluate関数で指定子を使う

    テーブル設定された表があります。 表には数字や数式が入っているのですが、 運用しているうちに数式が増え、再計算が時間が長くてストレスになってます。 時間を短くするため、VBAを使用して数式の計算結果と同じ値を設定(“値のみ貼り付け”と同じこと)をして数式を減らそうとしています。 ただし、数式は今後も追加・変更されるため、 そのときのVBAソースコード修正の手間を最小限にしたいという事情もあります。 まずは 【ソースコード1】 For i = 1 To 10000 Cells(i, 金額列).Value = Evaluate("[@単価]*[@数量]") Next i このようなコードを書いたのですが、エラー値になってしまいます。 Evaluate関数で指定子(角かっこ)は使えないのでしょうか。 使えないなら別の方法として、 数式を設定した後で、値をもう1度代入する方法を考えてます。 【ソースコード2】 For i = 1 To 10000 Cells(i, 金額列).Formula = "[@単価]*[@数量]" Next i Range("A1:Z10000").Value = Range("A1:Z10000").Value ’計算結果だけをもう1度代入して数式を消す 他に良いやり方があったら教えてください。

  • DoEvents

    VBSでDoEventsは使えないのでしょうか? *************************** Dim ObjIE dim i Set ObjIE = CreateObject("InternetExplorer.Application") ObjIE.Navigate "http://oshiete.goo.ne.jp/" ObjIE.Visible = True Do While ObjIE.Busy = True DoEvents Loop Do While ObjIE.Document.ReadyState <> "complete" DoEvents Loop Set ObjIE = Nothing *************************** だと、エラーになりました。 Wscript.sleep 3000 ならエラーにならずにコードは動きました。 VBAならDoEventsは使えるのに VBSで使えない理由を教えてください。

  • モードレスボタンを押す事により処理を中断する。

    初めまして、以下のVBAを組みました。メインプロシージャは以下の通りです。処理時間が長いので、途中でボタンを押す事により、中断したいと思っております。しかし、ボタンおしても止まりません。何卒ご教授の程宜しくお願い申し上げます。 c = 0 g = 0 UserForm.Show vbModeless UserForm.Repaint flag = False For y = kk To ll For x = ii To jj If ws1.Cells(y, x).Value = ws2.Cells(y, x).Value Then Else g = g + 1 c = c + 1 ws2.Cells(y, x).Interior.ColorIndex = 3 ws3.Cells(g, "A") = Cells(y, x).Address(RowAbsolute:=False, ColumnAbsolute:=False) End If DoEvents If flag Then Exit For Next x If flag = True Then GoTo ErrorHandler: Next y flag = False Unload UserForm Exit Sub ErrorHandler: flag = False Unload UserForm MsgBox "エラーで終了しました。" End Sub ユーザーフォームは以下の通りです。 Private Sub CommandButton1_Click() flag = True End Sub 当然flagはメインプロシージャの前に定義しております。 以上、宜しくお願い申し上げます。

  • プログレスバーでの経過状況表示

    vb.netでのtimerのようなものをやりたくてvbaでプログレスバーを使用して経過状況を表示するプログラムを作りました。プログレスバー自体での視覚的な経過状況表示はできたのですが、現在のパーセンテージをlabelに表示することができません。 Private Sub CommandButton4_Click() Dim i As Long Application.Visible = False i = 1 For i = i To 1000000 UserForm1.ProgressBar1.Value = i / 1000000*100 UserForm1.Label1.Caption =UserForm1.ProgressBar1.Value i = i + 1E-44 Next Application.Visible = True End Sub 上記のようにするとプログラム終了時にlabel1に現在のプログレスバーの値が表示されますが、進行中には表示されないのです。これを進行中も表示させるにはどうしたら良いのでしょうか?