質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Access

Accessはマイクロソフトによるリレーショナルデータベース管理システムです。オブジェクト指向のアプリケーション作成に対応しており、テーブルや編集をはじめ、クエリ生成、入力フォーム作成、レポート作成など一通りの機能を備えています。

Q&A

解決済

1回答

5028閲覧

VBA コードを書く場所

yuujiMotoki

総合スコア90

Access

Accessはマイクロソフトによるリレーショナルデータベース管理システムです。オブジェクト指向のアプリケーション作成に対応しており、テーブルや編集をはじめ、クエリ生成、入力フォーム作成、レポート作成など一通りの機能を備えています。

0グッド

1クリップ

投稿2018/09/17 11:38

最近、VBAでクラスモジュールを書くようになってから、
フォームでイベントハンドラさせたあとの、マクロの書き方に悩んでいます。

例えばですが

アクセスのデータベースにフォームボタンからイベント駆動する場合に
下記のような3階層のプログラムを書いています。

ユーザーフォームは、単なるイベントハンドラの受け場所にしており、
ほとんどソースを書かずに、標準モジュールに飛ばしています。

標準モジュールからwith 文を使って、クラスのインスタンスを生成して
あとはクラス内部に処理を書いています。

ただ、何をどう書くのかという決まりは持っておらず、
標準モジュールのコードはなるべく減らして、クラス側にもっていこうとはしています。

標準モジュールのオブジェクトは、毎回のイベント駆動で使っては消去しており、
ワークシート上へのデータ保持をしているような状態です。

いったん読みこんだデータソースなど、同じように何回も読みだすことに
実に不合理を感じています。

またフォーム上のコンボボックスの状態など、シートのセルに書き込んだりしていますが、
何か配列として、内部に置いておきたいと思っています(できればクラス変数として)

しかしながら、いまだにVBAのプログラムの組み方というものが
分かっておらず、何か無駄な感じがしてなりません。

何かこう、しっくりくるユーザーフォーム、標準モジュール、クラスモジュールの
使い方について指針というものはありませんでしょうか?

#現状のソースコード

ユーザーフォーム

VBA

1Private Sub CbtAccessOpen_Click() 2access_db.アクセスを開く 3End Sub 4 5Private Sub CbtDelete_Click() 6access_db.データ削除 7End Sub 8 9Private Sub CbtRefleshDB_Click() 10access_db.更新 11End Sub 12 13Private Sub CbtRenew_Click() 14access_db.データ追加更新 15End Sub 16 17Private Sub CommandButton4_Click() 18access_db.リレーション構築 19End Sub

access_db モジュール

VBA

1 2Option Explicit 3 4Sub 更新() 5 With New clsAccessDB 6 Set .WST = ActiveSheet 7 .SQL_OPTION = FrmDbAccess.TextBox1 8 .更新 Range("b2") 9 .WST.Columns.AutoFit 10 End With 11End Sub 12 13Sub データ追加更新() 14 With New clsAccessDB 15 Set .WST = ActiveSheet 16 If TypeName(Selection) = "Range" Then 17 Set .SetRange = Selection 18 .データ追加更新 19 MsgBox Selection.Address & "データを追加更新しました" 20 End If 21 End With 22End Sub 23 24Sub データ削除() 25 With New clsAccessDB 26 Set .WST = ActiveSheet 27 If TypeName(Selection) = "Range" Then 28 Set .SetRange = Selection 29 .データ削除 30 MsgBox Selection.Address & "データを削除しました" 31 End If 32 End With 33End Sub 34 35Sub アクセスを開く() 36 With New clsAccessDB 37 Set .WST = ActiveSheet 38 .アプリを開く 39 End With 40End Sub

clsAccessDB

VBA

