Excel (VBA)

Excel VBAに関するフォーラムです。
  • 解決済みのトピックにはコメントできません。
このトピックは解決済みです。
質問

 
(Windows 10 Home : その他)
ListBox Click()の異状動作
投稿日時: 24/02/21 17:45:35
投稿者: o_taroh

Private Sub ListBox1_Click()
    ActiveCell = Me.ListBox1.Value
    Me.Hide
    End
End Sub
 
上記のプロシージャの動作で悩んでます、Userform1.showで自走してしまい Listが表示されずActiveCellに不本意なリストを選択表示して終了してしまいます、即ちListを選択することができません。
 
どこかのステップ(例えばInitializeのEnd Sub等に)にブレークポイントを設定してステップ実行するとList表示して正常に動作します。また上記のプロシージャの Endの箇所にブレークポイントを設定しても次のステップで正常動作をして終了します、どうすればいいのでしょうか?

回答
投稿日時: 24/02/21 18:00:07
投稿者: WinArrow

取り敢えず
END
行は不要です。削除してください。
  
  
Private Sub UserForm_Initialize()
  
End Sub
内のコードを掲示してください。
 

投稿日時: 24/02/21 20:04:59
投稿者: o_taroh

Private Sub UserForm_Initialize()
    Dim r As Integer, re As Integer
    re = Sheets("P1").Range("AI5").End(xlDown).Row
    Me.Left = 320.95
    Me.Top = 309.75
    With Me.ListBox1
        For r = 5 To re
            .AddItem Sheets("P1").Cells(r, 35)
        Next r
    End With
End Sub
上記です、Shee"P1"に文字列のデータがありステップ実行すると正常に表示されますます、よろしくお願いします。                

回答
投稿日時: 24/02/21 20:43:03
投稿者: WinArrow

追加依頼。
 
掲示頂いたコードでは、異常はみられません。
 
Private Sub UserForm_Activate()
プロシジャがありましたら、
コードを掲示してください。
 
ユーザーフォームを開くところのプロシジャのコードを教えてください。

投稿日時: 24/02/22 08:13:48
投稿者: o_taroh

ご検討ありがとうございます。
Private Sub UserForm_Activate()
は使っていません。

回答
投稿日時: 24/02/22 09:24:37
投稿者: WinArrow

引用:
Private Sub UserForm_Activate()
は使っていません。

そうですか?
 
引用:

また上記のプロシージャの Endの箇所にブレークポイントを設定しても次のステップで正常動作をして終了します、どうすればいいのでしょうか?

掲示のコードだけで、解析することは無理です。
 
問題としているプロシジャに記述の
>End
は、不要というより、普通は、存在すること自体が、イレギュラーです。
>End
は、意図的にプロロラムを強制終了させる目的で使用するものと思います。
どのような目的で記述したのか分かりませんが、
 Endの箇所にブレークポイントを設定し
>End
を削除し、「F8」でどこに進む(または、もどる)かを検証してみてください。

回答
投稿日時: 24/02/22 11:16:22
投稿者: WinArrow

前レスで

引用:

 Endの箇所にブレークポイントを設定し
>End
を削除し、「F8」でどこに進む(または、もどる)かを検証してみてください。

と書きましたが、
 
ブレ―ポイントを設定せずに
End をStop
に変更し「F8」でどこに進む(または、もどる)かを検証してみてください。
 
 

回答
投稿日時: 24/02/22 13:00:20
投稿者: O.M

別の要素が絡んでいそうなので根本的な解決にはならないと思いますが、
フラグを立ててイベント抑制をすれば回避はできる気がします。
 
※私がスレッドをたてて質問しています件はエラーになったりなかなか進んでおらず、
煮詰まって合間にこちらを覗いて気になったのでかいてしまいました…すみません。
 

Private InitializeFlg As Boolean

Private Sub UserForm_Initialize()
    Dim r As Integer, re As Integer
    InitializeFlg = False
    re = Sheets("P1").Range("AI5").End(xlDown).Row
    Me.Left = 320.95
    Me.Top = 309.75
    With Me.ListBox1
        For r = 5 To re
            .AddItem Sheets("P1").Cells(r, 35)
        Next r
    End With
    InitializeFlg = True
End Sub

Private Sub ListBox1_Click()
    If InitializeFlg = False Then Exit Sub
    ActiveCell = Me.ListBox1.Value
    Me.Hide 'ユーザーフォーム非表示
    Application.Wait [Now()] + 500 / 86400000  '500ミリ秒待機
    Me.Show vbModeless  'ユーザーフォーム表示
    'ユーザーフォームを終了させる場合は
    'Unload Me
End Sub

回答
投稿日時: 24/02/22 14:58:15
投稿者: simple

引用:
Userform1.showで自走してしまい Listが表示されずActiveCellに
不本意なリストを選択表示して終了してしまいます

それなら、ListBox1_Clickはやめて、
別途コマンドボタンを作っておき、
ListBoxの選択を実行後、ご自分の選択内容をしっかり目視してから、
コマンドボタンを押すようにしたほうが明確じゃないですか?

投稿日時: 24/02/22 15:33:07
投稿者: o_taroh

