テーブルに新しいデータを追加するには、どうしたらいいでしょう。まず、テーブルの行はListRowで表されます。ListRowの集合体がListRowsコレクションです。このListRowsコレクションのAddメソッドを使うと、テーブルの最下行に新しい行が追加されます。
Sub Sample1() Range("A1").ListObject.ListRows.Add End Sub
新しい行を追加したことで、このテーブルには全部で「10個」の行(ListRow)が存在することになりました。いま新しく挿入した行が10番目です。この「10番目の行」は ListRows(10) で表されます。
Sub Sample2() Range("A1").ListObject.ListRows(10).Range.Select End Sub
この「10」は「ListRowの個数」でしたね。ListRowの個数は、ListRows.Count で調べることができます。したがって、上記のコードは、次のように書けます。
Sub Sample3() Dim N As Long N = Range("A1").ListObject.ListRows.Count Range("A1").ListObject.ListRows(N).Range.Select End Sub
以上を踏まえると、テーブルに新しい行を追加して、そこに新しいデータを代入するには、次のように考えられます。もう一度、データ件数9件の状態からやってみます。
Sub Sample4() Dim N As Long Range("A1").ListObject.ListRows.Add N = Range("A1").ListObject.ListRows.Count With Range("A1").ListObject.ListRows(N) .Range(1) = "2018/12/10" .Range(2) = "広瀬" .Range(3) = "C" .Range(4) = "1000" End With End Sub
できました。できましたけど、これ面倒くさくないですか?まぁ、考え方としては間違っていないんですけど、テーブルに新しいデータを追加するのって、おそらく実務ではよくある操作だと思います。そのたびに、毎回こんなことをしないといけないのでしょうか。
手動操作で、テーブルに新しいデータを追加するときって、いちいちテーブルの範囲を広げてから入れませんよね。テーブルの真下セル(←ここはテーブルではない)に、いきなりデータを入れます。そうするとExcelが自動的にテーブルの範囲を拡張してくれます。手動操作でそうやるのですから、マクロでも同じに考えていいはずです。
問題はこのセル(今回はセルA11)を、どうやって自動的に取得するかです。今まででしたら、Endモードを使って、A列の一番下から上にジャンプして、行き着くであろうセルの1つ下という考え方で特定しました。
Sub Sample5() Cells(Rows.Count, 1).End(xlUp).Offset(1, 0) = "2018/12/10" End Sub
しかし、これは古いというか、通常のワークシートでセルを特定する考え方です。ちなみに、書き方によっては、この方法だと失敗します。たとえば次のような感じです。
Sub Sample5() Cells(Rows.Count, 1).End(xlUp).Offset(1, 0) = "2018/12/10" Cells(Rows.Count, 2).End(xlUp).Offset(1, 0) = "広瀬" Cells(Rows.Count, 3).End(xlUp).Offset(1, 0) = "C" Cells(Rows.Count, 4).End(xlUp).Offset(1, 0) = "1000" End Sub
このマクロを実行すると、下図のようになります。
テーブルはデータベース領域なのですから、ここは発想を変えるべきです。EndモードやOffsetを使わないで、セルA11を特定してみます。
テーブルの1列目(左端の列)は、ListColumns(1) です。この列の全セルは、ListColumns(1).Range で表されます。ここのCountプロパティを調べれば、この列に何個のセルがあるか分かります。
Sub Sample6() MsgBox Range("A1").ListObject.ListColumns(1).Range.Count End Sub
列の一番下セルということは、列内で10番目のセルということですから、次のように特定できます。
Sub Sample7() Dim N As Long N = Range("A1").ListObject.ListColumns(1).Range.Count Range("A1").ListObject.ListColumns(1).Range(N).Select End Sub
今選択されているセルは、Range(10) です。で、ここからがちょっと難しいのですが。実はこの状態でも、Range(11)を操作できます。列内のセルは10個しかありません。でも、11個目のセルを指定できるんです。
Sub Sample8() Dim N As Long N = Range("A1").ListObject.ListColumns(1).Range.Count Range("A1").ListObject.ListColumns(1).Range(N + 1).Select End Sub
セルが10個しかないのに11番目を操作できるなんて、ちょっと不思議に感じるでしょうけど、実はExcelでは昔からこういう操作ができました。たとえば、次のようなコードです。
Sub Sample9() Dim A As Range Set A = Range("A1:A3") A.Select A(4) = 100 End Sub
オブジェクト変数Aには、セル範囲A1:A3という「3個のセル」を代入しています。でも「A(4)」として、代入した範囲外も操作できます。1つ下だけじゃありません。次のようなコードも問題なく動きます。
Sub Sample10() Dim A As Range Set A = Range("A1") A(3) = "田中" A.Item(4) = "鈴木" A.Cells(4, 2) = "佐々木" End Sub
「なぜ、こうなるのか」「なぜ、これができるのか」は、考えてはいけません。あなたのためです。こうした動作の仕組みを理解しようとするなら、そもそも「Rangeって何?」という迷宮に足を踏み込まなければなりません。これは、決して開けてはいけない扉です。ここは「なぜ、こうなるのか」「なぜ、これができるのか」と悩むところではありません。「なんか不思議~よく分かんないけど、できるんだ~へぇ~」と飲み込むところです。この仕組みを使うと、今回の課題である、テーブルの下のセルを簡単に特定できるんです。
話を戻しましょう。テーブルに新しいデータを追加するには、次のように考えます。
Sub Sample11() Dim N As Long With Range("A1").ListObject N = .ListColumns(1).Range.Count .ListColumns(1).Range(N + 1) = "2018/12/10" .ListColumns(2).Range(N + 1) = "広瀬" .ListColumns(3).Range(N + 1) = "C" .ListColumns(4).Range(N + 1) = "1000" End With End Sub
実際には、このように1セルずつ代入することは希かもしれません。もし別の表からデータをコピーする場合でも、上でやったように「テーブルの左下のセル」を特定できれば、あとは何とかなるでしょう。ちょっと、やってみましょうか。
アクティブシートに下図のようなテーブルがあります。
同じブック内のSheet2に、下図のような普通の表があります。こっちはテーブルではありません。このSheet2の表から、[名前]列が"田中"のデータだけ、アクティブシートのテーブルに追加します。
Sub Sample12() Dim LC As ListColumn, N As Long Set LC = Range("A1").ListObject.ListColumns(1) N = LC.Range.Count With Sheets("Sheet2") .Range("A1").AutoFilter 2, "田中" .Range("A1").CurrentRegion.Offset(1, 0).Copy LC.Range(N + 1) End With End Sub
じゃ、コピー元もテーブルだったら。
Sub Sample13() Dim LC As ListColumn, N As Long Set LC = Range("A1").ListObject.ListColumns(1) N = LC.Range.Count With Sheets("Sheet2").Range("A1").ListObject.DataBodyRange .AutoFilter 2, "田中" .Copy LC.Range(N + 1) End With End Sub
こんな感じですかね。
テーブルに新しいデータを追加するときに、注意しなければならないテーブルのバグがあります。もう、こちらは言い逃れができない、誰がどう見てもバグです。まずは、手動操作でやってみましょう。
下図のような簡単なテーブルを作りました。この後のことがあるので、装飾は「なし」にしています。
このテーブルに、手動で塗りつぶしの色を設定します。ここでは分かりやすくセルの塗りつぶしで解説しますが、ほかの書式でも同じ現象が起こります。
この状態で新しいデータを追加すると、テーブル内で設定した書式(塗りつぶし色)が引き継がれます。
これはいいです。正常です。では、テーブル内に設定されている書式を消します。塗りつぶし色が設定されているセルを選択して、リボンのボタンで[塗りつぶしなし]をクリックします。
もう新しいデータを入力しても、書式が引き継がれません。そりゃそうです。引き継ぐべき書式(塗りつぶし)は、もう設定していないのですから。
さあ、いよいよバグのご紹介です。同じ手順をもう一度繰り返します。まずテーブル内に書式(塗りつぶし)を設定します。
新しいデータに書式が引き継がれます。
テーブルに設定されている書式を消します。ただし、ここからの手順が違います。セルの書式をクリアするには、たとえば塗りつぶしなら、リボンの[塗りつぶしなし]ボタンや、[セルの書式設定]ダイアログボックスから消すことができます。あるいは、ほかにも手があります。何も書式が設定されてないブランクセルの書式をコピーする方法です。使用していないブランクセル(ここではセルD1)を選択して[書式のコピー/貼り付け]ボタンをクリックします。
マウスポインタの形がハケに代わります。ドラッグしてセルを選択すると、選択したセルにブランクセルの書式(つまり何もない)がコピーされます。
どちらも同じことですよね。少なくとも、見た目的には変わりません。でも、新しいデータを入力すると、消したはずの書式が引き継がれてしまいます。これは、あり得ないでしょう。これ、マクロで代入しても、同じように消したはずの書式が引き継がれしまいます。
Sub Sample14() Dim N As Long With Range("A1").ListObject N = .ListColumns(1).Range.Count .ListColumns(1).Range(N + 1) = "小島" End With End Sub
本件は、セミナーを受講された方から「なんかぁ~ときどきぃ~書式が設定されちゃうことがあるんですよぉ~」と相談されました。んなわきゃーないだろと思いましたが「じゃ、ちょっと調べてみますね」と。で、コメダ珈琲でコーヒー飲みながら確認したら、一発で再現できました。思わずコーヒーを吹き出しそうになりました。「書式のコピー/貼り付け」で起きるという発生要因を突き止められたのはラッキーです。私は、自作のアドインで「書式のコピー/貼り付け」にショートカットキーを割り当てています。いつものようにそれで消したら再現したわけです。調べたら、普通に[塗りつぶしなし]だと問題ないと。早々に修正していただきたいですね。