1Option Explicit 2 3Public WST As Worksheet '対象とするワークシート 4Public SQL_OPTION As String 5Private datarow As Integer 6Private dbPath As String 7Private con As Object 8Private mRS As Object 9Private list_rows As Collection 10Private table As Variant 11Const dbFile As String = "MEP2019.accdb" 12Private objrng As Variant 13 14Private Sub DB切断() 15 mRS.Close 16 con.Close 17 Set mRS = Nothing 18 Set con = Nothing 19End Sub 20 21Public Sub 更新(Optional locate As Variant) 22 If Not IsMissing(locate) Then 23 Set objrng = locate 24 End If 25 26 DB接続 27 DB読込 28 DB切断 29 TABLE作成 30End Sub 31 32Private Sub TABLE作成() 33 On Error GoTo エラー処理 34 With WST 35 Set table = .ListObjects.Add(Source:=objrng.CurrentRegion) 36 table.Name = .Name 37 End With 38 Exit Sub 39エラー処理: 40 MsgBox "テーブルは作成済みです。" 41End Sub 42 43 44Private Sub DB読込() '引数にセル名 45 Dim i 46 With WST.ListObjects 47 Do While .Count <> 0 48 .Item(1).Delete 49 Loop 50 objrng.CurrentRegion.Clear 51 End With 52 With mRS 53 For i = 0 To .Fields.Count - 1 54 objrng.Offset(0, i).value = .Fields(i).Name 55 Next i 56 End With 57 objrng.Offset(1, 0).CopyFromRecordset DATA:=mRS 58End Sub 59 60Public Sub データ追加更新() 61 Dim i 62 Dim data_r As Variant 63 If Chk行選択 Then 64 DB接続 65 66 For Each data_r In list_rows 67 With mRS 68 .MoveFirst 69 .Find Criteria:=.Fields(0).Name & "='" & WST.Cells(data_r, 1) & "'" 70 If .EOF = True Then 71 .AddNew 72 End If 73 For i = 1 To .Fields.Count - 1 74 .Fields(i).value = WST.Cells(data_r, i + 1).value 75 Next i 76 .Update 77 .MoveFirst 78 End With 79 Next 80 DB読込 81 DB切断 82 End If 83End Sub 84 85Private Function Chk行選択() As Boolean 86 If list_rows Is Nothing Then 87 MsgBox "行が選ばれていません" 88 Chk行選択 = False 89 Exit Function 90 End If 91 If MsgBox("行" & list_rows(1) & "から" & list_rows.Count & "個のデータを処理しますか?", vbOKCancel) = vbOK Then 92 Chk行選択 = True 93 Else 94 Chk行選択 = False 95 End If 96End Function 97 98Public Sub データ削除() 99 Dim i 100 Dim data_r As Variant 101 If Chk行選択 Then 102 DB接続 103 For Each data_r In list_rows 104 With mRS 105 .MoveFirst 106 .Find Criteria:=.Fields(0).Name & "='" & Cells(data_r, 1).value & "'" 107 If .EOF = True Then 108 MsgBox "該当するレコードは存在しません。" 109 DB切断 110 Exit Sub 111 End If 112 .Delete 113 .MoveFirst 114 End With 115 Next 116 DB読込 117 DB切断 118 End If 119End Sub 120 121Private Sub DB接続() 122 Dim SQL As String 123 Dim sheetname As Variant 124 Dim i As Integer 125 Dim TableName As String 126 'SQL_OPTION = FrmDbAccess.TextBox1 127 TableName = WST.Name 'シート名が、そのままACCESSテーブル名にリンクする 128 Set con = CreateObject("ADODB.Connection") 129 con.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" & dbPath & dbFile 130 con.Open 131 Set mRS = CreateObject("ADODB.Recordset") 132 SQL = "SELECT * FROM " & TableName & " " & SQL_OPTION 'クエリ検索オプションを指定して、SQL読込をする 133 'MsgBox SQL 134 mRS.Open SQL, con, adOpenKeyset, adLockOptimistic, adLockReadOnly 135End Sub 136 137Property Get RelationTBL() As Variant 138'Set RelationTBL = Sheets(RelTBL) 139End Property 140 141Property Set SetRange(ByVal rngs As Variant) 142 Dim list_ As New Collection 143 Dim rng As Variant 144 For Each rng In rngs 145 list_.Add rng.row 146 Next 147 Set list_rows = list_ 148End Property 149 150Private Sub Class_Initialize() 151 Set WST = ActiveSheet 152 Set objrng = Range("a1") 'default origin location 153 dbPath = ActiveWorkbook.Path & "..\" 154End Sub 155 156Private Sub Class_Terminate() 157 'MsgBox ("データベースを閉じました") 158End Sub 159 160Public Sub アプリを開く() 'ACCESSを起動する 161 With CreateObject("Access.Application") 162 .OpenCurrentDatabase dbPath & dbFile 163 .DoCmd.OpenTable ActiveSheet.Name, acNormal 164 .Visible = True 165 .UserControl = True 166 End With 167End Sub 168

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

クラスモジュール、標準モジュール、イベントプロシージャの使い分けの標準が、
もし、社内(個人でも良いと思います)にあるのであれば、それに従うのが普通です。

Accessとなると、業務用で使用されるアプリが多いと思います。
その場合、処理速度よりメンテナンス性が重要になり、そのメンテナンス性とは、わかりやすさです。

自分の場合、ある程度の使用ルールを決めています。

クラスモジュールの場合
クラスモジュールのメリットの一番は、データとメソッドが一体化でき、そのインスタンスを開放しない限りデータを保持できる点です。
Accessの場合、データの検索系の処理で役に立つ場合が多いです。
あるキーを渡したら、関連のデータを取得し、パラメータ値としていつでも渡す準備が出来る点。
また、アプリの基本機能としても有効だと思います。
例えば、経過時間の計算、レジストリの更新、ファイル操作などで、アプリの内容に左右されない場合ですね。
自分の場合。クラスモジュールは、一種の変数(レコード)として取り扱っている場合が多いです。

標準モジュールの場合
標準モジュールの一番のメリットは、フォームやクエリで、ユーザー定義関数として扱える点です。
ある特定の値を返す関数や決まりきった処理を行う時や、クエリ、フォーム、レポート等で、Excelのワークシート関数の様に使える点です。
これは、クラスモジュールでは実装できない点でもあります。
ある1つのフォーム内にテキストボックスが複数あり、同じ処理をする場合、イベントプロシージャに関数名を指定すると、同じコードを書く必要が無くなります。
自分の場合、一種の関数として使用しています。

イベントプロシージャの場合
自分の場合、ほとんどイベントプロシージャで済ませる場合が多いです。
それでも、共通する機能は、標準モジュールを使ったり、クラスを扱いますが、結局、メンテナンスは、オブジェクト単位で行う場合が多く、もし、標準モジュールにしていたら、他のオブジェクトに影響が出る可能性があります。
もし、他のオブジェクトに使用されていれば、使われているオブジェクトに対してもテストをする羽目になります。

ソースコードを見ましたが、ちょっと、標準モジュールとクラスモジュールに頼り過ぎだと思います。
クラスモジュールですが、プロパティを増やすと、共通化できると思います。
ただし、プロパティを増やし過ぎると、クラスとしてのメリットが無くなるので、やり過ぎと評価しました。

長くなりましたが、結局のところ、ルールは自分で作ることになるので、自分のポリシーを強く持って、コードを書いて下さいと言いたいです。

投稿2018/12/26 13:06

kai_keitai

総合スコア344

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問