Excel (VBA)

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

 
(指定なし : 指定なし)
ビタミンVBAさんへ
投稿日時: 18/01/03 22:16:24
投稿者: simple

「セル書式の範囲指定で1004アプリケーション定義またはオブジェクト定義のエラー 」(その後)
http://www.moug.net/faq/viewtopic.php?t=76635
のビタミンVBA さんへ。
 
少し開いたままにしておいていただけば、皆さんの助言が得られるはずです。
自分の判断、行動に間違いはないと自信を持っているので、
助言は要らないということですか?
 

引用:
今回の結果から
(1)Private Subで特定できるセル位置はその記述されたシートのみで、別シートで特定するためには
.Range(.Cells(X,X),.Cells(Y,Y))のようにCellsのまえにピリオドを入れる。
 本来はボタン1の方にもピリオドを入れるべきかと思いますが、同じシートなら特定できるから省略可能なのかと思います。
  
(2)標準モジュールに記述した場合は、変数でシートが特定できるから Cellsの前にピリオドがなくてもいける。(ピリオドを入れても動作するので、本当は入れるべき?)
と要約されましたが、もう少し正確な理解をしておいたほうがよろしいでしょう。(結果オーライでなく)
 
まず、
   ○.Range(Cells(X,X),Cells(Y,Y))
として、中のCellsのシートを特定しなかった場合、
 
シートモジュールに書かれているプロシージャでシートを省略した場合、
  そのプロシージャが書かれているシートを指すのが仕様です。
  選択されたシートは無関係。
 
・一方、標準モジュールのプロシージャ内でシートを省略した場合、
  その時にアクティブなシートを指す仕様となっています。
というのがルールです。
 
このことをしっかり理解してください。
 
With .Range(Cells(1, "A"), Cells(1, "E"))  'Cellsの前にピリオドなしでもOK
なのは、
.Activate とシートを明示的に選択状態にしているからです。
 
-------
(1)シートを一つしか使わない場合は、そのシートが選択状態であることが明確なので、
   シートを指定しなくてもよいことが多いですね。
 
(2)今回のように複数のシートを使う場合は、選択状態に依存せず、
   明示的に中のCellsに対してもシートを指定するべきです。
 
   Sheet2が選択状態のときに、
   Sheet1.Range(Cells(1,1),Cells(2,3)).value = 1
   などとするとたちまちエラーになります。
    
(3)その際、特定のシートをActivate(Select)するという対応は適当ではありません
   特に繰り返し処理のなかでシートを選択状態にすると、画面のちらつきにもなりますし、
   わずかですが処理効率が低下します。
    
   また、それ以上にコードの可読性を高めるためにも、シートの選択は避けたほうが無難です。
 
   その時点でどのシートが選択状態にあるか、を常に意識しなければなりませんし、
   長いコードになるほど、選択状態を調べるのが大変になります。
   そういうコードは質の低いものになりがちです。
 
(4)纏めると、セオリーどおり、
   With  Worksheets("Sheet1")
       .Range(.Cells(X,X),.Cells(Y,Y)) ・・・(略)
   End With 
とするか、
   Set ws = Worksheets("Sheet1") として
   ws.Range(ws.Cells(X,X),ws.Cells(Y,Y)) ・・・(略)
などとすることをお薦めします。
 

回答
投稿日時: 18/01/04 09:42:20
投稿者: WinArrow
投稿者のウェブサイトに移動

simpleさん、トピを立ち上げて頂きありがとうございます。
私の言いたいことをsimpleさんがすべてのべていますので、
追い打ちをかけるようになりますが、
 
なんとなくですが、目の前におきたことだけで、問題が解決した
というように書かれている。
しかし、目の前の「問題が解決した」ことと、「正しく理解した」は異なり、
正しく理解したというニュアンスが伝わってきません。
 
最初のスレにも書かせていただいたように
「過信」しているようにも見受けられます。
最初のスレに書かれていたコードがコピペではなかったので、
Sheet2をActivateしているのにエラーになる
ということは、実際のコードでは、Activateが抜けているのではないかと想像しました。
※実際は、シートモジュールに書かれていたんですね・・・
※ですが、コードの内容からシートモジュールに記述する必要性が感じられません。
 
 
基本的に
(1)複数のシートを扱うプロシジャは、標準モジュールに記述する
(2)シートのActivateやSelectは、不用意に使わない
を、常に心がけるようにお勧めします。
 
 
 

