1

私はこの同様の問題に遭遇し、返信を読みました: Modeless form that still pause code execution

私は、 David Zemensの提案を自分の状況に適用しようと試みてきました。私の状況では、GoTo も利用せずに Zemen 氏の提案を取り入れたアプローチを見つけることができないようです。

より良い、またはよりエレガントなソリューションがあるかどうか疑問に思っています。

ここに私がやっていることの概要があります:

複数の Excel ワークブックでいくつかのアクションを実行するコードの実行を開始するコマンド ボタンを持つユーザー フォームがあります。このように、多数のコード ブロックがあり、1 つのコード ブロックが正常に完了すると、後続のコード ブロックの実行が可能になります。

ある時点で、状況によっては、コードにユーザー入力が必要になる場合があります。その他の状況では、必要なデータは Excel から取得できます。ユーザーからの入力が必要な場合は、別のユーザー フォームが表示されます。

ユーザーは、入力を入力する前にいくつかの異なる Excel シートを表示する必要がある場合があるため、UserForm はモードレスです。そのため、ユーザーが必要な入力を入力して別のコマンド ボタンをクリックするまで、コードは停止します。

この時点で、プログラムフローを再開する方法に問題があります。「中断したところから再開する」唯一の方法は、GoTo ステートメントを使用することですか? または、モジュールを編成して、1 つの場所で定義され、ユーザー入力が必要になる可能性のあるポイントから重複しない単一の一貫したプログラム フローが存在するようにする方法はありますか?

4

2 に答える 2

2

ここに問題に対する私の見解があります。問題を正しく理解できたことを願っています。

仮定:

  1. 2 つのユーザー フォームがあります。
  2. 処理を開始するボタンを持つ UserForm1。
  3. 中間入力を提供するボタンを備えた UserForm2。
  4. UserForm1 を開始/起動するためのモジュール内のサブ。

VBA コード (サブルーチン用)

Sub LaunchUserForm1()
    Dim frm As New UserForm1

    '/ Launch the main userform.
    frm.Show vbModeless
End Sub

VBA コード (UserForm1 用)

Private Sub cmdStart_Click()
    Dim i       As Long
    Dim linc    As Long
    Dim bCancel As Boolean
    Dim frm     As UserForm2

    '/ Prints 1 to 5 plus the value returned from UserForm2.

    For i = 1 To 5

        If i = 2 Then
            Set frm = New UserForm2
            '/ Launch supplementary form.
            frm.Show vbModeless

'<< This is just a PoC. If you have large number of inputs, better way will be
' to create another prop such as Waiting(Boolean Type) and then manipulate it as and when User
' supplies valid input. Then validate the same in While loop>>

            '/ Wait till we get the value from UserForm2.
            '/ Or the User Cancels the Form with out any input.               
            Do While linc < 1 And (linc < 1 And bCancel = False)
                linc = frm.Prop1
                bCancel = frm.Cancel
                DoEvents
            Loop

            Set frm = Nothing
        End If

        Debug.Print i + linc
    Next

    MsgBox "User Form1's ops finished."

End Sub

VBA コード (UserForm2 用)

Dim m_Cancel        As Boolean
Dim m_prop1         As Long

Public Property Let Prop1(lVal As Long)
    m_prop1 = lVal
End Property

Public Property Get Prop1() As Long
   Prop1 = m_prop1
End Property

Public Property Let Cancel(bVal As Boolean)
    m_Cancel = bVal
End Property

Public Property Get Cancel() As Boolean
    Cancel = m_Cancel
End Property

Private Sub cmdlinc_Click()
    '/Set the Property Value to 10
    Me.Prop1 = 10
    Me.Hide
End Sub

Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    '/ Diasble X button

    Me.Cancel = True
    Me.Hide
    Cancel = True

End Sub
于 2016-07-11T21:25:07.327 に答える
0

わかりましたので、ここに私の考えがあります。

frmSelectUpdateSheetシートをプログラムで決定できない場合に、ユーザーがシートを選択できるようにするために使用したいユーザーフォームがあります。問題は、.Show vbModeless(ユーザーがワークシートをナビゲートできるようにする) 場合、コードが実行され続け、エラーや望ましくない出力が発生することです。

前の回答で説明した方法を適用することは可能だと思います。ただし、すべてのコードをリバースエンジニアリングするために私にお金を払っていない限り、それはここでは問題外です:P

この時点で割り当てる必要があるWorksheetオブジェクト変数 (またはシート名を表す文字列など) がある場合 (およびこの変数がPublicスコープ内にある場合)、フォームの CommandButton を使用して、選択した変数に基づいてこれを割り当てます。frmSelectUpdateSheetリストボックスの項目。

これはおそらく、次のような多くの理由から優れたアプローチです (この種の特殊なケースでのアプリケーションの再設計を回避しようとしていることが少なくとも 1 つではありません)。

  • これにより、フォームが保持vbModalされ、プロセス中にユーザーがワークシートを誤って改ざんすることを防ぎます。

  • このアプローチを使用すると、スレッドはvbModal表示されたままfrmSelectUpdateSheetになり、フォームのイベント プロシージャに依存してプロセス フロー/コード実行を制御します。

  • 実装がより簡単になるはずです(したがって、安価になります)。自社で行うか、外注するか。

  • 維持するのがより簡単になるはずです (したがって、より安価になります)。

現在、詳しく調べてみると、cmdbtnSelect_Clickイベント ハンドラーでこの種のアプローチを既に行っているように見えます。これにより、関連/フォローアップの問題があると思われます。

シート名 (リストボックス内) は、ユーザーが正しいワークシートを識別するのに十分ではありません。したがって、ユーザーがシートを「スクロール」する機能が必要な場合(たとえば、ウィンドウに収まらないデータを確認する場合など)、スピナー ボタンまたはその他のフォーム コントロールを追加して、シートをナビゲートできるようにします。

于 2016-07-16T03:46:32.877 に答える