WinArrowさん、O.Mさんご検討ありがとうございます
Private Sub ListBox1_Click()
    Stop
    Application.EnableEvents = False
    ActiveCell = Me.ListBox1.Value
    Application.EnableEvents = True
    Me.Hide
End Sub
Stopをこの位置に書いて実行すると確かにリストが表示されるのですが既に1つのリストが選択された状態になっています、そこで新たに別なリストを選択すると見た目には選択リストは変わりますが、そのあと実行すると
ActiveCellには最初に選択されていた違うリストが表示されます、Initializeで既選択状態をクリアする方法があるでしょうか、Endは取りました。
 
O.Mさんが書いてくれたプログラムを実行すると選択状態にはなりますがクリックしても反応しません、但しリストは無選択の状態です。
 
目下のところDouble Clickで凌いでいますが、できれば One Clickでいきたいと思っています。
 

回答
投稿日時: 24/02/22 15:53:51
投稿者: WinArrow

私は、Stopの目的を、
End Sub 後、どこに進む(戻るか)を検証するよう
お願いしたはずです。
 
もう一つ、
ユーザーフォームをひらいているプロシジャのコードの掲示を
お願いしてあります。

投稿日時: 24/02/22 16:03:58
投稿者: o_taroh

O.Mさん申し訳ありません私のUnload Meを戻すのを忘れていたミス操作でした、正常に実行されますありがとうございました。
If InitializeFlgのこと勉強し直します。

回答
投稿日時: 24/02/22 16:24:09
投稿者: WinArrow

Private Sub ListBox1_Click()
の中に
Ne,Hide
で、Userform1を表示にしています。
 
このままでは、Userform1が、メモリに残っています。
でも、
End
で、プログラムを強制終了しているから、Userform1を含めて、メモリはクリアされています。
 
Endを削除すると、当然のことながら、Userform1は、メモリに残っています。
次に
Useform1.Show
で開いたときには、Initializeプロシジャはスキップされます。
 
どのような動きになるのか?他のコードが掲示されていないから、分かりません。
 
「Hide」で、ユーザーフォームを抜けた時の後始末処理はご存知ですか?
 

投稿日時: 24/02/22 16:41:54
投稿者: o_taroh

WinArrowさん私の勘違いのようでした、申し訳ありませんでした。
 
O.Mさんの示してくれたコードも最初はやはりリストが1つ選択された状態でFormが表示されます、それが何故なのかよくわかりません。選択無し状態にするにはどうすればいいのか教えていただくとありがたいです、
選択されているリストをクリックしても無反応です、もしそれが希望する選択リストだったら戸惑います。
 
いまのFormを表示する前に複数用意してあるFormのうちの1つを選択するため別なFormを使い、やはりクリックしたリストを使いSelect Caseで問題のFormを選んでいます、それがミスする原因なのでしょうか?

回答
投稿日時: 24/02/22 17:26:23
投稿者: WinArrow

ユーザーフォームを開くと、
Listbox1
が選択されてる

このような症状は、普通にはありえません。
「ユーザーフォームを開く」に関連するプロシジャのコード掲示をお願いしています。
 
こちらで、無理やり、リストボックスを選択するコードを作成してみました。
 

Sub test()
    UserForm1.ListBox1.ListIndex = 0
    UserForm1.Show
End Sub

 
どこかに
ListBox1.ListIndex = 0
のようなコードありませんか?

回答
投稿日時: 24/02/22 17:56:42
投稿者: WinArrow

ついでに、余計なアドバイス
 
操作性を考慮した場合、
Listbox1_Clickイベントは、
誤操作を伴うため、お勧めできません。
かわりに、コマンドボタンを使います。
 
それから
ActiveCellは、乱暴です。
しかるべき、シート&セルかを、チェックしなくてよいのですか?
 
 
 
 
 

回答
投稿日時: 24/02/22 18:42:25
投稿者: WinArrow

投稿日時: 24/02/22 16:24:09投稿者: WinArrow
の入力ミス訂正

引用:

Private Sub ListBox1_Click()
の中に
Ne,Hide
で、Userform1を表示にしています。
 

正しくは
Private Sub ListBox1_Click()
の中に
Ne,Hide
で、Userform1を非表示にしています。

回答
投稿日時: 24/02/22 21:44:26
投稿者: O.M

ListBox1.ListIndex = -1

としている部分でPrivate Sub ListBox1_Click()のイベントが動いてループしてしまっていないでしょうか。
 
もしくは
 
    Application.ScreenUpdating = False
    Application.EnableEvents = False

 
としてTrueに戻していないなどが思い浮かびました。
 
何を行いたいのかわかっていないのですが
UserForm1にコマンドボタンを1個配置
UserForm2にリストボックスを配置
 
'UserForm1のモジュール
Private Sub CommandButton1_Click()
  Me.Hide
  UserForm2.Show vbModeless
End Sub

 
'UserForm2のモジュール パターン1
'選択行が青くならない
Private InitializeFlg As Boolean

'×マークでの終了した場合、UserForm1を再度表示
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
  If CloseMode = 0 Then  '×マークで閉じようとした場合
    Application.ScreenUpdating = False
    Application.EnableEvents = False
    Unload Me
    UserForm1.Show vbModeless
    Application.ScreenUpdating = True
    Application.EnableEvents = True
  End If
