ゆんの業務改善ブログ

①生産性向上 ②業務改善 ③自動化 について情報発信しています。VBAプログラムは本当の初心者から他のアプリケーションを呼び出して使う上級者的な使い方まで幅広いレベルで解説していきます。

VBA オブジェクトの階層構造

今回はオブジェクトの階層構造について解説します。オブジェクトの階層構造を理解しておくとWith~End Withステートメントの理解がスムーズになります。ついでに不要なSelectやActivateから卒業しましょう。ここで知識を整理しておきましょう!

この記事は初心者~中級です。
レベルについてはExcel VBAの実力(レベル)を定義してみる 初心者~三段をご参照ください。

目次

オブジェクトの階層構造

オブジェクトの階層構造とWith~End Withステートメントは別々に解説さえることが多いです。この記事では続けて解説することによって相互の理解を深めることを目指します。

まずはオブジェクトの階層構造について解説します。

ExcelのWorkbookオブジェクトの階層構造
ExcelのWorkbookオブジェクトの階層構造

上は、Excelのワークブックのオブジェクトの階層構造をイメージで示したものです。ピラミッドを上から見たところを想像して下さい。エクセル王国のワークブック王のピラミッドです。等高線のようになっていますよね。広いほど裾野が広いイメージです。

エクセルの中には様々なオブジェクトが存在しています。その中のWorkbook王のピラミッド(=ワークブックオブジェクト)にはいろいろなオブジェクトが存在し、その中の一つであるSheetsオブジェクトには、色なオブジェクトが存在し、その中の一つにRangeがあります。

標準モジュールで、単にRange("A1")と書けば、アクティブなシートのA1セルという意味でした。エクセルさんが、気を利かせてどのブックのどのシートのRangeなのかを補完してくれているわけですね。

これが便利なので、つい、操作するシートをアクティブにするActivateを使ってしまいがちです。そもそも階層構造を使えばActivateする必要がなくなるので、無駄な処理をせずに済みます。シートへの参照すらムダ、という意識の高い型はシートの中身をまず配列に入れてしまう、という工夫が考えられます。この記事は配列は解説外なので、割愛します。

オブジェクトの階層構造を意識して変数をうまく使う

この階層構造を理解すると、よりムダのない処理ができるようになり、Activateというフォーカスを変えるだけのメソッドを使う機会を最小にできます。先ほどのオブジェクトの階層構造のピラミッドに横から見たところを追加した図が下の図です。

オブジェクトの階層構造ピラミッドの等高線と横から見たところのイメージ
オブジェクトの階層構造ピラミッドの等高線と横から見たところのイメージ

オブジェクトを変数に入れるときにピラミッドを意識する

Rangeオブジェクトというピラミッドの最上層のオブジェクトを考えます。A1セルというRangeは他のワークブックにも存在します。しかし、全くファイル名(=フルパス)のワークブックは絶対に存在しません。そういうファイル名にできない仕様です。と言う事はA1セルはどのワークブックにも存在するが、このワークブックの、このシートのA1セルは世界に一つしか存在しないという事です*1

と言う事は、A1セルを変数に入れてしまえば、必然的にどのブックのどのシートのものなのかと言う情報をその変数が持つことになります。実験してみましょう。

デスクトップ上に「オブジェクトの階層構造.xlms」というエクセルマクロブックを保存しました。シートは1つだけで、シート名は「Sheet1」となっています。

Sub オブジェクトの階層構造の実験1()
    
    Dim rng1 As Range
    Set rng1 = Sheets("Sheet1").Range("A1")
    
    rng1 = "こんにちは" ’A1セルに こんにちは と表示される
    
End Sub

「Sheet1」と言うシートのA1セルをrng1という変数に入れたのですから、これでA1に「こんにちは」と表示されます。当たり前です。

次のはエラーが出ます。

Sub オブジェクトの階層構造の実験1()
    
    Dim rng1 As Range
    Set rng1 = Sheets("Sheet1").Range("A1")

    Sheets("Sheet1").rng1 = "こんにちは" 'ここでエラーになる
    
End Sub

rng1はSheet1のA1を代入しているので、もうSheets("Sheet1").rng1とすることはできません。rng1はSheet1に所属しているRangeを格納しているのでSheetsが被っています。

表示されるエラーメッセージの画面
表示されるエラーメッセージ

「オブジェクトは、このプロパティまたはメソッドをサポートしていません。」というメッセージが出ます。エラーの本質はSheetsが被っている事なのですが、エクセルさんはプログラムが間違っているという観点ではなく、どんなエラーが出ているかと言う観点でコメントしてくれています。

このエラーメッセージの意味はSheetsオブジェクト(クラス)にはrng1なんていうプロパティもメソッドも設定されていませんよ、と言う事です。そりゃrng1は今作ったタダの変数ですから当たり前です。

この項目の結論はよりピラミッドの高いところのオブジェクトは低いところにあるどのオブジェクトに属しているかの情報を持っている、と言う事です*2


他のシートのRangeに値を入力する

Activateを使わずに他のシートに値を入力します。予め、シートを追加しておきます。現在、「Sheet1」のシートの右側に「Sheet2」が追加された状態です。

Sub オブジェクトの階層構造の実験2()
    
    Sheets("Sheet1").Activate '明示的に予めSheet1をアクティブにしておく
    
    Dim rng1 As Range
    Set rng1 = Sheets("Sheet1").Range("A1")

    '現在Sheet1のA1セルには こんにちは が入力されている状態
    Sheets("Sheet2").Range("A1") = rng1
    
End Sub

Sheet2をActivateせずに、A1セルの内容を転記することができました。今まで、操作する為にわざわざアクティブなシートを切り替えていた人は参考にしましょう。

これでWith~End Withステートメントをスムーズに理解する前提知識が揃いました。With~End Withの解説ではピラミッドを横から見た図を大切にしていきます。今後も頑張って行きましょう!

<お知らせ>
Excel VBAでクラスやオブジェクトの概念と使い方を丁寧に解説し、ワンランク上の実力を目指すガイドを書きました。この本で本物の実力を身に付けて一皮むけてみませんか?
books.rakuten.co.jp

*1:厳密にはエクセルファイルは保存するまでファイル名(=フルパス)が決まらないため、未保存のワークブックが複数開いていればA1セルは複数ある事になりますが、この時点においてもBoo1、Book2など拡張子のない仮のブック名がつけられており、やはり全く同じ名前のA1セルは存在しません

*2:普通、オブジェクトの階層構造を説明するとき、上位といえばより広い概念です。Workbook>Sheets>Rangeの順です。私はピラミッドの説明が分かりやすいと感じたので上下が逆になっています。なので高い低いと言う言葉を使っています。他の人と話すときは通常、上位のクラスとか上位のオブジェクトと言ったらピラミッドの裾野が広い方の事なのでご注意下さい