回答
投稿日時: 18/01/04 23:03:24
投稿者: ビタミンVBA

simpleさん、WinArrowさん、ご丁寧なアドバイスありがとうございます。
 
うまくかけるかどうかわかりませんが、今回のいわば初歩的な問題なのでしょうが、私には理解を深める例になりました。VBAのすべてを正しく理解できれば言うことはありませんが、(私の知識ではとても無理で)結果オーライで生半可なままでやってきたことが露呈したものです。
 
>simpleさん

引用:
少し開いたままにしておいていただけば、皆さんの助言が得られるはずです。
 自分の判断、行動に間違いはないと自信を持っているので、
 助言は要らないということですか?

>WinArrowさん
引用:
最初のスレにも書かせていただいたように
「過信」しているようにも見受けられます。

とんでもないです。自信も過信もないと思っていますが、そのように受け取られたらすみません。
 
複数のシートに似たような処理をすることが多いので、今までは標準モジュールに共通マクロを記述してきましたが、そのためうまく行っていたのですが、ご指摘のような仕様を理解していませんでした。
今回簡単に試すためにシートにマクロを書き、標準モジュールのマクロをコピーして使ったら、エラーになることにパニックになり、ヘルプも見ましたがエラー内容が理解できずに、質問させていただきました。
 
simpleさんのアドバイスでエラーがなくなったので、とりあえずすすめました。しかし、以前うまく行けたものがなぜエラーになるのかという疑問が残りましたので、改めていろいろ試して、シートマクロと標準モジュールのマクロでは有効範囲が違うという初歩的なことを思い出し、ようやく納得できたので整理したものを投稿させてもらいました(その時点でWinArrowさんのコメントに気付きましたが)。
 
>simpleさん
引用:
・シートモジュールに書かれているプロシージャでシートを省略した場合、
  そのプロシージャが書かれているシートを指すのが仕様です。
  選択されたシートは無関係。
  
・一方、標準モジュールのプロシージャ内でシートを省略した場合、
  その時にアクティブなシートを指す仕様となっています。
というのがルールです。
  
このことをしっかり理解してください。

はい、何とか理解したと思います。
 
>WinArrowさん
引用:
基本的に
(1)複数のシートを扱うプロシジャは、標準モジュールに記述する
(2)シートのActivateやSelectは、不用意に使わない
 を、常に心がけるようにお勧めします。

(1)に関してはそのように心がけています。
(2)もできるだけActivateやSelectはチラツキにもなるので使わないようにしたいのですが、デバッグの際にはちゃんと思った通りに動いているかの確認用にしばしば使います。スムーズに行くようになれば外すようにしています。
 
今回、「生兵法はケガのもと」という格言通りやったなと思います。しかし、これからも生兵法のままで行くのも確かです。つまづいて味わう挫折感とわかった時のやった感。趣味で作る役に立たないプログラムは家人からはバカにされますが、VBAは老人にとってのビタミン剤でもあります。
お二人のアドバイスにすべてお応えできていないかと思いますが、今後もよろしくお願いします。

回答
投稿日時: 18/01/05 02:06:12
投稿者: もこな2

ビタミンVBA さんの引用:

(2)もできるだけActivateやSelectはチラツキにもなるので使わないようにしたいのですが、デバッグの際にはちゃんと思った通りに動いているかの確認用にしばしば使います。スムーズに行くようになれば外すようにしています。

横から口出しになってしまいますが、たぶん上記のようなコメントですけど、お二方が心配されてるところの理解が十分に進んでないのかなぁと思います。
本トピックでsimpleさんが赤字で強調されてますけど上位オブジェクトの記述を省略した場合、どのように理解するのかExcel君の目線に立って考えてみるとヒントになるかもしれません。
(少なくともチラツキを気にしてコメントされてるのではないと思います。)
 