End Sub

Private Sub UserForm_Initialize()
    Dim r As Integer, re As Integer
    InitializeFlg = False
    re = ThisWorkbook.Sheets("P1").Range("AI5").End(xlDown).Row
    Me.Left = 320.95
    Me.Top = 309.75
    With Me.ListBox1
        For r = 5 To re
            .AddItem ThisWorkbook.Sheets("P1").Cells(r, 35)
        Next r
    End With
    InitializeFlg = True
End Sub

Private Sub ListBox1_Click()
    If InitializeFlg = False Then Exit Sub
    Me.Hide
    Application.ScreenUpdating = False
    Application.EnableEvents = False
    ActiveCell = ListBox1.Value
    Application.Wait [Now()] + 800 / 86400000  '800ミリ秒待機
    Unload Me
    UserForm2.Show vbModeless
    Application.EnableEvents = True
    Application.ScreenUpdating = True
End Sub

 
'UserForm2のモジュール パターン2
’選択した行が青くなって再表示にもそのまま
Private InitializeFlg As Boolean

'×マークでの終了した場合、UserForm1を再度表示
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
  If CloseMode = 0 Then  '×マークで閉じようとした場合
    Application.ScreenUpdating = False
    Application.EnableEvents = False
    Unload Me
    UserForm1.Show vbModeless
    Application.ScreenUpdating = True
    Application.EnableEvents = True
  End If
End Sub

Private Sub UserForm_Initialize()
    Dim r As Integer, re As Integer
    InitializeFlg = False
    re = ThisWorkbook.Sheets("P1").Range("AI5").End(xlDown).Row
    Me.Left = 320.95
    Me.Top = 309.75
    With Me.ListBox1
        For r = 5 To re
            .AddItem ThisWorkbook.Sheets("P1").Cells(r, 35)
        Next r
    End With
    InitializeFlg = True
End Sub

Private Sub ListBox1_Click()
    If InitializeFlg = False Then Exit Sub
    Me.Hide
    Application.ScreenUpdating = False
    Application.EnableEvents = False
    ActiveCell = ListBox1.Value
    Application.Wait [Now()] + 800 / 86400000  '800ミリ秒待機
    Me.Show vbModeless
    Application.EnableEvents = True
    Application.ScreenUpdating = True
End Sub

 
 
 
 
Private Sub ListBox1_Click()の内部に
ListBox1.ListIndexの指定をいれてイベントがループする場合は、
ListBox1.ListIndexの指定の前にInitializeFlg = Falseとしておくと、
ループを若干短くはできると思います。
InitializeFlg = FalseでExitで終わらせてしまった場合、
動かなくなるのでTrueに戻すコードも足しました。
 
Private Sub ListBox1_Click()
    If InitializeFlg = False Then
      InitializeFlg = True
      Exit Sub
    End If
    Me.Hide
    Application.ScreenUpdating = False
    Application.EnableEvents = False
    ActiveCell = ListBox1.Value
    Application.Wait [Now()] + 800 / 86400000  '800ミリ秒待機
    Me.Show vbModeless
    Application.EnableEvents = True
    Application.ScreenUpdating = True
    InitializeFlg = False
    ListBox1.ListIndex = -1
End Sub

回答
投稿日時: 24/02/22 22:19:04
投稿者: O.M

昔ユーザーフォーム3つくらいを行き来するコード作る際にめちゃくちゃ悩んだので
もしかして同じ場所で悩んでないかなと思って追記します。
 

'標準モジュールに記入
Public UfForm As MSForms.UserForm 'UserForm
Public UFInitializeFlg As Boolean 'UserForm起動フラグ

 
 
'ユーザーフォームモジュール
Private Sub UserForm_Initialize()
Set UfForm = Me
End sub

 
のようにユーザーフォームを変数にいれることもできます。
ユーザーフォームを複数同時に起動して使用する操作に使える気はします。
 
 
 
 
あらかじめコードを作っておけば、
簡単な指定でコントロールの表示切替などが簡単な指定で行えるようになるので便利です。
※これに気が付くまでは表示切替がやたらと長く煩雑になってしまっていて悩みまくってました…。
 
 
'標準モジュールに記載
'コントロール使用禁止
Public Function ControlsEnabledFalse(UfForm As MSForms.UserForm, str As String, Ary As Variant)
  Dim i As Long
  For i = LBound(Ary) To UBound(Ary)
    UfForm.Controls(str & Ary(i)).Enabled = False
  Next
End Function

'コントロール使用禁止解除
Public Function ControlsEnabledTrue(UfForm As MSForms.UserForm, str As String, Ary As Variant)
  Dim i As Long
  For i = LBound(Ary) To UBound(Ary)
    UfForm.Controls(str & Ary(i)).Enabled = True
  Next
End Function

 
'使用例
'ユーザーフォームモジュール
Private Sub UserForm_Initialize()
  Dim Ary As Variant
  'チェックボックスの1,2,5,8,9を使用禁止
  Ary = Array(1, 2, 5, 8, 9)
  ControlsEnabledFalse Me, "CheckBox", Ary
  'チェックボックスの3, 4, 6, 7, 10を使用禁止解除
  Ary = Array(3, 4, 6, 7, 10)
  ControlsEnabledFalse Me, "CheckBox", Ary
