Mutable_Yunの業務改善ブログ

業務改善や生産性向上のブログです。自動化の手段として、VBAやRPAの勉強に役立つ解説をしています。

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

今回はVBAのクラスを使って実務的な機能の実装を試みます。
そもそもクラスやオブジェクトの概念がよくわからない方は、VBAのクラスを使う① クラスを使うためのオブジェクトとメソッドの前提知識をご参照ください。

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

目次

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

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

ファイルを開くは、たったの一行です。

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」というオブジェクト名にする。

このようにすることで、オブジェクト名が異なるだけで、挙動は同じだけど引数、例えばファイル名の違うファイルを選択するメッセージボックスを表示させることができます。

伝わったでしょうか。


手順まとめ

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

また、作成するオブジェクト名は上記の例のようにクラス名と違うモノにすることをおすすめします。
エクセルのもとからのオブジェクトのようにRangeはクラス名であり、オブジェクト名でもある、と言うのはわかりにくいです。

以上全2回続けてVBAのクラスについてでした。