Excel VBA質問箱 IV

当質問箱は、有志のボランティア精神のおかげで成り立っています。
問題が解決したら、必ずお礼をしましょうね。
本サイトの基本方針をまとめました。こちら をご一読ください。

投稿種別の選択が必要です。ご注意ください。
迷惑投稿防止のため、URLの入力を制限しています。ご了承ください。


9626 / 13644 ツリー ←次へ | 前へ→

【26257】マクロの暴走について いのっち 05/6/29(水) 18:41 質問[未読]
【26259】Re:マクロの暴走について Kein 05/6/29(水) 19:18 回答[未読]
【26289】Re:マクロの暴走について いのっち 05/6/30(木) 16:30 発言[未読]
【26317】Re:マクロの暴走について Kein 05/7/1(金) 11:54 発言[未読]
【26328】Re:マクロの暴走について いのっち 05/7/1(金) 14:42 お礼[未読]
【26266】暴走の理由 [名前なし] 05/6/29(水) 23:10 発言[未読]
【26290】Re:暴走の理由 いのっち 05/6/30(木) 16:31 お礼[未読]
【26300】Re:暴走の理由 [名前なし] 05/6/30(木) 22:26 回答[未読]
【26307】Re:暴走の理由 いのっち 05/7/1(金) 9:43 お礼[未読]

【26257】マクロの暴走について
質問  いのっち  - 05/6/29(水) 18:41 -

引用なし
パスワード
   D7セルに1を入力した時、次のセルE7に1を入れたい時のマクロです。
D7セルに2や3を入れると同様E7もそれぞれ2や3になります。
下記は正常動作しています。
----------------------------------------------------------------------
Private Sub Worksheet_Change(ByVal Target As Range)
   
Dim hen As integer

hen = 7
     Select Case ActiveSheet.Cells(hen, 4).Value
      Case 1
      ActiveSheet.Cells(hen, 5).Value = 1
      
      Case 2
      ActiveSheet.Cells(hen, 5).Value = 2
     
      Case 3
      ActiveSheet.Cells(hen, 5).Value = 3
      
      Case Else
      ActiveSheet.Cells(hen, 5).Value = 0
      
    End Select
   
End Sub
----------------------------------------------------------------------

ところが、D7に1を入れた時、E7に1,F7を10にしたいようにマクロを修正しましたが、
暴走してしまいます。

----------------------------------------------------------------------

Private Sub Worksheet_Change(ByVal Target As Range)
   
Dim hen As integer

hen = 7
     Select Case ActiveSheet.Cells(hen, 4).Value
      Case 1
      ActiveSheet.Cells(hen, 5).Value = 1
      ActiveSheet.Cells(hen, 6).Value = 10
      
      Case 2
      ActiveSheet.Cells(hen, 5).Value = 2
      ActiveSheet.Cells(hen, 6).Value = 20
     
      Case 3
      ActiveSheet.Cells(hen, 5).Value = 3
      ActiveSheet.Cells(hen, 6).Value = 30
      
      Case Else
      ActiveSheet.Cells(hen, 5).Value = 0
      ActiveSheet.Cells(hen, 6).Value = 0
      
    End Select
   
End Sub
----------------------------------------------------------------------
また、目的は、D列全てにこのような動作をさせるために、for文を使用
しましたが、これまたもちろん暴走します(というか、前の状態の
暴走がそのまま暴走している感じなのでしょう、きっと)

----------------------------------------------------------------------


Private Sub Worksheet_Change(ByVal Target As Range)
   
Dim hen As integer

For hen = 7 to 100
     Select Case ActiveSheet.Cells(hen, 4).Value
      Case 1
      ActiveSheet.Cells(hen, 5).Value = 1
      ActiveSheet.Cells(hen, 6).Value = 10
      
      Case 2
      ActiveSheet.Cells(hen, 5).Value = 2
      ActiveSheet.Cells(hen, 6).Value = 20
     
      Case 3
      ActiveSheet.Cells(hen, 5).Value = 3
      ActiveSheet.Cells(hen, 6).Value = 30
      
      Case Else
      ActiveSheet.Cells(hen, 5).Value = 0
      ActiveSheet.Cells(hen, 6).Value = 0
      
    End Select

Next
   
End Sub

----------------------------------------------------------------------
if文で試してもやはり2番目の状態で暴走しています。何か良い知恵が
ございましたら、お貸しください。

----------------------------------------------------------------------