End Sub

回答
投稿日時: 24/02/23 08:42:53
投稿者: simple

まずはこのあたりを参考に動作するものを真似してみることから始めたらいかがですか?
「リストボックスの使い方:Excel VBA入門」
http://www.eurus.dti.ne.jp/~yoneyama/Excel/vba/vba_userform03.html

回答
投稿日時: 24/02/23 10:26:14
投稿者: WinArrow

> Application.EnableEvents = False
 
このコードは、ユーザーフォームのイベントを抑止するためのものですか?
 
残念ながら、「Application」は、Excelです。
VBAではないので、無駄です。

回答
投稿日時: 24/02/23 10:44:43
投稿者: O.M

o_tarohさんの提示で

Private Sub ListBox1_Click()
    Stop
    Application.EnableEvents = False
    ActiveCell = Me.ListBox1.Value
    Application.EnableEvents = True
    Me.Hide
End Sub

 
とあったので、もしかして
セルをダブルクリックなどでユーザーフォーム起動
→起動したユーザーフォームのListBoxから別のユーザーフォームをクリックで選択して起動
→2個目に起動したユーザーフォームからアクティブセルに文字を記入
 
的なことをしていると想定してつけてました。
そういう意味じゃなかったらすみません。

回答
投稿日時: 24/02/23 11:26:17
投稿者: WinArrow

最初の質問説明

引用:
上記のプロシージャの動作で悩んでます、
Userform1.showで自走してしまい
 Listが表示されずActiveCellに不本意なリストを選択表示して

自走しない「Userform1.show」はあり得ません。
どこのルーチンを通って、どこまで処理されたかです
Listbox1_Clickが説明されているから、
Listbox1_Clickが処理されたと判断できます。
しかし、Listbox1_Clickは、クリックイベントですから、
listbox1のプロパティをセットしても、イベントはは発生しません。
手操作、もしくは、どこかで
Call Listbox1_Click
のようなコードが記述されていると推測して、
UserForm_Activate()の存在を確認確認しました。
UserForm_Activateは使用していないということです。
引用:

終了してしまいます、

は、[End]に起因します。
 
それはそれとして、
Listbox1_Clickイベントが発生する原因を突き止めるのが先決です。
1つの方法として
Listbox1_Clickをコメントアウトすることです。

投稿日時: 24/02/23 11:39:49
投稿者: o_taroh

皆さんを大変煩わして申し訳ありません、88才になる高齢者で昔Fortrannwo やったものでVBAにがじりついてなんとかできないものかと、四苦八苦していますので的外れの問題提供をお許しください。
 
考え方は
User Formを10個使い、1つを他の9つのFormを呼び出すのに使います、その他の9つのFormにはジャンル毎に文字列が複数個配置してあります、それらの文字列を指定したセルに移す操作をしたいのですが、使うときの状況は早さも求められますし操作する人もこれまた老人で操作が覚束ないのでOne Clickを考えた次第です。
 
最初のジャンルを選ぶFormは空白セルを選択したときか、または使用セルをDeleteしたときのイベントでOpenするようにしてあります。
 
プログラムもいろいろ悶えながら作ったもので煩雑過ぎて皆様に見てもらえるようなものではありませんが、この質問欄にはファイルを送ることが出来るのでしょうか?
 
皆さんの提供してくれたアイデアを復習させてもらい、もう一度考えてみます、今日は町の用事があり遅くなりますがまたよろしくお願いします。
 
 

回答
投稿日時: 24/02/23 12:03:22
投稿者: MMYS

Private Sub ListBox1_Click() にブレイクポイントを設定。
その後、意図しないブレイクポイントで停止時に、呼出履歴を確認しましょう。
 
呼出履歴の確認方法は、ブレイクポイントで停止時に
・Ctrl+L
・「表示」→「呼出履歴」
で、どのコートから呼ばれているかを確認しましょう。そして、リストから該当プロシージャを選べば、意図しないイベント発生元のコードが表示されます。
 
なお、すでに他の方のレスある通り、コードで選択値の設定でイベントは発生します。
そして、Application.EnableEvents も指摘通り、ユーザーフォームのイベントは対象外です。
 
対策は、自前でのイベントフラグなどが考えられますが、その前に、意図しないイベント発生場所の特定が先です。
 

回答
投稿日時: 24/02/23 16:59:28
投稿者: WinArrow

MMYSさんへの御礼
 
>「呼出履歴」
知らなかったです。
教えていただきありがとうございました。

投稿日時: 24/02/24 10:58:45
投稿者: o_taroh

実際のものは他の幾つかのプロシージャやModuleも混在しているのでそれらの影響が考えられるので全く別に、考えた部分だけを作ってみました。
Formを6つf1〜f6とします、f1はf2〜f6を呼び出すのに使います、BookのSheetsは3ページです
Sheet1のF列〜J列(例えばセルG4〜G10に「ABCD],「あいうえお」・・・等)の適当な数個の文字列を書き入れそれをC列の5行〜15行に取り出すのですがプログラムは次のようにかいてあります、E列のセルE4〜E8に「strA,strB,・・・strE」のように文字集団の呼び出す名前を入れてあります。
f1を呼び出すイベントはThisBookに次のように書きました
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
    Dim r As Integer, c As Integer
    r = Target.Row
    c = Target.Column
    If c = 3 Then
        If r >= 5 And r <= 15 Then
            UserForm1.Show
        End If
    End If