手前みそで恐縮ですが、別トピックで関係する部分の解説をしたので、リンクを置いておきます。
細かいところを間違ってるかもしれませんが、遠くはない・・・はずです。
(17/12/21 20:10:52 あたりの投稿をご参照願います。)
http://www.moug.net/faq/viewtopic.php?t=76592

回答
投稿日時: 18/01/05 11:14:44
投稿者: WinArrow
投稿者のウェブサイトに移動

2点追加のアドバイス
 
(

引用:
2)もできるだけActivateやSelectはチラツキにもなるので使わないようにしたいのですが、デバッグの際にはちゃんと思った通りに動いているかの確認用にしばしば使います。スムーズに行くようになれば外すようにしています。

 
デバッグには、ステっプ実行という便利な方法が用意されています。
「デバッグ用にシートを選択する」は、1つ方法かもしれませんが、
当該ステップを終了させる必要があります。終了するまでに内容が変わってしまうこともあり、
確認できないこともあり得ます。それに外すのを忘れてしまいます。
ステップ実行では、リアルタイムに確認が可能です。(例外もありますが)
 
引用:
今回、「生兵法はケガのもと」という格言通りやったなと思います。しかし、これからも生兵法のままで行くのも確かです。

 
私は、先入観が足を引っ張っていることが多く、何時も反省していますが、
なかなかこの生活習慣病は抜けないで困っています。
日々努力のみ
 
それから、独断と偏見ですが、VBA全てわかっている人なんていませんよ
安心して・・・・

投稿日時: 18/01/06 09:44:31
投稿者: simple

引用:
デバッグの際にはちゃんと思った通りに動いているかの確認用にしばしば使います。

その確認とは「コードが実行されている」ことの確認ですか?
それとも実際にそのシートがアクティブになっていることの確認ですか?
 
後者なら、シートの選択状態に依存したコードであるために必要となっているわけで、
依存しないコードであればもともと不要なものです。
 
前者のような、そのパスを通っているかの確認に使うのは邪道で、
ブレークポイント併用のステップ実行をすべきでしょう。
 
シートを選択したり、セルを選択しないようになってはじめて中級入りということかと
思います。頑張って下さい、というのも何かおかしいけれど。
 
どうですか、もう論点、質問はございませんか?

回答
投稿日時: 18/01/06 22:59:27
投稿者: ビタミンVBA

もこな2さん、WinArrowさん、simpleさん、いろいろとありがとうございます。
 
>もこな2さん

引用:
横から口出しになってしまいますが、たぶん上記のようなコメントですけど、お二方が心配されてるところの理解が十分に進んでないのかなぁと思います。
 本トピックでsimpleさんが赤字で強調されてますけど上位オブジェクトの記述を省略した場合、どのように理解するのかExcel君の目線に立って考えてみるとヒントになるかもしれません。
 (少なくともチラツキを気にしてコメントされてるのではないと思います。)

simpleさんが、赤字で強調されている部分の意味は理解したつもりでいます。今回の私の問題が上位オブジェクトの指定の仕方が要因であったと思ってますが、チラツキと結び付けて考えてはいません。
 
また参考に書いていただいたリンクのとピックスの内容は申し訳ありませんが、さっぱり理解できませんでした。ただ元のトピックスを書かれたkokoiさんの気持ちのことを教えていただいた例ならまったく同感できるものがあります。
 
>WinArrowさん
引用:
デバッグには、ステっプ実行という便利な方法が用意されています。

 
>simpleさん
引用:
その確認とは「コードが実行されている」ことの確認ですか?
それとも実際にそのシートがアクティブになっていることの確認ですか?
  
後者なら、シートの選択状態に依存したコードであるために必要となっているわけで、
 依存しないコードであればもともと不要なものです。
  
前者のような、そのパスを通っているかの確認に使うのは邪道で、
ブレークポイント併用のステップ実行をすべきでしょう。
  