【26259】Re:マクロの暴走について
回答  Kein  - 05/6/29(水) 19:18 -

引用なし
パスワード
   入力したい値には一定のパターンが見られるので、数式で処理することが出来ます。
とうぜん手入力でも出来ますが、あえてVBAマクロでやるとしたら

With Range("E7:E100")
  .Formula = "=IF(OR($D7<1,$D7>3),0,$D7)"
  .Offset(, 1).Formula = "=IF(OR($D7<1,$D7>3),0,$D7*10)"
  .Resize(, 2).Copy
  .PasteSpecial xlPasteValues
End With
Application.CutCopyMode = False

というコードで出来ます。ただし、これは既にD7:D100の全てのセルに数値が入力済み
のときに、イベントではなく普通のマクロとして実行する場合のコードです。
もしセル入力イベントにしたいなら

Private Sub Worksheet_Change(ByVal Target As Range)
  If Intersect(Target, Range("D7:D100")) Is Nothing Then Exit Sub
  With Target
   If .Count > 1 Then Exit Sub
   If IsEmpty(.Value) Then Exit Sub
   If Not IsNumeric(.Value) Then Exit Sub
   Application.EnableEvents = False
   Select Case .Value
     Case 1 To 3
      .Offset(, 1).Value = .Value
      .Offset(, 2).Value = .Value * 10
     Case Else
      .Offset(, 1).Value = 0
      .Offset(, 2).Value = 0
   End Select
  End With
  Application.EnableEvents = True
End Sub

ぐらいで出来るでしょう。

【26266】暴走の理由
発言  [名前なし]  - 05/6/29(水) 23:10 -

引用なし
パスワード
   ▼いのっち さん:
暴走する理由は、Changeイベントが何度も呼ばれてしまうためです。

>下記は正常動作しています。
>----------------------------------------------------------------------
>Private Sub Worksheet_Change(ByVal Target As Range)
>   
>Dim hen As integer
>
>hen = 7
>     Select Case ActiveSheet.Cells(hen, 4).Value
>      Case 1
>      ActiveSheet.Cells(hen, 5).Value = 1
>      
>      Case 2
>      ActiveSheet.Cells(hen, 5).Value = 2
>     
>      Case 3
>      ActiveSheet.Cells(hen, 5).Value = 3
>      
>      Case Else
>      ActiveSheet.Cells(hen, 5).Value = 0
>      
>    End Select
>   
>End Sub

「正常動作している」のではなく、「正常動作しているように見える」だけです。
シートモジュールに以下のコードを書いて、適当なセルに1を入力してみてください。

Private Sub Worksheet_Change(ByVal Target As Range)
  Range("A1").Value = Range("A1").Value + 1
End Sub

おそらくA1の値が200超くらいで止まると思いますが、このイベントが何度も呼ばれている
証拠です。なんで途中で止まるのかはよくわかりませんが・・・。
Changeイベントは、「あるセルの値を変更した時」に実行されます。これは、マクロで
値を変更しても例外ではありません。

>ところが、D7に1を入れた時、E7に1,F7を10にしたいようにマクロを修正しましたが、
>暴走してしまいます。

今度はChangeイベントを以下のように変更して、またどこかのセルの値を変更してみて
ください。

Private Sub Worksheet_Change(ByVal Target As Range)
  Range("A1").Value = Range("A1").Value + 1
  Range("A2").Value = Range("A2").Value + 1
End Sub

おそらくカウントアップが止まらなくなるので、途中でEscキーでキャンセルして
ください。

このように、マクロによるセルの値変更時にChangeイベントを発生するのを防ぐため
には、値を変更するコードの前に

Application.EnableEvents = False

の一行を追加します。ただし、このまま放置しておくとイベントが発生しないままなので、
値を変更するコードの後に

Application.EnableEvents = True

の一行を追加します。つまり、

Private Sub Worksheet_Change(ByVal Target As Range)
  Application.EnableEvents = False
  Range("A1").Value = Range("A1").Value + 1
  Range("A2").Value = Range("A2").Value + 1
  Application.EnableEvents = True
End Sub

のようになるわけです。
これで試してみてください。セルの値が1増えて終わりになるはずです。

【26289】Re:マクロの暴走について
発言  いのっち  - 05/6/30(木) 16:30 -

引用なし
パスワード
   keinさん、アドバイスありがとうございました。さくさく動くようになりましたm(_._)m