End Sub
Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
    Dim r As Integer, c As Integer
     
    r = Target.Row
    c = Target.Column
     
    If c = 3 Then
        If r >= 5 And r <= 15 Then
            UserForm1.Show
        End If
    End If
End Sub
 
f1のプログラムです
Private Sub UserForm_Initialize()
    Dim r As Integer, re As Integer
    re = Sheets("Sheet1").Range("E5").End(xlDown).Row
    With Me.ListBox1
        For r = 4 To re
            .AddItem Sheets("Sheet1").Cells(r, 5)
        Next r
    End With
    Me.StartUpPosition = 0
    Me.Left = 300.95
    Me.Top = 300.75
     
End Sub
Private Sub ListBox1_Click()
    Dim Dta As String
    Dta = Me.ListBox1.Value
    Me.Hide
    Select Case Dta
        Case "strA": UserForm2.Show
        Case "strB": UserForm3.Show
        Case "strC": UserForm4.Show
        Case "strD": UserForm5.Show
        Case "strE": UserForm6.Show
    End Select
End Sub
 
f2〜f6はほとんど同じですからf2ものを1つだけ掲示します
Private Sub CommandButton1_Click()
    Me.Hide
End Sub
Private Sub ListBox1_Click()
    Application.EnableEvents = False
    ActiveCell = Me.ListBox1.Value
    Application.EnableEvents = True
    Unload Me
End Sub
Private Sub UserForm_Initialize()
    Dim r As Integer, re As Integer
    re = Sheets("Sheet1").Range("F4").End(xlDown).Row
    With Me.ListBox1
        For r = 4 To re
            .AddItem Sheets("Sheet1").Cells(r, 6)
        Next r
    End With
    Me.StartUpPosition = 0
    Me.Left = 300.95
    Me.Top = 300.75
End Sub
 
これを実行したとき不可解なことはf2だけ正常に動作してf3〜f6はやはり自走してしまいうことです
厄介なことを提示してすいません、よろしくお願いいたします

回答
投稿日時: 24/02/24 12:22:39
投稿者: WinArrow

問題点
 
Me.Hide

Unload Me
を理由もなく混用しないこと
Me.Hide
では、当該ユーザーフォームがメモリ内に残っているので、
その時点で,Userform2を開くと
「Initialize」をスキップ(リストが設定されない&表示もされない)して、
いきなり「Listbox1_click」が実行されます。
 
今回の処理で[Me.Hide]は必要ないでしょう。
 
 
対策1
安全のため、Userform1.Initializeの最後に以下のコードを追加
本来不要ですが、あくまでも予防措置
 

    If Not UserForm2 Is Nothing Then
        Unload UserForm2
    End If
    If Not UserForm3 Is Nothing Then
        Unload UserForm3
    End If
    If Not UserForm4 Is Nothing Then
        Unload UserForm4
    End If
    If Not UserForm5 Is Nothing Then
        Unload UserForm5
    End If
        If Not UserForm6 Is Nothing Then
        Unload UserForm6
    End If

 
対策2
Userfrm2〜6
のCommandButton1_Click変更
Private Sub CommandButton1_Click()
    Unload Me
End Sub
 
対策3
Userform1のistbox1_clickの最後に
    Unload Me
を追加する
 
対策4:付録
Thisworknook内コードを統一化する
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
    If Sh.Name <> "Sheet1" Then Exit Sub
    Call USERFORM_OPEN(Target:=Target)
End Sub

Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
    If Sh.Name <> "Sheet1" Then Exit Sub
    Call USERFORM_OPEN(Target:=Target)
End Sub

Private Sub USERFORM_OPEN(ByVal Target As Range)
    Dim r As Integer, c As Integer
    r = Target.Row
    c = Target.Column
    If c = 3 Then
        If r >= 5 And r <= 15 Then
            UserForm1.Show
        End If
    End If
End Sub

Userform6だけおかしいのは、
ステップ実行などを使って、自分で解決しましょう。
 

回答
投稿日時: 24/02/24 12:34:40
投稿者: WinArrow

参考意見、
ユーザーフォームを1つにまとめて、且つ、モードレスで開くと
シートの操作(セル選択)しながら、ユーザーフォームを操作することができるので、
検討してみるとよいでしょう。

回答
投稿日時: 24/02/24 13:20:18
投稿者: MMYS

もしかして、Show とすると、初期状態で、ユーザーフォームが起動すると思ってませんか。違います。
  
UserFormはクラスの一種なのでインスタンス化しないと使用出来ません。UserFormは、メモリ上に展開しないと、使うこととが出来ません。メモリへの展開は、UserForm.Showを実行すると、内部で自動的にメモリ上に展開(インスタンス生成)されます。そして、「☓」や Unload するとメモリから開放されます。
  
