[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]
『Rangeの中のCells』(k)
Sub 請求書作成()
Dim rowsData As Long, rowsClient As Long
rowsData = wsData.Cells(Rows.Count, 1).End(xlUp).Row '請求データの最終行数
rowsClient = wsClient.Cells(Rows.Count, 1).End(xlUp).Row '取引先マスタの最終行数
'年月を入力ダイアログで入力
Dim dayCutoff As Date
dayCutoff = Application.InputBox("年月を入力してください", "対象年月を入力", Format(Date, "yyyy/mm"))
Dim n As Long
For n = 2 To rowsClient
Dim client As String client = wsClient.Cells(n, 1).Value
'ひな形ブックを開きそのシートとともにセットする Dim wb As Workbook, ws As Worksheet Set wb = Workbooks.Open(ThisWorkbook.Path & "\請求書ひな形.xlsx") Set ws = wb.Worksheets(1)
'該当の取引先かつ年月のデータを転記する Dim i As Long, k As Long k = 21 For i = 2 To rowsData
If wsData.Cells(i, 2).Value = client Then Dim deliDate As Date deliDate = wsData.Cells(i, 1).Value
If Year(deliDate) = Year(dayCutoff) And Month(deliDate) = Month(dayCutoff) Then ★ wsData.Range(wsData.Cells(i, 3), wsData.Cells(i, 5)).Copy ws.Cells(k, 1) k = k + 1 End If End If Next i
'その他の転記と行の非表示 ws.Rows(k & ":50").Hidden = True 'データがない行を隠す
ws.Range("A18").Value = "ご請求金額:" & Format(ws.Range("D54").Value, "#,##0") & " 円" ws.Range("A3").Value = client & "御中" '取引先名 ws.Range("A5").Value = "〒" & wsClient.Cells(n, 2).Value '郵便番号 ws.Range("A6").Value = wsClient.Cells(n, 3).Value '住所1 ws.Range("A7").Value = wsClient.Cells(n, 4).Value '住所2 ws.Range("D15").Value = DateSerial(Year(dayCutoff), Month(dayCutoff) + 1, 0) '請求日 ws.Range("D16").Value = DateSerial(Year(dayCutoff), Month(dayCutoff) + 2, 0) 'お支払期限
'ファイル名を生成して保存して閉じる Dim fileName As String fileName = ThisWorkbook.Path & "\" & Format(dayCutoff, "yyyymm") & "請求書_" & client & ".xlsx" wb.SaveAs fileName wb.Close
Next n
End Sub
★の部分なのですが
★wsData.Range(wsData.Cells(i, 3), wsData.Cells(i, 5)).Copy ws.Cells(k, 1)
wsData.Range(Cells(i, 3), Cells(i, 5)).Copy ws.Cells(k, 1)
と書いたらエラーがでてしまいました。wsData.を入れないと動かないのはなぜでしょうか?宜しくお願い致します。
< 使用 Excel:Office365、使用 OS:Windows10 >
(γ) 2020/08/27(木) 15:59
どこまでも突き詰めたいなら、どなたが回答して下さるかもしれないが、
結局はそう決めたんだ、というところに落ち着くことになる。
なので、上の話は聞かなかったことにして、
ひとまず、2020/08/27(木) 15:59の回答で、
ああそうなのね、と鞘に収めるのが妥当なところかもしれません。
(γ) 2020/08/27(木) 16:07
97のころは、 Activesheet.range(Cells(1,1),Cells(3,3)) でも、エラーになってましたから、指定するなら外も中も同じ物を指定するようにしました。 (BJ) 2020/08/27(木) 20:33
あこっちか
With Activesheet .range(Cells(1,1),Cells(3,3)).select End with (BJ) 2020/08/27(木) 20:36
>Range(wsData.Cells(i, 3), wsData.Cells(i, 5)).Copy ws.Cells(k, 1) >と書いたら、同じ理屈?でエラーになりそうなものだが、エラーにはならない(たぶん)。 これがエラーにはならない理由は、 Application.Range(wsData.Cells(i, 3), wsData.Cells(i, 5)).Copy ws.Cells(k, 1) だからですよね? (´・ω・`) 2020/08/28(金) 09:17
(γ) 2020/08/29(土) 07:06
>この話は、別のところでもよく議論になりますが、
昔、私も余所でやったことがありますが、今でも腑に落ちない部分があります。
通常、この解釈がまかり通っていますよね? | (Application.Rangeのヘルプにも書かれていることです。) ↓ 「オブジェクト修飾子を指定せずにRangeプロパティを使用すると、 ActiveSheet.Range のショートカットとなります。」
ところが、このタイプの時だけ、Applicationが省略された処理になるのは何故か?(その記述がどこにも見当たらない) ↓ Range(cellオブジェクト, cellオブジェクト)
(半平太) 2020/08/29(土) 10:43
そもそもそんな記述する機会がなかったのですが、
>ところが、このタイプの時だけ、Applicationが省略された処理になるのは何故か? Range(ws.Cells) Range(ws.Range) Range(ws.Range, ws.Range) Range(変数, 変数) または↑の組合せ の場合はまた違った結果なんですかね? 今日パソコンのないところにいるのでためせないんですが。 (稲葉) 2020/08/29(土) 11:52
>オブジェクト修飾子を指定せずにRangeプロパティを使用するとActiveSheet.Range は、
オブジェクト修飾子を指定せずにRange(Cells)プロパティを使用すると Application.Range(Cells) と解釈され、 Application.Range(Cells) は ActiveSheet.Range(Cells) を返す。
と2段階なんだとおもいます (´・ω・`) 2020/08/29(土) 12:17
稲葉さん > Range(ws.Cells) > Range(ws.Range) 二つの違いがわからないです。 そのまんまじゃないですよね?
(´・ω・`)さん > オブジェクト修飾子を指定せずにRange(Cells)プロパティを使用すると > Application.Range(Cells) と解釈され、 > Application.Range(Cells) は ActiveSheet.Range(Cells) を返す。
「Cells」の部分を実際に動く記述で教えて頂けませんか?
(半平太) 2020/08/29(土) 14:23
↑こういう文章は饒舌だと思います。
質問も結局くどくどwsDataを何度も書きたくないということが発端だと思います。
で、省略した結果、記述に矛盾が出てきてエラーになる羽目に。
Application.Range(wsData.Cells(i, 3), wsData.Cells(i, 5))
僕は普段からこう書きます。
(というか、wsData.Cells(i, 3).resize(,3)こう書くかな。)
なぜなら、
そもそも、Cells等が返すRangeオブジェクトには、
そのセルが属するシートやブックの情報があるので、
1回「このセル範囲」と決めたら、あとは、「これ」というだけで、
そのセルが属するシートの話をしなくても話が通じるので、
「このセルからこのセルの範囲」というときは、シートの話をするのは無駄だし、
間違いのもとになるからです。
なので、セル範囲を変数に代入することを覚えることをお勧めします。
>rowsData = wsData.Cells(Rows.Count, 1).End(xlUp).Row
↑
Rows.countはどのシートの行数を数えるか明示してないので、
エクセル君が勝手に判断することになります。
dim c as range
set c = Cells(Rows.Count, 1).End(xlUp)
msgbox c.worksheet.name
msgbox c.address(,,,true)
こんなことを試してみることをお勧めします。
あと、ステップインで実行しながらローカルウィンドウで、
変数cの中身を確認してみてください。
変数の左の「+」をクリックすることで中身を展開できます。
(「オブジェクト」というものの概念がもしかしたら理解できるかも)
この件に関しては様々な意見があると思いますし、
僕の説明も言葉が足らない部分もあるとは思います。
ぼくの意見が正しいというわけでもないけど、参考になれば。
(まっつわん) 2020/08/29(土) 16:20
半平太さん ごめんなさい。書き方がわるかったです。
オブジェクト修飾子を指定せずにRangeプロパティを使用すると Application.Range と解釈され、 Application.Range は ActiveSheet.Range を返す。
同様に
オブジェクト修飾子を指定せずにCellsプロパティを使用すると Application.Cells と解釈され、 Application.Cells は ActiveSheet.Cells を返す。
です。 (´・ω・`) 2020/08/29(土) 17:16
(´・ω・`)さん
すみません。勘違いしました。m(__)m
全て、Applicationが省略されていると考えるのが正しいですね。
Activesheetが省略されているのと言って構わないケースが多いが、 2つの引数がRangeオブジェクトの場合は、 その2つがActivesheetのものじゃない限り、Activesheetの省略であるハズがない。
ありがとうございました。
(半平太) 2020/08/29(土) 19:33
なるほど! そこまでお節介やいてくれるなら Sheets(1).Range(cells(1, 1)) こういうのはSheetsオブジェクトを省略しても通じてほしいですよね (稲葉) 2020/08/29(土) 21:03
(1) Excel.Global のメンバー Property Range(Cell1, [Cell2]) As Range (2) Excel.Application のメンバー Property Range(Cell1, [Cell2]) As Range (3) Excel.Worksheet のメンバー Property Range(Cell1, [Cell2]) As Range という少なくとも3つのRangeがありますね。(オブジェクトブラウザ参照)
コードを、何の修飾もなく Rangeで始めた場合は、(1)が適用されます。 Excel.rangeとか、Application.Rangeとかは(2)です。 エラーになったというのは、無論(3)ですね。 シートの食い違いが起きうるのは、(3)の時だけです。
■ | 97のころは、 | Activesheet.range(Cells(1,1),Cells(3,3)) | でも、エラーになってましたから、指定するなら外も中も同じ物を指定するようにしました。 私はそういう記憶がなかったですが、そうすると当初は、 Range(ws.Cells(1,1),ws.Cells(3,3))という書き方が主で、 その後、 ws.Range(Cells(1,1),Cells(3,3))に移っていったのでしょうか(自信なし)。
■ Range(ws.Cells(1,1),ws.Cells(3,3))を使っている方がおられましたが、 少なくとも質問掲示板では余り多くない印象です。
その理由についての私見ですが、 Set R = Worksheets("Sheet1").Range("A1") というようにワークシートを頭につけて、修飾するのが分かり易いわけで、 そうすると、それで統一したほうが可読性は高まるのではないか、という気持ちから、 ws.Range(Cells(1,1),Cells(3,3))と書くことが一般的なのではないでしょうか。 さらにエラー対応を考慮して、 ws.Range(ws.Cells(1,1),ws.Cells(3,3)) となったのではないか、と思います。
# 稲葉さん、Range(Cells(1,1)) という書き方はそもそもエラーですよ。うっかりミスでしょうね。
(γ) 2020/08/29(土) 21:36
γさん > コードを、何の修飾もなく Rangeで始めた場合は、(1)が適用されます。 > Excel.rangeとか、Application.Rangeとかは(2)です。
はー、そうなんですか。またまた分からなくなったです。 どこで差が出るんでしょうか?(無理にご返信は要らないですが)
稲葉さん 今回は、引数を2つ持つときの話がメインとなっています。 1つの時は、Range型は引数にできません。
その時は、cells(1, 1)の標準プロパティが採用されることになるので、 A1セルにアドレスを指定する文字(例えば「Z9」とか)が入って無い限り、エラーになります。 たまたまエラーにならなかったとしても、期待する結果ではないと思います。
(半平太) 2020/08/29(土) 21:57
>(1)と(2)は実質的には同様の機能と思います。 >(グローバルというクラスで定義されていないものは、文頭からは使えないということですね。 > Rangeはグローバルの配下にあるので、頭からRangeと書き始めることができます。)
と言うことは(省略と言う言葉の問題かも知れませんが)、
> Range(ws.Cells(1,1),ws.Cells(3,3)) この書き方は、オブジェクト修飾子が省略されている訳ではないのでそのままでよく、
Application.Range(ws.Cells(1,1),ws.Cells(3,3)) とわざわざ正規っぽく書く必要はないし、 ws.Range(ws.Cells(1,1),ws.Cells(3,3)) と屋上屋を重ねる意味も「本来」ない。
という理解でいいですかね。(無理にご返信は要らないですが)
ただ、私の場合、2つの引数がRangeオブジェクトになることはそう多くないので 通常は、こう書かざるを得ないことが多いです。(頭のws.がないと"A1"がActiveSheetのA1セルと解釈されるので) ↓ ws.Range("A1",ws.Cells(3,3))
(半平太) 2020/08/30(日) 09:34
前半に書かれた点は、私は自己流にそう考えていました。 そう解釈しないと現実が説明つかないと思うのです。 つまり、Application.Rangeのヘルプ(*)には、 省略時はActivesheetと解釈すると書かれていますが、 それだと、 Range(ws.Cells(1,1),ws.Cells(3,3)) としたときに、wsがアクティブシートでなければ、 エラーにならないといけないと思うのですね。 しかし、実際には有効に機能する、という現実があります。
(私は、その記法を推奨しているわけではなく、既に書いたように、 統一して、Rangeの頭にシートの指定をしたほうが一貫性はあるのかなと思っています。)
後半の部分は、どう書くんでしょうか。 まっつわんさんにお聞きしたいですが、 Range(ws.Range("A1"),ws.Cells(3,3)) と書くんでしょうか。
(*)例文がWorksheet.Rangeのものであったりして、精度は余り高くない印象です。 現実を説明できないところは、ユーザーとしては現実を認めていくしかないのかな、と。 (γ) 2020/08/30(日) 11:04
> 後半の部分は、どう書くんでしょうか。
あ、すみません。そこは悩んでないところです。それしかないと思っていますので。
実際はこんな形で使っています。
With ws Debug.Print .Range("A1", .Cells(.Rows.Count, "A").End(xlUp).Offset(, 3)).Address End With
(半平太) 2020/08/30(日) 12:00
(HAN) 2020/08/30(日) 12:39
GlobalとApplicationの違いが何処かにありはしないかテストしていたら、 コードを書く場所が、シートモジュールの場合、差が出ました。ご参考まで。
このコードをWorksheets(2)に書くと、Globalはエラーになります。 ↓ Sub texOnSheet() Dim r1 As Range Dim r2 As Range
Set r1 = ThisWorkbook.Worksheets(1).Range("B2") Set r2 = ThisWorkbook.Worksheets(1).Range("E4")
Debug.Print Application.Range(r1, r2).Address 'OK Debug.Print Range(r1, r2).Address 'Error End Sub
(半平太) 2020/08/31(月) 08:33
>後半の部分は、どう書くんでしょうか。 >まっつわんさんにお聞きしたいですが、 >Range(ws.Range("A1"),ws.Cells(3,3)) >と書くんでしょうか。
そうです。
>With ws > Debug.Print .Range("A1", .Cells(.Rows.Count, "A").End(xlUp).Offset(, 3)).Address >End With
with worksheets(1) debug.print application.range(.range("A1"),.cells(.rows.count,"A").end(xlup).offset(,3).address end with
こう書きます。
「application.range(・・・)と書くよ」と発言したら、別の掲示板でもちょっと叩かれましたが、
たくさんの自分の失敗と、たくさんの掲示板の質問と回答
(特に識者(γさんや半平太さんも含む)の考察)を見てきた中での、
現時点での自分なりの結論です。
「一貫性」っていうなら、
>sheets("Sheet1").Range("Sheet1!$A$1", sheets("Sheet1").Cells(.Rows.Count, "A") って書くのが筋では?と感じてます。
あと、
sheets(1).range(sheets(2).range("A1"),sheets(3).cells(3,"C").select
っても書いちゃったときに、
シートの整合性をチェックする箇所が3か所になります。
application.range(sheets(2).range("A1"),sheets(3).cells(3,"C").select
ならば、かっこの中1か所だけチェックしたら済むと思います。
というか、セル範囲に何かしたいときは、ワークシートを代入する変数は使いません。
Option Explicit
Sub test() Dim rngTop As Range Dim rngBottom As Range Dim rngTable As Range Dim r As Range
With Worksheets(1) Set rngTop = .Range("A3") Set rngBottom = .Cells(.Rows.Count, "C").End(xlUp) End With Set rngTable = Application.Range(rngTop, rngBottom)
For Each r In rngTable.Rows MsgBox r.cells(1).Address(False, False) Next
With rngTable.Worksheet .Range("D10").Select End With End Sub
また、
Range(ws.Range("A1"),ws.Cells(3,3))
のように親オブジェクトを省略すれば、
見た目違和感ありありですし、
思いもよらぬ解釈をエクセル君にされる可能性を否定できないので、
明示する必要があるというのは、半平太さんの実験でも明らかです。
Debug.Print Range(r1, r2).Address 'Error
は、
Debug.Print Me.Range(r1, r2).Address 'Error と解釈されるようですね。
kさんへ>>
回答は、ひとつの見解であり様々な意見があります。
なので、広く回答を募る中で自分で判断し取捨選択されるようにするといいと思います。
僕の意見は、異端のようなので、切り捨てても問題ないです。
が、
VBAのコードは様々な場面で省略して記述することが可能になっています。
知っていて省略するのと、知らないで「こういう時はこう書くんだ」と丸暗記しているのでは、
大きな違いがあります。
なので、こういう質問はすごく有意義だと思います。
どんどん、質問して見識を深めていってください^^
回答側も意見の交換が出来て大変有意義だったと思います^^
(こちらも、考えを言語化できて整理が出来ました^^)
参考になれば。
(まっつわん) 2020/08/31(月) 10:38
誤作動の理解はできたのですが、
wsData.Rangeと最初に指定していると思うのですが、なぜRangeの中になるとActivesheetと、判断するのでしょうか?
回答は、全て読んだのですが、自分の理解力のなさで、この回答がすでに書かれていたらすみません。
(k) 2020/08/31(月) 11:47
>wsData.Rangeと最初に指定していると思うのですが、 >なぜRangeの中になるとActivesheetと、判断するのでしょうか?
お気持ちは分かりますが、 最初に指定したシートの効力が、カッコの中にまで及ぶのは、 引数が「アドレス文字の場合」であり、
カッコの中が「2つのRangeオブジェクトの場合」は、 まず、そのオブジェクトを評価するという仕様になっているだけです。
また、カッコの中に入ったからActiveSheetと判断した訳じゃなく、 Cellsにシートの修飾子がないから一般則に従ってActiveSheet上と判断された訳です。
※もし、コードがシートモジュールに書かれたのであれば、 そのSheet上と判断されます。アクティブであろうがなかろうが(念の為)
(半平太) 2020/08/31(月) 14:34
# 半平太さんから適切なコメントがあり、ちょっと重複が生じてしまいましたが、そのまま投稿します。
半平太さん ご指摘ありがとうございました。 シートモジュール(というある種のクラスモジュール)でのふるまいは また別の要素が入ってくるのですね。勉強になります。
まっつわんさん、コメントありがとうございました。 生粋のapplication.range派であることが理解できました。
# Range(ws.Range("A1"),ws.Cells(3,3))と書くと、頭のRangeのシート指定がないじゃないか、 # と脊髄反射を起こしてしまいがちな人も多いかも知れませんね。 # (これが統一性ということにつながるのかどうか) # また、 # application.range(.range("A1"),.cells(.rows.count,"A") # といった書き方は、将棋で言う「敵の打ちたいところに打て」を想起させますね。 # 予め、誤解が起きないように、application.rangeと書いておいて、入り込まないようにする # ということですか。単なる感想です。
質問者さんへ: | wsData.Rangeと最初に指定していると思うのですが、なぜRangeの中になるとActivesheetと、判断するのでしょうか?
ひとことで言えば、そのように決めた製品だから、ということでしょう。 (また、より正確に言うと、シートモジュールの時は、Activesheetでなくそのシートと判断)
Worksheet.Rangeの中で使用するRangeオブジェクトのワークシート帰属をどう定めるか、 それは、質問者さんの考える方法もあり得たでしょう。 そのほうが気が利いているとも思います。 しかし、マイクロソフトはそうは考えなかった。 色々な内部事情があるのかもしれません。 結果として、現在のような仕組みを採用した、ということです。 採用理由はマイクロソフト社にしか回答できないでしょう。
くどいですが、これはユーザー側が論理で導き出せる話ではなく、 そう決めたから、という話なので、 ユーザーとしては、そういう振る舞いをすることを知って(このことが重要)、 それを使いこなすことに注力するほかないと思われます。 例えば、Withステートメントを使っていかに冗長度を低減するかとかの工夫も、 「使いこなす」の中に含まれるでしょう。 (γ) 2020/08/31(月) 14:47
>wsData.Rangeと最初に指定していると思うのですが、なぜRangeの中になるとActivesheetと、 >判断するのでしょうか? >回答は、全て読んだのですが、自分の理解力のなさで、この回答がすでに書かれていたらすみません。
なぜと言われてもそういう風にExcelVBAが作られているとしか言いようがないかと。
使う側はその振る舞いを理解し使いこなすことしかできません。
Cells(1,1)と書いたら、
それは、何かのプロパティの括弧の中であっても、外であっても、
「アクティブシートの1行目の1列目」という意味になります。
それ以外の事はありません。
「(シートの修飾を)省略している」ということは、
読み手に(今回の場合はエクセル君)に解釈を任せるということなので、
書き手と読み手で齟齬が起きるかも知れないということは当然の事だと思います。
再度書きます。
>rowsData = wsData.Cells(Rows.Count, 1).End(xlUp).Row
rowsData = wsData.Cells(activesheet.Rows.Count, 1).End(xlUp).Row
の意味になります。
シートの行の数を数えるだけですので、どのシートで数えてもいいようなものですが、
同じエクセルのファイルでも、旧バージョンのシートの行数と新バージョンのシートの行数が
違う場合がありますので、エラーの温床になります。
rowsData = wsData.Cells(wsData.Rows.Count, 1).End(xlUp).Row
などと書いておいた方が今後バージョンアップでエクセルのシートの行数が変わっても、
戸惑うことが少ないと思います。
(まっつわん) 2020/08/31(月) 17:34
こちらは解決されました?
まずは途中で変な茶々入れたのでお詫びを・・・ To All 申し訳ございませんでした。
今回の疑問とは直接関係ないですが、色々なやり方あるよってことで、参考までに・・・ Sub test1() Dim i As Long Dim ws As Worksheet Set ws = Sheets(1) Sheets.Add.Activate For i = 1 To 10 Debug.Print ws.Range(Cells(i, "C").Address, Cells(i, "E").Address).Address & ":文字列" Debug.Print ws.Range(ws.Cells(i, "C"), ws.Cells(i, "E")).Address & ":Rangeオブジェクト" With ws Debug.Print .Range(.Cells(i, "C"), .Cells(i, "E")).Address & ":Rangeオブジェクト With省略" End With Debug.Print ws.Range("C" & i, "E" & i).Address & ":文字列2" Debug.Print ws.Range("C1:E1").Rows(i).Address & ":Range&Rows" Debug.Print ws.Rows(i).Columns("C:E").Address & ":Range&Columns" Next i End Sub
あと、色々やってて、これだとオブジェクトエラーじゃないんだなと、新たに発見。なぜかは不明。 Sub a() On Error Resume Next Debug.Print Range([Sheet1!a1], [Sheet2!a1]).Worksheet.Name Debug.Print Err.Description On Error GoTo 0 End Sub 「指定した名前のアイテムが見つかりませんでした。」
(稲葉) 2020/08/31(月) 18:13
[ 一覧(最新更新順) ]
YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki.
Modified by kazu.