Excel (VBA)

Excel VBAに関するフォーラムです。
  • 掲示板への投稿には会員登録(無料)が必要です。会員登録がまだの方はこちら
  • 掲示板ご利用上のお願い」に反するご記入はご遠慮ください。
  • Q&A掲示板の使い方はこちらをご覧ください
トピックに返信
質問

 
(指定なし : 指定なし)
同一セルに数字を入力していくと加算されて表示する方法
投稿日時: 22/01/19 09:16:40
投稿者: たなかゆう

初投稿です。VBAに関しては無知です。
在庫管理のため、伝票入力簡易化を図り、VBAについて調べています。
下記サイトでA1のセルに数字を打ち込んでいくと加算できるようになりました。
 
https://oshiete.goo.ne.jp/qa/2154378.html
 
 
 
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address <> "$A$1" Then Exit Sub
If Target.Value = "" Then Exit Sub
x = Target.Value
With Application
.ScreenUpdating = False
.EnableEvents = False
.Undo
y = Target.Value
Target.Value = x + y
.EnableEvents = True
.ScreenUpdating = True
End With
With Cells(ActiveSheet.Rows.Count, "C").End(xlUp)
.Offset(1, 0).Value = x
.Offset(1, 1).Value = Time()
End With
End Sub
 
これに加え、A2、A3、A4と複数のセルで入力、別の数字を打ち込んで
加算されるようにしたいのですがどうすればよいのでしょうか?
どなたかご存じの方、ご教示ください。

回答
投稿日時: 22/01/19 11:20:31
投稿者: QooApp

効率的な記述をしても理解に時間がかかるかもしれないので一番わかりやすい(はず)の処理は下記のとおりです。赤字部分
 
▼▼▼▼
Private Sub Worksheet_Change(ByVal Target As Range)
     
    If Target.Address <> "$A$1" And Target.Address <> "$A$2" And Target.Address <> "$A$3" And Target.Address <> "$A$4" Then Exit Sub
     
    If Target.Value = "" Then Exit Sub
     
    x = Target.Value
     
    With Application
        .ScreenUpdating = False
        .EnableEvents = False
        .Undo
        y = Target.Value
        Target.Value = x + y
        .EnableEvents = True
        .ScreenUpdating = True
    End With
     
    With Cells(ActiveSheet.Rows.Count, "C").End(xlUp)
        .Offset(1, 0).Value = x
        .Offset(1, 1).Value = Time()
    End With
 
End Sub
 
まず、VBの参考書(初心者向けVB教本系)を必ず手元に用意いただければ幸いです。
今回の要望「A1以外のセルでもやれるようにしたい」がどのような処理となったか赤字部分をよく理解ください。

回答
投稿日時: 22/01/19 11:32:20
投稿者: QooApp

追記:提示いただいたソースコードの末尾には入力履歴をC列に書き出すことができる処理が含まれていますが、
今回の変更修正で「どのセルに入力した履歴なのかわからない」という問題が出てきます。
(そもそも入力履歴自体を使用しないなら読み飛ばしてください。)
 
・どのセルを変更したのか
・どんな入力がなされたか
・いつ記録されたか
 
が必要になるかなと思いますが、提示のソースコードの「いつ記録されたか」の記載については時刻表記しかないので「〇月×日」という判断がつきません。
Time() を Now() に変更すると年月日と時刻まで記録されます。目的に沿って必要なデータを入力するように変更してください。
 
▼それを踏まえて、C列以降に上記3要素の入力履歴を記録するならば、
    With Cells(ActiveSheet.Rows.Count, "C").End(xlUp)
        .Offset(1, 0).Value = Target.Address
        .Offset(1, 1).Value = x
        .Offset(1, 2).Value = Now()
    End With

投稿日時: 22/01/19 14:50:13
投稿者: たなかゆう

 QooApp様
 
ご回答ありがとうございます。
コード作成の際、改行していくにはどうすれば良いのでしょうか?
具体的には
 セルE7〜E45、I7〜I40、M6〜M41、Q6〜Q43に加算する指示を入力したいです。
 
何度も申し訳ございません。
よろしくお願いいたします。