シートを選択したり、セルを選択しないようになってはじめて中級入りということかと
思います。

 
開き直る訳ではないのですが、中級入りには程遠いのかなというくらい、シートやセルの選択をしていますね。。。(デバッグでステップ実行やブレークポイント、StopやOn Errrorなどもしょっちゅう使っていますが) 
私の場合、よくシートのセル範囲をレンジとして指定し、ソートしたり、範囲内のデータを処理するときに思うような結果が出ないことがよくあります。その時に正しく特定のシートやセル範囲になっているのかなと調べるためにActivateとSelectで確認するようにしています(他に確認する方法を知らないので)。
確認できればActivateやSelectを外しますが、外し忘れてちらついたり、違うシートに変わってしまい別のバグにしてしまうこともあります。すべてトップのシートから処理して終わってから結果のシートをActivateして確認するようにできればいいのでしょうが、無理だなあというのが実情です。
 
引用:
もう論点、質問はございませんか?

本件についてはありません。アドバイスしていただいた内容にあったレベルのレスポンスができませんので、違う方向になってしまう恐れもあるので、私はこの辺で失礼させていただきたいと思います。
いろいろとありがとうございました。またよろしくお願いします。

投稿日時: 18/01/06 23:18:56
投稿者: simple

一点だけ恐縮です。
 
# 初級者と決めつけているわけではないので、誤解なきよう。
# 不適切な言い方であれば失礼しました。
 
おっしゃるのは、
例えば、
Debug.Print r.Address(External:=True)
などとするかわりに、
Sheet2.Select
r.select
などとしてrの正しさを確認している、ということでしょうか。
いわば、デバッグの過程でしか使っていないと。
そういう方法もあるんでしょうかね。
 
とすると、前回の発言の中にある

With Worksheets(ShtName)
        .Activate
などというのも、
デバッグの過程で入れたものの消し忘れなんでしょうか。

回答
投稿日時: 18/01/10 18:21:09
投稿者: もこな2

引用:
引用:
横から口出しになってしまいますが、たぶん上記のようなコメントですけど、お二方が心配されてるところの理解が十分に進んでないのかなぁと思います。
 本トピックでsimpleさんが赤字で強調されてますけど上位オブジェクトの記述を省略した場合、どのように理解するのかExcel君の目線に立って考えてみるとヒントになるかもしれません。
 (少なくともチラツキを気にしてコメントされてるのではないと思います。)

simpleさんが、赤字で強調されている部分の意味は理解したつもりでいます。今回の私の問題が上位オブジェクトの指定の仕方が要因であったと思ってますが、チラツキと結び付けて考えてはいません。

解りづらくて済みません。
引用:
スムーズに行くようになれば外すようにしています。
先日は、この部分が気になってコメントをさせていただきました。
たとえば、「データ」シートに5時間掛かって入力したものを、「作業」シートに抽出作表するようなマクロを組んだとします。
そして、作業シートの表が要らなくなったら、そっくり削除するマクロを標準モジュールに、このように作ったとします。
Sub 作業シートをすっきりさせるマクロ()
    Cells.Delete
End Sub
これが、動くか動かないかと聞かれれば、シート保護でもかかってない限り動くとおもいます。
ですが、このマクロの問題は作業シートじゃなくても、正常に動いてしまうことなんです。
5時間かけて入力した「データ」シートでうっかり実行してしまったら・・・・マクロで行った作業は元に戻せませんので、5時間前に逆戻りです。(保存しなければ最後に保存したところまではもどれますが)
さらに言えば、他のブックからでも動かせますので、重要なブックを開いて作業してるときにうっかりこのマクロ動かしたら・・・・考えただけで恐ろしいです。
 
では、こうすればいいんじゃないかって考えかもあるかもしれませんが、
Sub 作業シートをすっきりさせるマクロ()
    Worksheets("作業").Activate
    Cells.Delete
End Sub
これはこれで、作業シートがなんかのトラブルでActiveにならなかったり、別ブックの作業シートの操作をしてしまったりする可能性がごくわずかですが存在します。(既にコメントされてますが、シートを切り替えてる時点で実行速度も低下します。)
なので、技術的にはできるんですが、意図的に対象のオブジェクトは明記Active○○を操作対象にすることは極力さける方が、安全というか確実ですよ。ということを皆さんお伝えしたいんじゃないかとおもいます。(でないと私みたいに自分のデータを自分で消して途方に暮れることになりかねません)
それは承知のうえということであれば、お目汚し失礼しました。
 
 

投稿日時: 18/01/11 00:15:58
投稿者: simple

皆さんからの助言を参考にしてください。
それではこれで閉じます。