Excel (VBA)

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

 
(Windows 7全般 : Excel 2010)
beforeupdateがクリック時のみ2回実行されます
投稿日時: 18/03/03 13:49:37
投稿者: jicco

はじめましてVBA勉強中のjiccoと申します。
初歩的な質問かもしれませんがお付き合いいただけますと幸いです。
 
以下のコードを実行し、登録しますか?のメッセージでNOと答えた場合、処理が2回実行されます。
ItmTxtの入力後、Enterキーを押した時には1回のみだったのですが、どこかをクリックすると2回メッセージが表示されます。
ステップインで1行ずつ実行させてみたところ、クリックの時はExit Subの後Private Sub ItmTxt_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)まで戻っていました。
いろいろ試してみたのですがこのクリックの現象がおこるのはダイアログから戻り値をもらって処理した時のようで、ItmTxtに入力された値を判別するだけの場合には発生しませんでした。
 
1.クリックとEnterキーでは処理に何か違いがあるのでしょうか
2.クリック時でも処理を1回のみにしたいのですが何か良い案はありますでしょうか?
 
以上2点ご教示よろしくお願いいたします。
 
 
    Private Sub ItmTxt_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
    Dim l As Long
         
    Call SEARCH
        If FD Is Nothing Then
    l = MsgBox("データがありません。登録しますか?", vbYesNo + vbQuestion + vbDefaultButton2)
            If l = vbYes Then
                Call RAG
            Else
                ItmTxt = Undo
                Cancel = True
                Exit Sub
            End If
        End If
  End Sub

回答
投稿日時: 18/03/03 19:10:18
投稿者: i-brown

ItmTxt = Undo

でItm_Txtの値を変更しているので、それが引き金になって
ItmTxt_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
が呼び出されていると思います。

投稿日時: 18/03/03 19:48:38
投稿者: jicco

i-brownさま
ご回答ありがとうございます。

ItmTxt = Undo
これが引き金になっていたとのことで大変勉強になりました。
 
ただ、キーボードのみで入力やダイアログの選択をした際には問題なくUndoが使えていたのは何故なのでしょうか。
Undoが使用できないとなるとBeforeUpdateにする必要は無くなってしまいますね...
BeforeUpdateに代わる良案がございましたらご教示くださいますと幸いです。
度々申し訳ございませんがよろしくお願いいたします。

回答
投稿日時: 18/03/03 20:54:24
投稿者: simple

回答者さんへの連携
http://www.excel.studio-kazu.jp/kw/20180228235342.html
 
マルチポストは好まない。
するなら他でも質問していることを書くべきでは?

投稿日時: 18/03/03 21:01:54
投稿者: jicco

simpleさま
そうでしたか、禁止事項などにも記載がなかったので気にせず質問してしまいました。
知恵袋と同じような感覚で質問してしまい、気分を害してしまったのなら謝ります。
常識知らずで大変申し訳ありませんでした。

回答
投稿日時: 18/03/04 10:36:35
投稿者: mattuwan44

jicco さんの引用:
simpleさま
そうでしたか、禁止事項などにも記載がなかったので気にせず質問してしまいました。
知恵袋と同じような感覚で質問してしまい、気分を害してしまったのなら謝ります。
常識知らずで大変申し訳ありませんでした。

 
ここのサイトでは明確に禁止とは書いてありませんが、
回答する側としてはいい気がしない。
なので、あちこちで聞いていることを明示してほしいという要望ですかね。
いずれにしても、回答者が回答したいと思えるように行動することが、
有益な情報を得るコツだと思います。。。
 
あちらで話を続けるなら、こちらは解決にしてあちらで話を続けたらいいと思います。
 
 

回答
投稿日時: 18/03/04 11:18:12
投稿者: mattuwan44

Option Explicit
 
 
Private Sub TextBox1_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
    Dim txt1 As MSForms.TextBox
    Dim rngNew As Range
    Dim s As String
     
    Set txt1 = Me.TextBox1
    s = txt1.Text
    If chkNew(s, rngNew) = True Then
        If MsgBox("新しいデータ。登録?Yes/No", vbYesNo) = vbYes Then
            rngNew.Value = s
        Else
            Stop
            txt1.Text = ""
            Cancel = True
        End If
    End If