回答
投稿日時: 22/01/19 17:30:27
投稿者: QooApp

その質問が来ることは想定済みでした。
 
が、その上で一番わかりやすい方法で記述したつもりです。
ゼロベースでソースコードを書かせるのはこのサイトの規約に反しますので、発展的な考え方ができるように書いたつもりです。
 
私の第1投稿の赤字部分をよく観察してください。
今回の議題に対する変更箇所は唯一1行のみです。

 
元々セルA1(表記上は$A$1ですが)しか加算されない仕組みだったものに、A2・A3・A4が増えたらどうなるかという質問で改良した箇所を赤字で記載しています。
 

引用:
セルE7〜E45、I7〜I40、M6〜M41、Q6〜Q43に加算する指示を入力したいです。

 
最も簡単な書き方は第1投稿にて提示したように「If ■■■■■ Then」の■部分にその通りに記載する方法です。
ソースコードが非常に長くなりますが、最も単純な表記方法で再度設定を変更したい場合においてもメンテナンス性が高いです。
 
面倒だと思いますが、VBの基本構造を理解するためにも一度書いてみてください。
小学生の漢字ドリルと同じで何度も構文を書かないと感覚を掴むのは厳しいと思います。
 
 
多少の補足を下記に掲載します。よくお読みください。
「If ■■■■■ Then」の部分は「もし■■■■■が正しかったら」という疑問文になります。
 
■の部分には数学的な式で一致・不一致を判定できます。
式例:1=1:1は1か > 同じなのでTRUE判定(正しいという判定)
式例:1=2:1は2か > 違うのでFALSE判定(間違っているという判定)
 
また、今回のIF文で使用しているのはイコールではなく、ノットイコール(不一致である)という意味の「<>」を使用して記述されています。
 
つまり先ほどの式例の答えが逆になります。
式例:1<>1:1は1じゃないか > 同じなのでFALSE判定
式例:1<>2:1は2じゃないか > 違うのでTRUE判定
 
また、If文には複数の条件式をまとめてぶっこむことができます。
条件式と条件式の間に「 And 」(前の条件式と後ろの条件式を両方とも満たす場合のみTRUEとなる)または「 Or 」(前の条件式と後ろの条件式いずれかが条件を満たしていればTRUEとなる)を含めることで連続で条件式を書けます。
 
 
本題に戻ります。
If Target.Address <> "$A$1" Then Exit Sub
プログラムを和訳すると「ターゲットアドレスのセル座標はA1じゃないか?」となります。
ここでいうターゲットアドレスとはセルの値を編集した箇所(最後に入力された箇所)がセルA1ではない番地か、という意味です。
 
元のソースコードではターゲットアドレスかどうかとしてA1以外のセルには加算する処理を行わないとしています。
 
それをA2・A3・A4のセルもターゲットアドレスとして含める変更をしています。
 
▼A1だけのとき
If Target.Address <> "$A$1" Then Exit Sub
 
▼A1・A2・A3・A4は加算するとき
If Target.Address <> "$A$1" And Target.Address <> "$A$2" And Target.Address <> "$A$3" And Target.Address <> "$A$4" Then Exit Sub
 
つまり「And」で条件式を追加するだけです。
例えどれだけ長大な1行式を書くことになろうとも、そもそもの意味が理解できていなければメンテナンスできないので、あえてこの通り説明させていただきます。
 
今後VBの開発をされるならエチケットとして参考書を手元に置くようにして下さい。
高度な疑問以外受け付けないわけではないのですが(私も初歩的なミスをやりますし)、完全に知識がない状態の人に説明するための労力を常時割けるかどうかはここの常連さん方の気分次第です。
 
ただし、今回の質問の仕方については疑問点が非常に明確になっているので、質問に質問を返してより詳細な問題を理解したうえで解答しなければならない労力が無かったので非常に良い質問の仕方だと思います。

回答
投稿日時: 22/01/19 17:33:19
投稿者: QooApp

QooApp さんの引用:

「If ■■■■■ Then」の部分は「もし■■■■■が正しかったら」という疑問文になります。

 
言葉の表現に違和感があったので訂正します。
「If ■■■■■ Then」の部分は「もし■■■■■の条件式を満たす場合は」という疑問文になります。