一方、Hideは、一時的に非表示。あくまで、画面に表示しないだけです。たとえば、ユーザーの選択値は、その状態のまま保持されます。
  
1回目の、UserForm.Showはメモリ上には展開されてないので、初期化イベントのUserForm_Initializeが実行されます。その後、画面にUserFormが表示され、ユーザーの入力待ちとなります。その後、HideでUserFormを消すと、画面からはUserFomrは消えます。
  
そして、2回めのUserForm.Showを実行すると、すでにメモリ上にあるので、それを単に表示します。もちろん、1回目でユーザーがListboxを選択してたら、その選択した状態で。
 
ユーザーが「☓」で閉じた場合、メモリから開放されるます。再度、UserForm.Showを実行するとメモリに無いので初期化から。画面上では、Listboxは非選択の状態で。
 

回答
投稿日時: 24/02/24 13:21:44
投稿者: O.M

試してみましたが、
1.UserForm1を閉じていない
2.UserForm2以降のコマンドボタンが終了ボタンならボタンを押した際にUserForm2以降をを終了していない
3.1と2によりユーザーフォームが多重起動になって誤動作がおきやすい
  (Hideで隠してShowで表示、ステップ実行だとマクロの実行のリセットですべて終わってしまって
   確認がすることが難しくなっている?)
4.待機時間がなくてユーザーフォームの操作が同時進行になる
(ステップ実行になるとステップを指定している間に時間が過ぎるので同時進行にならない)
5.UserForm1でリストボックスをダブルクリックするとクリックの2回目が
 起動したUserForm2以降のフォームに反映されてしまう
 (待機時間がないのでダブルクリックの2回目でUserForm2以降のリストボックスを
   クリックして閉じるまでの動作が見えないため何が起こっているのかわからない)
    
となっているのかなーと思いました。
    

'UserForm1
Private Sub UserForm_Initialize()
    Dim r As Integer, re As Integer
    re = Sheets("Sheet1").Range("E5").End(xlDown).Row
    With Me.ListBox1
        For r = 4 To re
            .AddItem Sheets("Sheet1").Cells(r, 5)
        Next r
    End With
    Me.StartUpPosition = 0
    Me.Left = 300.95
    Me.Top = 300.75
End Sub
Private Sub ListBox1_Click()
' Dim Dta As String     使用していないのでコメントアウトさせてます
' Dta = Me.ListBox1.Value  使用していないのでコメントアウトさせてます
'
' ListBox1.ListIndexのListBoxを変数で指定したい場合は
' Dim Dta As Object
' Set Dta = ListBox1
' Select Case Dta.ListIndex
'
    Application.Wait [Now()] + 0.5 / 86400 '★リストボックス選択で青くなるのを見せる為の時間
    Me.Hide
    Select Case ListBox1.ListIndex '★シートの名前と違うと起動しなくなってしまうのでリスト番号で選んだ方がいいと思います
        Case 0: UserForm2.Show
        Case 1: UserForm3.Show
        Case 2: UserForm4.Show
        Case 3: UserForm5.Show
    End Select
End Sub

 
   
    
'UserForm2
Private Sub CommandButton1_Click()
    Unload Me '★終了ボタンでしたらHide(隠す)ではなくUnload(終了)にしておかないと二重起動等の誤動作になる
End Sub
Private Sub ListBox1_Click()
    Application.EnableEvents = False
    ActiveCell = Me.ListBox1.Value
    Application.EnableEvents = True
    Application.Wait [Now()] + 0.5 / 86400 '★リストボックス選択で青くなるのを見せる為の時間
    Unload Me
End Sub
Private Sub UserForm_Initialize()
    Dim r As Integer, re As Integer
    Unload UserForm1 '★UserForm1を閉じる
    Application.Wait [Now()] + 0.5 / 86400 '★UserForm1か閉じるのを待つ時間
    re = Sheets("Sheet1").Range("F4").End(xlDown).Row
    With Me.ListBox1
        For r = 4 To re
            .AddItem Sheets("Sheet1").Cells(r, 6)
        Next r
    End With
    Me.StartUpPosition = 0
    Me.Left = 300.95
    Me.Top = 300.75
End Sub

 
   
コードを書いてみましたが、5は解消されないので
(待機時間作ったので何が起きてるかはわかりやすくなったとは思いますが)
クリックイベントじゃなくダブルクリックイベントにした方がいい気がします…。

回答
投稿日時: 24/02/24 17:01:04
投稿者: WinArrow

