ゆんの業務改善ブログ

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

VBAのクラスを使う② ユーザーが選択したファイルを開く実務的なオブジェクトを生成する

今回はVBAのクラスを使って実務的な機能の実装を試みます。そもそもクラスやオブジェクト、メソッドと言う概念がよくわからない方は、VBAのクラスを使う① クラスを使うためのオブジェクトとメソッドの前提知識をご参照ください。概念をしっかり理解した上で具体的な処理を学ぶ事で理解を定着させることができます。

目次

クラスからオブジェクトを生成するして使う手順

VBAでクラスを利用するには、下記のステップを踏みます。

  1. どのような機能をもったクラスを作るか考える
  2. クラスモジュールにメソッドを記述する
  3. 標準モジュールのSubプロシジャから呼び出して使う

順に解説していきます。

どういうオブジェクトを作るか考える

クラスの有効な使いどころは、必要な機能は同じだけど必要な処理は毎回違う、という作業です。例として今回はユーザーが選択したファイルを開くオブジェクトを生成するクラスを作ります。ファイルを開くは、たったの一行です。

WorkBooks.Open Filename:= ファイル名のフルパス

しかし、ユーザーにファイルを選択させ、選択させたファイルを開くとなると、一例として下記のような作業が必要と考えられます。

  • メッセージボックスに「○○のファイルを選択して下さい」と表示する
  • 右上の×や「キャンセル」を押されたときの対応
  • ファイル選択ダイアログボックスの表示
  • ファイル選択ダイアログボックスで右上の×やキャンセルを押されたときの対応
  • 選択させたファイルがすでに開かれていた時の対応
  • 選択されたファイルを開く
  • 後々閉じるために選択されたファイルをWorkBook型の変数に格納
  • 必要に応じてファイル名(フルパスからフォルダのパスを除いたモノ)を取得
  • このファイルの存在した場所に加工後のファイルを保存したいときに備えてフォルダのパスを取得

ユーザーにファイルを選択させたい場面はよく出てきます。しかも似たような業務、例えば売上集計、受注集計、仕入れ集計などの業務を一つにまとめたある程度の規模のエクセルマクロブックを作成するならば、毎回そっくりなプロシジャを書かねばなりません。

そしてユーザーに選択させたいファイルの種類やフォルダのパスを取得しておく必要があるかどうかも毎回変わります。

一つのPublic Subプロシジャを毎回呼び出す事が思い浮かぶかもしれませんが、これは事実上できません。理由はPublic Subプロシジャを呼び出すときに渡す引数がPublic型である必要があるため、ファイルを選択する場面が複数出てくると変数が上書きされてしまうためです。

そこで、クラスを活用します。例をシンプルにするために上の箇条書きの例から、抜粋して下記を実装します。

  • メッセージボックスに「○○のファイルを選択して下さい」と表示する
  • 右上の×や「キャンセル」を押されたときの対応
  • ファイル選択ダイアログボックスの表示
  • 選択されたファイルを開く

コーディングに入る前にクラスを使うメリットを予め書くと、下記のようなことが考えられます。

例えばファイルを選択する部分を作成する時に、ファイルがすでに開かれている時に開こうとすると開けないというエラーがあるなんて思いも寄らなかったとします。そしてツールを利用してくれている同僚からエラーを指摘されたとします。もしこのクラスを実装していれば、そのクラスに予め開かれていないか調べる機能を一カ所追加すればOKです。

クラスでは無く、毎回コピペ+修正をしていると、全てのファイルを開くプロシジャを修正しなくては成らなくなります。これがクラスのメリットです。

クラスモジュールでクラスを実装する

それでは実装に移ります。

まず、クラスモジュールから書いていきます。必要な機能をオブジェクトに持たせるためのクラスは、いわばオブジェクトの説明書です。

まず、挿入⇒クラスモジュールでクラスモジュールを挿入します。
f:id:mutable_yun:20190911184313p:plain

クラスモジュールの名前はクラス名となる大事な名前なのでデフォルトのClass1から分かりやすい名前に変更しておきましょう。今回は「OpenFiles」と言う名前にします。

表示⇒プロパティウィンドウ
f:id:mutable_yun:20190911184556p:plain

表示されたプロパティウィンドウの(オブジェクト名)の右のModule1をOpenFilesに変更する。
f:id:mutable_yun:20190911184815p:plain

*オブジェクト名と言うのが気になりますが、これはクラス名なので注意です。
「このクラスで作られるオブジェクトのクラス名」の略だと思っておきましょう。

まず宣言セクションでPublicでクラスの中で使う変数を定義します。複数の異なるプロシジャで使う事を想定してのことです。

Option Explicit

Public file_book As Workbook '開くファイルを閉じるなど、後々使いやすいように変数を用意
Public full_path As String  'ユーザーが選択したファイル名のフルパスを取得する為の変数を用意
Public file_directory As String '加工後の成果物を保存するなどのためにフォルダのパスも格納する
Public buttons_as_msgbox_textstyle As Long  'メッセージボックスの上のところのテキスト。毎回違うので変数を用意
Public text_on_msgbox As String  'メッセージボックスに表示させるテキスト