End Sub
 
Private Function chkNew(ByVal sKey As String, ByRef rngTarget As Range) As Boolean
    Dim Rng As Range
     
    Set Rng = ActiveSheet.Columns("D")
    Set rngTarget = Rng.Find(What:=sKey, LookAt:=xlPart)
    If c Is Nothing Then
        With Rng
            Set rngTarget = .Cells(.Rows.Count, 1).End(xlUp).Offset(1)
        End With
        chkNew = True
    End If
End Function
 
あっちは読みにくいなぁ。。。
こんなことをしたいのかな?

回答
投稿日時: 18/03/04 11:26:33
投稿者: i-brown

jicco さんの引用:
i-brownさま
ただ、キーボードのみで入力やダイアログの選択をした際には問題なくUndoが使えていたのは何故なのでしょうか。
Undoが使用できないとなるとBeforeUpdateにする必要は無くなってしまいますね...
BeforeUpdateに代わる良案がございましたらご教示くださいますと幸いです。
度々申し訳ございませんがよろしくお願いいたします。

 
マルチポスト先の方がいろいろ詳しく書いてあったので、
「ItmTxtの値が初期値なら何もしない」を先頭に書き足してみました。
ItmTxtに何か初期値がある前提の設計です。
 
あと、気づいた点としては
1. Rangeが返ってくることが分かっている場合は、ObjectではなくRange型の変数を宣言する。
2. Ifの入れ子が深い。Exit Subを知っているのであれば、簡単な判断は先に済ませてExitした方が、
  Ifの入れ子が浅くなって読みやすくなる。
3. 1回しか使わないBooleanの変数は、宣言しなくてもよいかも。
  (デバッガで見るのには使えるので、ここは好みが分かれます。)
 
Option Explicit

Private Const Undo As String = "(検索値)" ' よく分からないので、適当に定義

Private Sub ItmTxt_BeforeUpdate(ByVal Cancel As MSForms.ReturnBoolean)
    If ItmTxt.Text = Undo Then Exit Sub  ' 初期値の時は反応させない
    
    Dim FTRng As Range  ' メンバー自動表示を使うため、型を変更
    Dim FDRng As Range
    
    ItmLst.Clear

    Set FDRng = I_LIST.Columns("D").Find(What:=ItmTxt, LookAt:=xlPart)
    If Not FDRng Is Nothing Then Exit Sub   ' こちらのほうがIfの入れ子が少なくなって見やすい
    

    If vbYes = MsgBox("データがありません。登録しますか?" _
            , vbYesNo + vbQuestion + vbDefaultButton2) Then
       MsgBox "OK"
       Exit Sub ' 簡単に解決する条件があるなら、こちらをExit Subしては?
    End If
        
    Cancel = True
    ItmTxt = Undo   ' 何かの文字列に戻す
    MsgBox "入力し直してください。", vbExclamation
End Sub

回答
投稿日時: 18/03/04 23:51:50
投稿者: ただやん

ItmTxt = Undoの前に、
Application.EnableEvents = False
後ろに、
Application.EnableEvents = True
を入れたらどうですか?
 
             If l = vbYes Then
                 Call RAG
             Else
                 Application.EnableEvents = False
                 ItmTxt = Undo
                 Application.EnableEvents = True
                 Cancel = True
                 Exit Sub
             End If

投稿日時: 18/03/05 13:48:19
投稿者: jicco

皆さまたくさんの良案を、ご教示下さいまして本当にありがとうございます。
ItmTxtを初期値に戻すことでbforeupdateが動いてしまっているとのことで、この処理をパスするいろんな方法が学べました。
Option Explicitさえ知らない初心者で、皆さんから教えていただいたコードに分からないところが多々ありますのでこれからこれから1つずつ学習しながら実践していきたいと思います。
またこの問題以外にも見やすい作り方やアドバイスなどもいただけて本当に助かりました。
長らくお付き合いいただきありがとうございました。
一度自分で実行してまた分からないところが出てきましたらお力を貸していただきたく思います。