最後のコードは、最初のデザインとだいぶ異なっていますが、
>Sheet1のF列〜J列(例えばセルG4〜G10
ほ、F列〜J列に対応したユーザーフォームが存在すると考えてよいのですか?
  
若しそうだとしたら、E列をキーとして、E列に対応したF列〜J列を選択する処理を
キー選択する「Useform1」とF列より右側列を選択する「Useform2〜6」に分けているのではないでしょうか?
  
Listboxは複数列設定する(Listbox1の中に1〜nを格納してしまえば)
キー項目:1回のクリックで、F列〜複数列の値を取得でき、それぞれに対応したセルに代入できます。
推測で書いているので、外しているかもしれません。

回答
投稿日時: 24/02/24 17:52:03
投稿者: WinArrow

引用:

空白セルを選択したときか、または使用セルをDeleteしたとき

という条件に対して
現在のThisworkbookモジュール内のコードで満足していると考えていますか?
 
>Private Sub Workbook_SheetChange
は、セルの値が変ったときというイベントですが、
セルの値が変らなくてもイベントが発生します。
変更後の値を確認するには、このプロシジャだけでは、無理です。
変更前の値を退避しておく必要があります。
 
>Private Sub Workbook_SheetSelectionChange
は、セル(含む、意図しないセル)にカーソルが入ったときにイベントが発生します。
列C、行5〜15というチェックがあるので、意図しないセルは、抑止できているとして、
空白セルのチェックが必要ではないでしょうか?
 
C5〜C15の範囲内で、カーソルが動くたびにイベントが発生し、オームが表示されます。
意図しないセルでない場合、ユーザーフォームをその都度[×]で閉じなければならない。
操作性は決して良くないと思います。

回答
投稿日時: 24/02/24 20:03:47
投稿者: O.M

多重クリックした際の挙動の回避方法が知りたくなってこちらに現れてしまっています…。
 
UserForm1のListBox1をダブルクリックするとシングルクリック2回と同じような動作で、
クリックの2回目がUserForm2のListBoxクリックで反映されてしまい
(ListBox1_DblClickイベントをいれてみましたがそちらは反応せず)
UserForm2のListBoxをダブルクリックしてしまうと同じくクリックの2回目扱いで
シートのセルが選択されるといった挙動になるようです。
 
 
WinArrowさんのご意見とは別の考え方で、
ユーザーフォーム2〜6は1つにまとめれるのではと思って気になっていたのでそちらを
反映させたコードもあげておきます。
 

'UserForm1
Private Sub UserForm_Initialize()
    Dim r As Integer, re As Long  '★reをlongにしていないと行数が多くなった際にオーバーフロー
    re = Sheets("Sheet1").Range("E5").End(xlDown).Row
    
    'ユーザーフォームのリストを消してしまった時の対策
    '4行目以下に文字がないと取得されるのが最終行になる、
    '500行目に文字といったことになるとリストボックスのリストが500ということにもなるので、
    '実際はIF Re > 30 thenのように表の範囲は絞った方がいいと思います。
    If re = Rows.Count Then
      MsgBox "Sheet1のE列にユーザーフォームのリストが存在しません。"
      Unload Me
      Exit Sub
    End If

    With Me.ListBox1
        For r = 4 To re
            .AddItem Sheets("Sheet1").Cells(r, 5)
        Next r
    End With
    Me.StartUpPosition = 0
    Me.Left = 300.95
    Me.Top = 300.75
End Sub

Private Sub ListBox1_Click()
  Dim re As Long '★表のリストを消してしまった時の対策
' Dim Dta As String     使用していないのでコメントアウトさせてます
' Dta = Me.ListBox1.Value  使用していないのでコメントアウトさせてます
'
' ListBox1.ListIndexのListBoxを変数で指定したい場合は
' Dim Dta As Object
' Set Dta = ListBox1
' Select Case Dta.ListIndex
    Application.Wait [Now()] + 0.5 / 86400 '★リストボックス選択で青くなるのを見せる為の時間
    '★表のリストを消してしまった時の対策
    re = Sheets("Sheet1").Cells(4, ListBox1.ListIndex + 6).End(xlDown).Row
    If re = Rows.Count Then
      Me.Hide
      MsgBox "Sheet1に" & ListBox1.Value & "のリストが存在しません。"
      Unload Me
      Exit Sub
    Else
      Me.Hide
      UserForm2.Show
    End If
End Sub

 
'UserForm2
Private Sub CommandButton1_Click()
    Unload Me '★終了ボタンでしたらHide(隠す)ではなくUnload(終了)にしておかないと二重起動等の誤動作になる
End Sub
Private Sub ListBox1_Click()
    Application.EnableEvents = False
    ActiveCell = Me.ListBox1.Value
    Application.EnableEvents = True
    Application.Wait [Now()] + 0.5 / 86400 '★リストボックス選択で青くなるのを見せる為の時間
    Unload Me
End Sub
Private Sub UserForm_Initialize()
    Dim r As Integer, re As Long '★reをlongにしていないと行数が多い場合にオーバーフロー
    Dim i As Long
    i = UserForm1.ListBox1.ListIndex + 6  '★クリックしたUserForm1のListBox1のListIndex番号+6で列番号
    Me.Caption = UserForm1.ListBox1.Value '★UserForm2の表示名をクリックしたUserForm1のListBox1の値にする
    Unload UserForm1 '★UserForm1を閉じる
    Application.Wait [Now()] + 0.5 / 86400 '★UserForm1か閉じるのを待つ時間
    re = Sheets("Sheet1").Cells(4, i).End(xlDown).Row
    With Me.ListBox1
        For r = 4 To re
            If Sheets("Sheet1").Cells(r, i) <> "" Then .AddItem Sheets("Sheet1").Cells(r, i) '★空白は取り込みから除外
        Next r
    End With
    Me.StartUpPosition = 0
    Me.Left = 300.95
    Me.Top = 300.75
End Sub

回答
投稿日時: 24/02/24 21:32:39
投稿者: MMYS

下記に簡単な例を示しました。ユーザーフォームを「閉じる・消す」には
・「☓」(閉じる)ボタン
・Hide
・Unload
がありますが、動作は異なります。
 
UserForm1を起動。その後、CommandButton1からUserForm2を起動。そしてリストを選択
 1.リストを選択後、Hideで終了。そのまま再度UserForm2を表示
 2.リストを選択後、「☓」(閉じる)で終了。そのまま再度UserForm2を表示
 3.リストを選択後、Unloadで終了。そのまま再度UserForm2を表示
選択値やイベントは、想定どおりの動きをしましたか。
 
UserForm1

Private Sub CommandButton1_Click()
    UserForm2.Show
End Sub

Private Sub CommandButton2_Click()
'    Unload UserForm2
End Sub

UserForm2
Private Sub CommandButton1_Click()
    Me.Hide
End Sub

Private Sub CommandButton2_Click()
    Unload Me
End Sub

Private Sub UserForm_Initialize()
    MsgBox "初期化"
    With Me.ListBox1
        .AddItem "あああ"
        .AddItem "いいい"
        .AddItem "ううう"
    End With
End Sub

Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    MsgBox "閉じる"
End Sub

Private Sub UserForm_Terminate()
    MsgBox "メモリ開放"
End Sub

 
※補足
Hideは和訳(直訳)すると、「隠す」です。つまりユーザーから隠しているだけ。Showで隠したUserFormを表示します。
Initialize は直訳すると初期化
Terminate は終了
 
※2
コメントアウトしている、次のコード。これを有効にして、なぜこのような動きになるかを説明できると、理解が深まると思います。(インスタンスを理解されると当然の動作)
 
Private Sub CommandButton2_Click()
'    Unload UserForm2
End Sub

 

回答
投稿日時: 24/02/25 10:44:16
投稿者: WinArrow

事象が
「ユーザーフォームの挙動がおかしい」
という話で始まったため、議論が
ListBox1_Clickプロシジャに集中していました。

引用:

Private Sub ListBox1_Click()
    Stop
    Application.EnableEvents = False
    ActiveCell = Me.ListBox1.Value
    Application.EnableEvents = True
    Me.Hide
End Sub

このプロシジャの中で
> Application.EnableEvents = False
については、
ユーザーフォーム内コントロールのイベント抑止と誤解しているのでは?
と考えコメントしていましたが、
そうではなく、Thisworkbookモジュール内の
Private Sub Workbook_SheetChange

Private Sub Workbook_SheetSelectionChange
を制御していることが分かりました。
通常では、
「Workbook_SheetChange」では、
イベントが終了すると自動的に次セルにカーソル移動します。
そうすると、
「Workbook_SheetSelectionChange」が発生します。
 
一方、
「Workbook_SheetSelectionChange」では
イベント終了後は、カーソルは移動しません。
従って、「Workbook_SheetChange」のカーソル移動を止める目的だった。
ということですね?
 
「Listbox1_Click」ではなく、ダブルクリックで凌いでいるという件
むしろ、ダブルクリックは、Thisworkbookモジュール側で使う方が、効果的と思います。
「Workbook_SheetBeforeDoubleClick」を使用すると、空白セルのチェックも、値削除(Delete)のチェクも可能になります。
「Workbook_SheetSelectionChange」と「Workbook_SheetSelectionChange」はやめた方がよいです。
勿論、
    Application.EnableEvents = False
    Application.EnableEvents = True
も不要です。
 
◆追加質問
 
今回のUserform2〜6の処理は、結果的に言えば、
各々のリストボックスの値(シートで言えば、F列から右へ6セル分)を
C5から下へ複写している処理になるのですか?
手操作では、「行列を入れ替える」に匹敵する処理です。
若し、そうだとしたら、ユーザーフォームは1つに集約可能です。

投稿日時: 24/02/26 09:41:12
投稿者: o_taroh

色々ご配慮ありがとうございます。
じつはある団体の金銭出納簿の処理に使っています、主に使う人がキーの操作が未だ不慣れなもので摘要欄の入力がスムーズにいかずそれをカバーしようとしたものです、しかも入力項目はかなり限られているものが目立ちます、それらを10のジャンルに分けて摘要欄へ入力するように考えたものです、摘要欄をクリックしたとき、または訂正のためDeleteしたときなどに先ずジャンルを選び次に入力項目を選択するようにしたものです、私と同じく年配者でダブルクリックが覚束なくなんとかワンクリックと思い始めたのですが、どうも私の知識では及ばず悩んだ挙句でした、そろそろダブルクリックも慣れてきてスムーズさも出てきましたので、このままダブルクリックでいきたいと思います、長い間ありがとうございました、見よう見まねで始めたVBAですが、私も大変勉強になりました、今後ともよろしくお願いします。
出納帳の仕上がる年度末それを使い決算書を一気に作成するのですが、そのプログラムも作りましたが仕上がるまでちょっと時間(5秒ぐらい待つ)がかかるのでそれを改良できないもかと考え中です、また質問をお願いすることがあるかもしれませんがよろしくお助け下さい。
ありがとうございました。