質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.49%

「お待ちください」表示用フォーム モーダルウィンドウの上にモードレスフォーム

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 5,885

score 21

よろしくお願いいたします。

Excelでの質問です。
シート上にコマンドボタンを配置して、それをクリックすることで、様々な計算等の一連の処理を行なうように作りました。
そこまで来て、その処理に時間がかかるのが気になったため、「処理中です。手をふれないでそのまま待っててください」と表示させるためのフォーム(F_wait)を作り、処理開始から処理終了まで表示させることとしました。
処理の開始時に
F_wait.show
とだけ書くと、その時点で処理が中止されてしまうことがわかり、自分なりに調べたら

F_wait.show vbmodeless


と書けば、処理が中断せずに進行すると知り、そのようにして上手くいきました。

次に、同ファイルの別の場所での機能なのですが、あるモーダルフォームF1を表示させていて、そのF1上にあるコマンドボタンにも、
F1上のテキストボックスF1.txt1に入力されている値を使って、計算したり印刷したりシートのコピーを別ファイルとして保存したりといった、
一連の処理を作成しました。
この処理も処理に時間がかかるため、F_waitを表示させようとしたのですが、今度は
「モーダルフォームが表示されているときは、モードレスフォームを表示できません」とエラーが出て来てしまいます。

仕方がないからと、処理スタート時に、一旦

unload F1
F_wait.show modeless


とするわけにはいきません。
実行したい処理において、F1上のテキストボックスの値を参照したいからです。
とはいえ、F1の上にF_waitをモーダルでshowすると処理が止まってしまいます。

パッと思いつく対策は、F1上のテキストボックス群の諸値を変数に持ち、F1がunloadされた後でもその変数を用いて処理を続行する、ということなのですが、
しかし、この方法は、あまりスマートじゃない感じがするのと、
見た目的に、大きなF1がクリック後も表示されたままその中央あたりに小ぶりなF_waitが重なるように表示されることで、使用者にとってちゃんと何らかの処理がされているんだなという印象を持たせることができそうなので、そうしたいです。
デモデモダッテになりますが、だからといって、F_waitは、他の処理や今後の機能拡張でも使い回したいので、F_wait自体に処理のコードを書くもの避けたいです。

こういう状態でグダグダとしているのですが、何かいい方法はないでしょうか?
「お待ちください」表示フォームのことをまったく考えないでほとんどの処理を書いてしまったのがまずかったとは思ってるのですが、どうしたもんでしょう?

経験豊富な方はどうしておられるのか教えてください。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

モーダルのフォームからはモーダルしか呼び出せないというのは、処理に一貫性を持たせるための仕様なのでそれは尊重すべきだと思います。
モーダルで実行しているときは、他の処理は実行できない、ユーザーも処理が終わるまでは何もできないという仕様ということなので。

また、F_wait を他に処理にも使いまわしがきくようにしたいということなので、
F_Wait から他の処理(プロシージャ)を呼び出せるように設計すればどうでしょうか。
標準モジュールに記述したプロシージャなら、Application.Run
ユーザーフォームやシートモジュールに記述したプロシージャなら CallByName で呼び出せます。

F_waitのモジュール

Option Explicit
Public ProcName As String
Public obj As Object
Public msg As String

Private Sub UserForm_Activate()
    Me.Label1.Caption = msg
    If Not obj Is Nothing Then
        CallByName obj, ProcName, VbMethod
        Unload Me
    ElseIf ProcName <> "" Then
        Application.Run ProcName
        Unload Me
    End If
End Sub

F1 のモジュール

Option Explicit

Public Sub FormProc()
    MsgBox "FormProc"
End Sub

Private Sub CommandButton1_Click()
    Load F_Wait
    With F_Wait
        .ProcName = "FormProc"
        Set .obj = Me
        .msg = "FormProc を実行中です。しばらくお待ちください。"
        .Show
    End With
End Sub

Private Sub CommandButton2_Click()
    Load F_Wait
    With F_Wait
        .ProcName = "ModuleProc"
        .msg = "ModuleProc を実行中です。しばらくお待ちください。"
        .Show
    End With
End Sub

標準モジュール

Option Explicit

Public Sub ModuleProc()
    MsgBox "ModuleProc"
End Sub

改良案

上記のコードは、F_Wait の呼び出しがちょっと面倒です。呼び出しをメソッド(Subプロシージャ)にするとシンプルに呼び出せます。

F_waitのモジュール

Option Explicit
Private sProcName As String
Private sobj As Object

Public Sub Start(ProcName As String, Optional msg As String, Optional obj As Object)
    sProcName = ProcName
    If Not obj Is Nothing Then Set sobj = obj
    Me.Label1.Caption = msg
    Me.Show vbModal
End Sub

Private Sub UserForm_Activate()
    If Not sobj Is Nothing Then
        CallByName sobj, sProcName, VbMethod
    ElseIf sProcName <> "" Then
        Application.Run sProcName
    End If
    Unload Me
End Sub

F1 のモジュール

Private Sub CommandButton1_Click()
    F_Wait.Start "FormProc", _
                 "FormProc を実行中です。しばらくお待ちください。", _
                 Me
End Sub

Private Sub CommandButton2_Click()
    F_Wait.Start "ModuleProc", _
                 "ModuleProc を実行中です。しばらくお待ちください。"
End Sub


F_Wait.Start とメソッドを呼び出すことで、F_Wait は自動でLoadされます。あとは、引数で、プロシージャ名等を渡せばいいだけです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/02/26 01:46

    ご回答ありがとうございました。
    まだ完全には理解できていませんが、私の希望にぴったりの方法だということはわかります。
    何を我慢しなければいけないのか教えてもらうくらいの気でいたので、ここまで完璧な方法があるとは望外の喜びです。
    とても勉強になりました。
    ありがとうございました。

    キャンセル

+1

プログレスバー(お待ちください的なもの)は、
基本的に面倒なのでやりません。
※面倒な割に大して評価されませんし、
その上今回のようにメイン処理に悪影響を及ぼすことが多いので。

自分は、Application.Statusbar を使用します。
※最初と最後にクリア(Application.Statusbar = False)するようにします。

Excelが応答無しになってしまうと、もちろん経過は分かりませんが、
それはExcel自体の仕様で、何をやっても経過は分かりませんので、
使用者に、それでも動いているから、と教えます。

応答無しを回避する為には、DoEvents等を使います。
https://tsware.jp/tips/tips_114.htm

ご参考まで。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/02/25 09:28

    キャンセル

  • 2019/02/26 01:48

    ご回答ありがとうございました。
    ステータスバーのこと、参考になります。
    ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.49%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る