Excel (VBA)

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

 
(Windows 10 Pro : Microsoft 365)
同じ列で、対応する範囲(行)を決めて特定のセル(同じ列内)で結果を表示させたい
投稿日時: 24/04/06 11:10:47
投稿者: しおちゃん
メールを送信

Private Sub Worksheet_Change(ByVal Target As Excel.Range)
Dim r As Range
  For Each r In Target
    If r.Column = 6 And r.Row >= 8 Then
        If Cells(r.Row, r.Column) <> "" Then
            Range("f7") = Format(Now, "hh:mm")
        End If
    End If
  Next r
  For Each r In Target
    If r.Column = 7 And r.Row >= 8 Then
        If Cells(r.Row, r.Column) <> "" Then
            Range("g7") = Format(Now, "hh:mm")
        End If
    End If
  Next r
 End Sub
 
このようなプログラムまでは、出来たのですが、この場合だと同じ列全てが対象となる為、
この列の下段の方で、別の表を作成した時に対応が出来ないので、列全体の指定ではなく、
対象となる範囲(行)の範囲を指定したプログラムに変更がしたいのですが、どうすれば
良いかわかりません。ご教授願います。

回答
投稿日時: 24/04/06 12:46:18
投稿者: hatena
投稿者のウェブサイトに移動

対象となる範囲(行)の説明を具体的にしてください。
   
表形式になっいるなら、CurrentRegionプロパティで表範囲を取得できるので、それを利用するといいでしう。
テーブル化してあるのなら、テーブルで指定するのが確実です。
   
あと、InterSectメソッドで更新範囲(Target)と表範囲の共通部分を対象にするといいでしょう。
   
コード例
  
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
    Dim r As Range, c As Range
    Set r = Intersect(Target, Range("F7").CurrentRegion, Range("F8:G" & Rows.Count))
    If r Is Nothing Then Exit Sub
    For Each c In r
        If c <> "" Then
            Cells(7, c.Column) = Format(Now, "hh:mm")
        End If
    Next c
End Sub

投稿日時: 24/04/06 14:16:09
投稿者: しおちゃん
メールを送信

早速のご回答ありがとうございます。
例えば、7列目の8行から15行までの数字で対応したい場合となります。
それ以降は、同じく8列目の8行から15行・・・(同様に何列か続く)
教えて頂いたプログラムを入力してみましたが、エラーになってしまい、
よくわかっていませんです。すみません。

回答
投稿日時: 24/04/06 14:16:37
投稿者: simple

引用:
この場合だと同じ列全てが対象となる為、
この列の下段の方で、別の表を作成した時に対応が出来ないので、列全体の指定ではなく、
対象となる範囲(行)の範囲を指定したプログラムに変更がしたい

良く理解ができていませんが、
対象となる範囲の行の上限の制約(r.Row <= 30 とか)をAND条件で追加すれば済む話ですか?
その制約を自動でしたいのが希望なんですか?
されたいことをもう少し追加説明をしてもらわないと、
お互いに思惑がミスマッチのまま時間が経過します。
 
なお、
Range("f7") = Format(Now, "hh:mm")
右の式はあくまで例示なので意味はないのですね?
Targetが複数セルの時にも、同じセルに書き込んでいるのも、あくまで例示ということですね?
そこは気にしなくていいと。

回答
投稿日時: 24/04/06 14:32:55
投稿者: WinArrow


この場合だと同じ列全てが対象となる

Target(選択セル)が全てになるということですか?
 
それしたら、
>If r.Column = 6 And r.Row >= 8 Then

If r.Column = 6 And r.Row >= 8 And r.Row <= 50 Then
のように条件を追加すれば、ダメですか?
 
それと
>Range("f7") = Format(Now, "hh:mm")
の後に
Exit For
でループを抜ける
この時、このプロシジャが、起動されることをご存知ですか?

回答
投稿日時: 24/04/06 14:41:50
投稿者: simple

発言時期が重なっていて見落としました。
> それ以降は、同じく8列目の8行から15行・・・(同様に何列か続く)
例えば、J列までなら、hatenaさんのコードで

    Set r = Intersect(Target, Range("F8:J15"))
などとすればよいのではないですか?

回答
投稿日時: 24/04/06 15:11:12
投稿者: WinArrow