回答
投稿日時: 22/01/19 22:30:03
投稿者: simple

横から失礼します。
 
まずは、22/01/19 14:50:13の質問にコメントします。
1. 対象範囲の限定

  If Intersect(Target, Range("E7:E45,I7:I40,M6:M41,Q6:Q43")) Is Nothing Then Exit Sub
などと書くのが普通かと思います。
 
2. 「改行したい」というのは、進んでいるはずのカーソルがUndoによって戻ったままだからです。
以下の修正案を見てください。(他の要素も反映しているので、少し様子が変わっていますが)
   
Private Sub Worksheet_Change(ByVal Target As Range)
    Dim x As Double, y As Double    'またはLongかCurrencyか
    Dim rng As Range
    
    If Intersect(Target,Range("E7:E45,I7:I40,M6:M41,Q6:Q43")) Is Nothing Then Exit Sub
    If Target.Value = "" Then Exit Sub  ' 複数セルの同時変更はとりあえず対応せず。
    
    '設定変更
    Application.ScreenUpdating = False
    Application.EnableEvents = False
    
    x = Target.Value        '入力値
    Set rng = ActiveCell    '移動後の現在のセルを記憶  ■
    Application.Undo        '元に戻す
    y = Target.Value        '入力前の数値
    Target.Value = x + y    '合計値に更新
    rng.Select              'セル位置を復旧            ■
    
    'ヒストリーに書き込み
    With Cells(Rows.Count, "C").End(xlUp)   'ここはそのまま
        .Offset(1, 0).Value = x
        .Offset(1, 1).Value = Time()
    End With
    
    '設定の復旧
    Application.EnableEvents = True
    Application.ScreenUpdating = True
End Sub

 
■提示された最初のコードについて、念のため言及すれば、
1. 元々の質問掲示板の制約だったのかも知れないが、インデントをつけないのは不適切です。
   きちんとインデントをつける習慣をつけることを推奨します。
2. 変数x,yの宣言がないのも適当ではありません。
3. セルに入力するのであれば、漏らさず、イベント抑止、復旧をきちんと入れるべきで、
   .Offset(1, 0).Value = x や、.Offset(1, 1).Value = Time()では
   セルの変更によって、自分自身が呼び出されてしまっていて、無駄が生じています。
   (結果に影響しないとはいえ、無駄は無駄です)
4.複数セルに同時入力(例えば、セル範囲のコピーペイスト)があるとエラーになります。
5.入力のチェックも必要でしょう。間違えて空白や"."だけ入力した場合、エラーになってしまいます。
(上記のコードには、4,5は対応していません。必要ならそちらで)
 
■これは人それぞれで考え方がありますので、ひとつの意見としてコメントしておきます。
 
そもそも、合計値のうえにさらに追記していくという(トリッキーな)処理の目的は
どんなことなんでしょうか。
1.一つの項目が、複数の内訳要素に分かれていて、内訳は残さずに、面上で合計してしまいたい、
  ということですか?
  (そういう場合は、 = 100 + 200 + 300 などと式として表すのが普通です)
または、
2.修正が発生したとき、差分を計算して差分を入力させることで済ませたい、ということですか?
 あなたひとりだけなら結構ですが、普通は、戸惑うようなインターフェイスだと思います。
 普通に入力して、上書き修正するのが間違ないはずです。
 
また、合計はいくらでも後で(または並行して)計算できるんですから、
合計値の上に項目を追加するなどということはやめて、
まずはきちんと入力することに集中すべきだと思います。

回答
投稿日時: 22/01/19 22:39:18
投稿者: WinArrow
投稿者のウェブサイトに移動

この仕事の意図がよく理解できないですが、
 
なにか電卓の代わりをやっているような感じを受けます。
 
もし、入力間違いあったとして、
どのように、その間違いを検出するのか?
直前の入力が間違っているとは限らないという前提はおかしいかな?
 
A列に上から入力していき、右隣へ累計(計算式)を入れれば、
検証はできるのではないか?
(マクロ不要)

トピックに返信