これで、クラスモジュールの外=標準モジュールで使う変数が用意できました。

それではクラスモジュールの中身を書いていきます。

Public Sub FileSelector() 'これがメソッドになる!
    
    Const fixed_msg As String = "を選択して下さい。"
    Dim msgbuf As Long ’「○○のファイルを選択して下さい」のメッセージボックス関数の戻り値を受け取る
    
    msgbuf = MsgBox(file_name & fixed_msg, buttons_as_msgbox_textstyle, text_on_msgbox)
    
    full_path = Application.GetOpenFilename()
    
 'メッセージボックスでOK以外が押されたときや、手で誤入力して存在しないファイルを選択した時の対応
    If msgbuf <> 1 Or full_path = "False" Then
        MsgBox "キャンセルしました"
        End
    End If
    
    'あとで閉じるときなどの取り扱いがしやすいように変数に入れておく
    Workbooks.Open Filename:=full_path
    Set file_book = ActiveWorkbook
    
    'ついでにファイルの保存されていたフォルダのパスも取得しておく
    Dim file_temp as String
    file_temp = Dir(full_path)
    file_directory = Replace(full_path, file_temp, "")
    
End Sub

クラスモジュールの中に書くSubプロシジャやFunctionプロシジャがメソッドになります。どんな機能なのか分かりやすい名前にしておきましょう。今回はFileSelectorにしました。一つのクラスモジュールの中にプロシジャを増やすことで、複数のメソッドを作る事も可能です。

今回の例で言えば、下記のところがクラスモジュールの特徴を表しています。

    msgbuf = MsgBox(file_name & fixed_msg, buttons_as_msgbox_textstyle, text_on_msgbox)

これは「○○のファイルを選択して下さい」のメッセージボックスを表示させます。この○○のところや、
表示させるアイコンは毎回違うので、変数にしています。「のファイルを選択して下さい」は毎回同じでいいので、Constを使ってクラスモジュールの中に定数として定義しています。

以上で、クラスモジュールは完成しました。

標準モジュールでクラスからオブジェクトを生成する

実際に標準モジュールで使ってみます。

Sub 受注データを選択する()

    Dim OpenBook As OpenFiles
    Set OpenBook = New OpenFiles
    
    OpenBook.file_name = "受注データ"
    OpenBook.buttons_as_msgbox_textstyle = vbInformation
    OpenBook.text_on_msgbox = "受注データ選択メッセージ"
    
    OpenBook.FileSelector

End Sub

まず、OpenBookという新しいオブジェクトを宣言します。型は先ほど作ったクラス名であるOpenFilesです。
クラスモジュールの名前をClass1からOpenFilesに変更したのは、ここでクラス名が分かりやすい方が良いからです。これがDim OpenBook As Class1だったら後から見たときに意味不明です。

次にSet OpenBook = New OpenFilesでオブジェクトを作っています。

Set OpenBook = New OpenFiles

今宣言した「OpenBookというオブジェクトに新しい OpenFilesクラスを格納する」ことでオブジェクトが生成されます。

ここからはオブジェクト.変数という形で変数を使う事ができます。

OpenBook.file_name = "受注データ"

これはfile_nameというクラスモジュールで宣言した変数にデータを入れています。

OpenBook.FileSelector

この部分はおなじみの、Range("A1").ClearContents と同じ構造です。「オブジェクト名.メソッド名」と言う形で使う事ができるようになったわけです。このように使う事ができるようになることのメリットについて少し解説します。

例えば今回はOpenBookというオブジェクト名にしました。仮に受注データが格納されたエクセルブックをユーザーに選択させて開くというプロシジャではオブジェクト名を「受注OpenBook」というオブジェクト名にしておきます。さらに、別のプロシジャで仕入れファイルをユーザーに選択させて開く時には「仕入れOpenBook」というオブジェクト名にします。

このようにユーザーに開くべきエクセルブックを選択させて開くという操作が複数あり、開くエクセルブックが異なると言う場合、ファイルを選択させるときのメッセージボックスの表示だけが異なる、というそっくりなプロシジャをいくつも書かなくてはなりません。しかし、エクセルブックを開く時のメッセージボックスを出して選択を促し、選択させ、開くという一連の作業をクラスモジュールでクラスとして定義することにより、オブジェクト名が異なるだけで、挙動は同じだけど引数、例えばファイル名の違うファイルを選択するメッセージボックスを表示させることができます。

つまりエクセルブックを選択させて開くという機能をクラスの集約させ、Subプロシジャではクラスからインスタンスを生成し利用するという処理の流れのみを記述する事になります。このことにより機能と処理の流れが分離でき、効率的に開発ができるだけでなく、メンテナンス性を高める事につながります。この機能と処理の流れの分離こそがオブジェクト指向の核となる考え方です

手順まとめ

クラスからオブジェクトを生成して使う手順をまとめると下記の通りです。

  1. クラスモジュールで変数を宣言
  2. クラスモジュールを作成
  3. 実際にオブジェクトを作ってから、オブジェクトを使う

クラスに慣れ親しんで、オブジェクト指向を取り入れた一歩上のプログラムを目指しましょう。

<関連記事>