質問
Worksheet_Changeプロシジャは、セルが更新(同じ値を含む)されて場合
起動されるイベントプロシジャです。
複数のセルを同時(一緒)に更新することはあるのでしょうか?
Targetセルは更新されたセルです。
For Each r In Target
と記述すると、更新対象セル全てが処理の対象となります。
例え、そうであったとしても、時刻を代入するセルは、1つしかないので、
最初の1つのセルだけ処理の対象としてはいけないのでしょうか?
つまり、ル―ウする必要はない・・・ということです。
   
例えば、F8〜F15(または、G8〜G15)の間の何れかのセルが更新された時
という意味でしたら、コードが違います。
   
>この列の下段の方で、別の表を作成した時に
これに対する説明がないように思います。

回答
投稿日時: 24/04/06 18:22:03
投稿者: hatena
投稿者のウェブサイトに移動

質問のコードと説明から、下記のような仕様だと判断しました。
 
F7セルを含む表があり、さらにその下方に別の表がある(両表の間には空行がある)という状況で、
前者の表の範囲のF列、G列の8行目以降のセルが変更されたら、その列の7行目のセル(F7、G7)に更新時刻を入力する。
 
もし、異なる部分があるなら、仕様を明確に説明してください。
 

しおちゃん さんの引用:

それ以降は、同じく8列目の8行から15行・・・(同様に何列か続く)
教えて頂いたプログラムを入力してみましたが、エラーになってしまい、
よくわかっていませんです。すみません。

 
私の作成したサンプルでは、提示したコードでエラーなく、上記の仕様通りに動作してます。
 
どのようなエラーになりましたか。
また、エラーになったときデバッグボタンを押すとどのコードが反転表示されますか。
 
(同様に何列か続く)に関しては、表になっていて、その表範囲の列が対象ということですか。
 
プログラムを書くには「何列か」というようなあいまいな表現ではなく、明確なだれにでも判断できるような表現をする必要があります。

回答
投稿日時: 24/04/07 09:59:48
投稿者: WinArrow

勝手な推測で、書かせていただきます。
前レスでも書きましたが、
Worksheet_Changeイベントは、
引数のTargetセルの値が変わった時(同じ値を含む)に発生します。
Targetは複数セルに対応しているから、判断に迷っているところです。
 
Worksheet_Changeイベントの目的は
(1)セルの値が変わった時、その値が妥当であるか確認する(エラーチェック、別処理のトリガーなd)
(2)セルの値が変わったことをどこかに記録する、またはアナウンスする。
(3)その他
 
今回は、(2)のように思います。
そこで、問題は
「特定セル範囲内の複数セルを同時更新する」があるのでしょうか?
若し、あったとしても、更新時刻を別セルに記録するのに、
更新セル全てを対象にする必要があるのでしょうか?
という疑問。
 
更新セルが特定セル範囲でる確認(チェック)は、ループしなくてもチェックできます。
(他の回答者のレス参照)
 
そこで、勝手に推測したのは、
「特定セル内のどこかのセルが更新されたら、その更新日付を別セルに代入する」
という仕様なのではないかということです。
どこかのセルだから、勿論、複数セル対応です。
しかし、複数セルかどうかの判定は必要ないと思います。
 
それより、特定セル範囲が複数存在するような説明があるので、
複数の特定セル範囲を簡便に指定する方法(複数の特定セル範囲の配置関係を明確にする)を模索した方がよいと思います。
 
余談ですが、別セルに更新時刻を代入した時にも、イベントは発生しするので、
その対処も必要です。(特定セル範囲に含めないこと)
 
 
 
 
 

投稿日時: 24/04/07 10:05:57
投稿者: しおちゃん
メールを送信

皆さま、色々とご指導いただきまして誠にありがとうございます。
当方、まだまだ駆け出しの初心者なものですから中々表現等が
出来ていなくて申し訳ありません。また専門的な表現も私にはまだ
まだ十分理解できるほど知識がありません。
私が作りたい表の詳細を出来るだけ書きますのでよろしくお願い致します。
 
C1〜F9までを使った表を作ります。
C1〜F1までは、入力された時刻を表示します。
C2〜C9まで数字を入力するとC1にそのセル範囲内で最終的に入力された
数値の時刻を表示する。
同じようにD1にはD2〜D9までに入力された数値の最終の入力された
時間を表示する。という風にF列まで続きます。
 
次にC12〜F20の表がありまして、ここでもC12〜F12は時刻を表示するセルです。
上記と同じようにC13〜C20までを入力するとその最終的に入力した時刻が
C12に表示するといった具合で、F列まで同じ様に対応する表を作りたいのです。
 