Application.EnableEventsの使い方がわかって勉強になりました。すいません、もし
時間があったら教えて欲しいのですが、

>  If Intersect(Target, Range("D7:D100")) Is Nothing Then Exit Sub

Intersectのhelpを見ても今ひとつ仕様がわからなかったのですが、intersectを
する理由は何でしょうか?以下のWith文とともに、データの有る無しをCHECK
して、無ければsubから出るというのは何となく見た感じですが、あっているで
しょうか。


>  With Target
>   If .Count > 1 Then Exit Sub
>   If IsEmpty(.Value) Then Exit Sub
>   If Not IsNumeric(.Value) Then Exit Sub

【26290】Re:暴走の理由
お礼  いのっち  - 05/6/30(木) 16:31 -

引用なし
パスワード
   こんにちは。アドバイスありがとうございましたm(_._)m
ステップ的に教えていただいたので、changeの仕組みが
なぜ暴走するのか、理解が進み、助かりました。
色々とつたない私のやり方で試してみたら、アドバイスのおかげで
ちゃんと動くようになりました、また、新たな疑問も出てきましたが、
また再質問できましたら、よろしくお願いいたします。

--------------------------------------------------------------------------
Private Sub Worksheet_Change(ByVal Target As Range)
'イベントの暴走を食い止めるおまじないstart
 Application.EnableEvents = False
 
 Select Case Range("A1").Value
 Case 1
  Range("B1").Value = 2
  Range("C1").Value = 3
 Case 2
  Range("B1").Value = 3
  Range("C1").Value = 4
 Case 3
  Range("B1").Value = Range("A1").Value + 1
  Range("C1").Value = Range("A1").Value + 2
 Case Else
  Range("B1").Value = 0
  Range("C1").Value = 0
 End Select
  
 Select Case Range("A2").Value
 Case Null
 
 Range("A2").Value = Null
 Range("B2").Value = Null
 Range("C2").Value = Null
 
 Case Else
 Range("B2").Value = Range("A2").Value + 1
 Range("C2").Value = Range("A2").Value + 2
'MsgBox Range("A2").Value
  
 End Select
 
 Dim hen As Integer
 
 For hen = 3 To 100
 
 Select Case ActiveSheet.Cells(hen, 1).Value
 
  Case ""
  ActiveSheet.Cells(hen, 2).Value = Null
  ActiveSheet.Cells(hen, 3).Value = Null
    
  Case Else
  ActiveSheet.Cells(hen, 2).Value = ActiveSheet.Cells(hen, 1).Value + 1
  ActiveSheet.Cells(hen, 3).Value = ActiveSheet.Cells(hen, 1).Value + 2
  
 End Select
 
 Next
  
'イベントの暴走を食い止めるおまじないend
 Application.EnableEvents = True

End Sub
--------------------------------------------------------------------------
※新たな疑問というのはCase""とした時とCase Nullとした時動作が違うので・・・
?という感じですが、いま、調査中です。

【26300】Re:暴走の理由
回答  [名前なし]  - 05/6/30(木) 22:26 -

引用なし
パスワード
   ▼いのっち さん:
>※新たな疑問というのはCase""とした時とCase Nullとした時動作が違うので・・・

こちらについては、[#19866]が参考になるかと思います。

【26307】Re:暴走の理由
お礼  いのっち  - 05/7/1(金) 9:43 -

引用なし
パスワード
   >こちらについては、[#19866]が参考になるかと思います。

再度、m(_._)m ありがとうございました。今度から検索することも忘れないようにいたします。

【26317】Re:マクロの暴走について
発言  Kein  - 05/7/1(金) 11:54 -

引用なし
パスワード
   >intersectをする理由
目的のセル範囲内に入力したかどうか、をチェックするためです。
もし D7:D100 以外のセルに入力したときは、マクロを中止することにしないと、
どこでもイベントが発生して処理が走ってしまいますから。

【26328】Re:マクロの暴走について
お礼  いのっち  - 05/7/1(金) 14:42 -

引用なし
パスワード
   ▼Kein さん:

>どこでもイベントが発生して処理が走ってしまいますから。

なるほど、確かにおっしゃる通りです。エラーを少なくする為のテクニックとして
とても参考になりました、ありがとうございました。m(_._)m

9626 / 13644 ツリー ←次へ | 前へ→
ページ:  ┃  記事番号:
2610219
(SS)C-BOARD v3.8 is Free