これで、お判りいただけましたでしょうか?
宜しくお願い致します。

回答
投稿日時: 24/04/07 10:58:12
投稿者: hatena
投稿者のウェブサイトに移動

しおちゃん さんの引用:

C1〜F9までを使った表を作ります。
C1〜F1までは、入力された時刻を表示します。
C2〜C9まで数字を入力するとC1にそのセル範囲内で最終的に入力された
数値の時刻を表示する。
同じようにD1にはD2〜D9までに入力された数値の最終の入力された
時間を表示する。という風にF列まで続きます。
 
次にC12〜F20の表がありまして、ここでもC12〜F12は時刻を表示するセルです。
上記と同じようにC13〜C20までを入力するとその最終的に入力した時刻が
C12に表示するといった具合で、F列まで同じ様に対応する表を作りたいのです。

 
纏めると下記のようなことでしょうか。
 
C1〜F9とC12〜F20の範囲の2つの表がある。
それぞれの表の1行目には、その列の1行目以外のセルの最終更新時刻を表示させたい。
 
最初の質問のコードには下記のような条件がありました。
 
If Cells(r.Row, r.Column) <> "" Then
 
これは更新後のセルが空欄のときは何もしないということになります。
つまり、値があるセルをDeleteキーなどで削除したときは更新扱いにしないという意味になりますが、
その仕様で間違いないですか。
 

回答
投稿日時: 24/04/07 11:05:35
投稿者: hatena
投稿者のウェブサイトに移動

また、Changeイベントは、入力前の値と同じものを上書き入力した場合も発生します。
その場合、入力前と入力後の値は変化していないことになりますが、
これも更新されたと判断していいですか。

回答
投稿日時: 24/04/07 11:14:36
投稿者: WinArrow

回答ありがとうございます。
だいぶすっきりしましたね・・・・
  
前提として、
シートモジュール(Sheet1)に記述します。
特定範囲が複数存在することから、
例えば、増減が生じた場合でも、コードメンテナンスを極力少なくする
意味で、特定範囲を配列にしています。
更新セル(Target)は複数を前提にしている(例、横方向に複数)ので
ループ処理を組み込んでいます。
更新セルは、同値変更、削除変更を含んでいます。
(値削除でも時刻は代入されます)
更新前の値のチェックは、このイベント処理だけで対応できません。
日付代入時はイベント発生しないようにしています。
  

引用:
Option Explicit
 
Dim 特定範囲
Dim myRNG As Range
Dim R As Long, C As Long
Dim TCELL As Range
 
Private Sub Worksheet_Change(ByVal Target As Range)
     
    特定範囲 = Array("C2:F9", "C13:F20")
    For R = LBound(特定範囲) To UBound(特定範囲)
        Set myRNG = Application.Intersect(Target, Range(特定範囲(R)))
        If Not myRNG Is Nothing Then
            Application.EnableEvents = False
            For Each TCELL In Target
                C = TCELL.Column - Range(特定範囲(R)).Column + 1
                Range(特定範囲(R)).Cells(1, C).Offset(-1).Value = Format(Now(), "hh:mm")
            Next
            Application.EnableEvents = True
            Exit For
        End If
    Next
 
 
End Sub

回答
投稿日時: 24/04/07 11:29:37
投稿者: WinArrow

コードを一部修正します。

Option Explicit
 
Dim 特定範囲
Dim myRNG As Range
Dim R As Long, C As Long
Dim TCELL As Range
 
Private Sub Worksheet_Change(ByVal Target As Range)
     
    特定範囲 = Array("C2:F9", "C13:F20")
    For R = LBound(特定範囲) To UBound(特定範囲)
        Set myRNG = Application.Intersect(Target, Range(特定範囲(R)))
        If Not myRNG Is Nothing Then
            Application.EnableEvents = False
            For Each TCELL In myRNG      '←修正個所
                C = TCELL.Column - Range(特定範囲(R)).Column + 1
                Range(特定範囲(R)).Cells(1, C).Offset(-1).Value = Format(Now(), "hh:mm")
            Next
            Application.EnableEvents = True
            Exit For
        End If
    Next
 
 
End Sub

投稿日時: 24/04/07 13:16:44
投稿者: しおちゃん
メールを送信

hatena様
WinArrow様
simple様
色々とご指導ありがとうございました。無事私の望み通りの表示が
出来ました!!
本当に、ありがとうございました。
こんな初心者の私に色々としてくださり、感謝感謝です。m(_ _)m