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

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

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

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

Q&A

解決済

5回答

3756閲覧

VBA コード簡略化 下記処理の場合

King_of_Flies

総合スコア382

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

0グッド

1クリップ

投稿2017/10/20 02:13

編集2017/10/20 08:49

お疲れ様です。

私が実装したVBAコードに下記実装があります。
それぞれフォームからのボタン押下で流れる作りになっています。

VBA

1'*************************************************************************** 2'実行 3'実行ボタン押下で実行される処理 4'*************************************************************************** 5 Sub 実行() 6 Application.EnableEvents = False 7 Application.ScreenUpdating = False 8 Call SetReportDate 9 Call EditFormats 10 Call SetLines 11 Call SetCells 12 Range("A1").Select 13 MsgBox ("処理が正常に完了しました。") 14 Application.ScreenUpdating = True 15 Application.EnableEvents = True 16 End Sub 17'*************************************************************************** 18'リセット 19'リセットボタン押下で実行される処理 20'*************************************************************************** 21 Sub リセット() 22 Application.EnableEvents = False 23 Application.ScreenUpdating = False 24 Cells.Select 25 Selection.EntireRow.Hidden = False 26 Sheet1.ComboBox1.Clear 27 Call SetRows 28 Call ResetColors 29 Call ResetInputDataProjects 30 Range("A1").Select 31 MsgBox ("リセットしました。") 32 Application.ScreenUpdating = True 33 Application.EnableEvents = True 34 35 End Sub 36 37'*************************************************************************** 38'絞り込み 39'絞込ボタン押下で実行される処理 40'*************************************************************************** 41 Sub 絞り込み() 42 Application.EnableEvents = False 43 Application.ScreenUpdating = False 44 Cells.Select 45 Selection.EntireRow.Hidden = False 46 Range("A1").Select 47 Call NarrowRows 48 Application.ScreenUpdating = True 49 Application.EnableEvents = True 50 End Sub

この処理を見ていただくとわかると思いますが、
Application.EnableEvents = False
Application.ScreenUpdating = False
処理
Application.ScreenUpdating = True
Application.EnableEvents = True
という作りになっています。

この処理前、処理後の二行をプロシージャとして切り出して、
CALLすれば一応簡略化は図れるのですが、

VisualStudioのC#の実装実装で、
メソッド呼び出し前後に起動する処理が指定できた記憶があり、
同様のことをVBAでできないかと考えております。

確かこんな感じでした
[POST]
[AttributeUsageAttribute]
[BeforeMethod]
[AfterMethod]
public なんちゃら

ちょっとこの当たりは詳しくないので、
クラスの属性?か何かの話だと思うのですが、
これをVBAの特定のプロシージャに属性付与して、

上の例でいうのであれば[BeforeMethod][AfterMethod]を記述することで、
そのメソッドの実行時には前処理にValidCheck、後処理にオブジェクトの解放をする。というような当時の私には理解できなかったコード実装がされていたのですが、似たような実装で、上のコードの簡略化は実装できないでしょうか。

イメージとしてはこんな感じにしたいです。

vba

1[BeforePro]'クラス内に存在するそれぞれのプロシージャ実行前に流れる処理。 2[AfterPro]'クラス内に存在するそれぞれのプロシージャ実行後に流れる処理。 3Class 実行トリガープロシージャ 'こんな概念ないと思いますがプロシージャをグループ化する意図です。 4  Sub 実行() 5   Call SetReportDate 6   Call EditFormats 7   Call SetLines 8   Call SetCells 9   Range("A1").Select 10   MsgBox ("処理が正常に完了しました。") 11  End Sub 12 13 14  Sub リセット() 15   Cells.Select 16   Selection.EntireRow.Hidden = False 17   Sheet1.ComboBox1.Clear 18   Call SetRows 19   Call ResetColors 20   Call ResetInputDataProjects 21   Range("A1").Select 22   MsgBox ("リセットしました。") 23  End Sub 24 25  Sub 絞り込み() 26   Cells.Select 27    Selection.EntireRow.Hidden = False 28   Range("A1").Select 29   Call NarrowRows 30  End Sub 31End Class

現在VBA学習段階ですので、
実装できないようであるなら普通にプロシージャを二つ作成して
内部でコールすればいいので、問題はないのですが、
記憶の片隅にある「出来たような気がする」という感情があり、気になってしまいまして。

実装可能であるようであれば、どのようなつくりにできるのか、
ご教授お願いいたします。

*繰り返しますが、不可能なら不可能ですとおっしゃていただければそれでよいです。

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2017/10/20 02:40

VBA、具体的にはどの処理系でしょうか。Officeソフト名とそのバージョン、32bitか64bitかも。
King_of_Flies

2017/10/20 02:44

処理系というのはどういう回答を望まれてますか?Officeソフト名:MicroSoft Excel 2010 Bit:64
King_of_Flies

2017/10/20 02:47

自分なりに質問内容としてわかりやすいようにしてみたのですが、実装したいことの意図は汲み取れましたでしょうか。つたない文章故伝わっていない部分ありましたら、質問にてよろしくお願いします。
guest

回答5

0

ベストアンサー

要するに、複数の処理をグループ化して、その処理の実行前後に、共通の前処理、後処理を実行したいということですよね。

クラスを作成して、グループ化したい処理をそのクラスのメソッドにすればどうでしょうか。コンストラクタとデストラクタに前処理、後処理を記述します

クラスモジュール MyProc.cls

Option Explicit Private Sub Class_Initialize() Application.EnableEvents = False Application.ScreenUpdating = False Debug.Print "Initialize" End Sub Private Sub Class_Terminate() Application.ScreenUpdating = True Application.EnableEvents = True Debug.Print "Terminate" End Sub Sub 実行() Debug.Print "実行" MsgBox "処理が正常に完了しました。" End Sub Sub リセット() Debug.Print "リセット" MsgBox "リセットしました。" End Sub Sub 絞り込み() Debug.Print "絞り込み" End Sub

呼び出し例

Public Sub 共通処理テスト() Dim M As New MyProc M.実行 M.リセット M.絞り込み End Sub

デバッグ表示結果

Initialize called. 実行 リセット 絞り込み Terminate called.

Dim M As New MyProcとクラスを生成するコードが必用になりますが、スコープを抜けると自動で後処理が実行されます。
また、M. と入力した時点で、メソッドの一覧が表示されますのでコード入力が楽になります(インテリセンス)。

処理内の一部分のみに適用したい場合は下記のような記述もできます。

Public Sub 共通処理テスト2() Debug.print "処理1" With New MyProc .実行 .リセット .絞り込み End With Debug.print "処理2" End Sub

デバッグ表示結果

処理1 Initialize called. 実行 リセット 絞り込み Terminate called. 処理2

これも、.の入力でメソッド一覧が表示されます。

投稿2017/10/24 02:48

hatena19

総合スコア33699

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

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

King_of_Flies

2017/10/24 02:54

お、やりたいことが実装できそうなコード例ですね! 少し試してみます!
King_of_Flies

2017/10/24 03:51

実行()リセット()絞込()がマクロの実行プロシージャで、 クラスモジュールに記述してしまうと、 呼び出せないみたいですね。
hatena19

2017/10/24 04:21

ワークシート上に、フォームコントロールのボタンを配置したときに、選択するマクロのことでしょうか。それにクラスモジュールのメソッドは表示されませんね。 ActiveXコントロールのボタン、またはユーザーフォーム上のボタンの Private Sub CommandButton1_Click() End Sub から呼び出すことになりますね。
King_of_Flies

2017/10/24 05:19

ActiveXのコントロールから共通処理テスト()を呼び出すこととなると思うのですが、 実行ボタンを押したら実行()のみが流れ、 リセットボタンを押したらリセット()のみが流れるようにしたいのですが、 Public Sub 共通処理テスト() Dim M As New MyProc M.実行 M.リセット M.絞り込み End Sub この処理の引数にsheetからどのボタンが押下されてこのプロシージャが呼ばれたか。を判定する方法はありますか?
King_of_Flies

2017/10/24 05:50

出来ました。 わざわざ判定処理をしなくて良かったです。 Private Sub BtnRun_Click() Dim M As New MyProc M.実行 End Sub Private Sub BtnReset_Click() Dim M As New MyProc M.リセット End Sub Private Sub BtnNarrows_Click() Dim M As New MyProc M.絞り込み End Sub これでクラスのMyProcを呼び出せば、コンストラクタ、デストラクタが働いてうまく動作することができました。
King_of_Flies

2017/10/24 05:52

あー。Dim M As New MyProcが外に出せるので、 実際は Dim M As New MyProc Private Sub BtnRun_Click() M.実行 End Sub Private Sub BtnReset_Click() M.リセット End Sub Private Sub BtnNarrows_Click() M.絞り込み End Sub となりました。
hatena19

2017/10/24 06:20

そうすると、M のスコープがプロシージャになりますので、ユーザーフォームのモジュールだと、フォームを開いている間、前処理が適用されます。標準モジュールだとブックを開いている間、前処理が適用されます。 スコープ(適用範囲)を意識して、クラスを宣言するようにしてください。
King_of_Flies

2017/10/24 06:50

Mのスコープがグローバルだと、デストラクタが発動しないようで、 改善方法が不明だったので、 プロシージャごとにMを宣言するように戻しました。
King_of_Flies

2017/10/24 06:51

M = Nothingで呼び出せると考えたのですが、うまく動作しませんでした。
King_of_Flies

2017/10/24 06:58 編集

**MyProC.cls 'コンストラクタ Private Sub Class_Initialize() Application.EnableEvents = False Application.ScreenUpdating = False End Sub 'デストラクタ Private Sub Class_Terminate() Application.ScreenUpdating = True Application.EnableEvents = True Range("A1").Select End Sub Public Sub 実行() Call SetReportDate Call EditFormats Call SetLines Call SetCells MsgBox ("処理が正常に完了しました。") End Sub Public Sub リセット() Call CellsReView Call SetRows Call ResetColors Call ResetInputDataProjects Sheet1.ComboBox1.Clear MsgBox ("リセットしました。") End Sub Public Sub 絞り込み() Call CellsReView Call NarrowRows End Sub **sheet1 Private Sub BtnNarrow_Click() Dim M As New MyProC M.絞り込み End Sub Private Sub BtnReset_Click() Dim M As New MyProC M.リセット End Sub Private Sub BtnRun_Click() Dim M As New MyProC M.実行 End Sub
King_of_Flies

2017/10/24 06:53

上記コードでやりたいことの実装はできました。 ありがとうございました。
guest

0

今回の場合であれば、(アプリ)状態を自動で切り替えるクラスを使うとよいかと思います。
このクラスにおいて、コンストラクタ(Class_Initialize)でアプリ無効に、デストラクタ(Class_Terminate)でアプリ有効に切り替えます。
途中で処理を抜けても終了処理が走るので少し楽できるかと思います。

Excel VBAでクラスを使ったことありませんのでコードにおかしな点あるかもしれませんが、Excel2007環境にて以下コードで動作しました。

AppDisable.cls

VBA

1' コンストラクタ 2Public Sub Class_Initialize() 3 Debug.Print "Initialize called." 4 SetStatus False 5End Sub 6 7' デストラクタ 8Public Sub Class_Terminate() 9 Debug.Print "Terminate called." 10 SetStatus True 11End Sub 12 13' アプリ有効無効の切替 14Public Sub SetStatus(bSts) 15 Debug.Print "SetStatus : " & bSts 16 Application.EnableEvents = bSts 17 Application.ScreenUpdating = bSts 18End Sub

各処理

Sub test() Dim ad As AppDisable Set ad = New AppDisable ' ここで無効になる ' Subを抜けると有効に戻る Debug.Print "処理1" Dim ret As Integer ret = MsgBox("処理を続行?", vbYesNo + vbQuestion, "確認") If ret <> vbYes Then Exit Sub End If ad.SetStatus True ' 一時的に有効に Debug.Print "処理2" ad.SetStatus False ' また無効に Debug.Print "処理3" End Sub

デバッグ表示結果

Initialize called. SetStatus : False 処理1 SetStatus : True 処理2 SetStatus : False 処理3 Terminate called. SetStatus : True

投稿2017/10/20 07:33

can110

総合スコア38262

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

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

King_of_Flies

2017/10/20 07:51

これは処理実行前(実行()や、リセット()、絞込()の呼び出しのこと)に、 ab.SetStatusにTrueを設定、 処理実行後にFalseを設定という作りをクラス化したもので間違いないですか? この実行()、リセット()、絞込()はボタンによるマクロ処理ですが、 test()ですべて読み込むのではなくそれぞれのアクションからなる処理なので、 その実装のためには、 Sub 実行() Dim ad As AppDisable Set ad = New AppDisable ab.SetStatus True "処理1の記述" ab.SetStatus False End Sub のようなつくりになりますよね? これだと、プロシージャ切り出しで、 Sub BeforePro() Application.EnableEvents = False Application.ScreenUpdating = False End Sub Sub AfterPro() Application.EnableEvents = True Application.ScreenUpdating = True End Sub とし、 Sub 実行() Call BeforePro "処理1の記述" Call AfterPro End Sub とする方が簡潔でいいと思うのですが、 このtest()はどのような意図で使っていますか? ここまで書いて置いて何ですが、can110さんの意図と違っていたらすいません。
King_of_Flies

2017/10/20 07:54

要は実行(),リセット(),絞込()の処理をグループ化して、 そのグループに対し、内部プロシージャの実行前後に、 ab.SetStatus True ab.SetStatus Falseを実行できればよいのですが。 グループ化の概念がなさそうなので。。。
can110

2017/10/20 08:23

Subに入ったら開始処理、(任意のタイミングで)抜けたら終了処理を自動で実行するように意図しています。 すなわち、質問の最初に挙げられている ----- [BeforePro] [AfterPro] Sub 実行() ----- は ----- Sub 実行() Dim ad As AppDisable Set ad = New AppDisable ----- で実現できます。 > 要は実行(),リセット(),絞込()の処理をグループ化して、 残念ながら、この回答ではここまでは実現できません。
King_of_Flies

2017/10/20 08:39

コンストラクタ、デストラクタの参考にはなったので、とても役立つ情報でした。 ありがとうございます。 >> 要は実行(),リセット(),絞込()の処理をグループ化して、 > 残念ながら、この回答ではここまでは実現できません。 やっぱり難しいですかね。 やりたいことの意図は理解していらっしゃるんですよね・・?
can110

2017/10/20 08:55

「追記あるいはこうできたらベスト」のほうでやりたいことの意図でしょうか? 理解はできてると思いますが、私の知識不足もありVBAでは無理そうなので考えるのは諦めてました^^;
King_of_Flies

2017/10/20 08:57

>「追記あるいはこうできたらベスト」のほうでやりたいことの意図でしょうか? そうです。 *回答をいただいた後質問内容を編集し、現在その文言は削除しました。
guest

0

ご提示の前処理、後処理は、自分は関数を作って使ってます。

もしこの質問でご希望の処理ができるのであれば、非常に知りたい限りです。

投稿2017/10/20 06:28

ExcelVBAer

総合スコア1175

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

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

King_of_Flies

2017/10/24 06:55

クラスモジュールに前処理、後処理を加えたいプロシージャと、 コンストラクタ、デストラクタを設定して、 そのプロシージャを外部クラスから呼び出すことで、実装できました。 詳しくはhatena19さんとのやり取りを参考ください。
guest

0

言語の違い

VBAはVB6.0ライクな言語です。
C#でできることが何でもVBAでそのままできるという訳ではありません。
しかしがんばれば目的の動作(あるいはそれに近い動作)を実現することはできるかもしれません。
それが労力に見合うものかは別の話になりますが。。(^_^;;

擬似コントロール配列

まず、VB6.0でボタン押下時の動作を統一したい場合、コントロール配列にしてイベント共有させるのが手っ取り早い方法でした。

例えばボタン1~5をコントロール配列として作成しておくと、その1~5のどのボタンを押しても同じクリックイベントが発生するのです。
基本的にはそのイベント処理内で全ボタン共通の処理を行い、ボタン毎の固有の動作をさせたい部分だけ必要に応じてイベント内で処理分岐させるような使い方をしていました。
(イベント共有の書き方は異なりますが、VB.NETやC#でも同じようにイベントを共有させることができますよね。)

さて、ここまで書いておいてなんですが、コントロール配列はVB6.0では使用できるのに残念ながらVBAでは使用できません。
それでもVBAでもコントロール配列と同じような動作をさせたいという人はたくさんいるので、その線でweb検索すると有益な情報が得られると思います。

例えば以下のサイトでは独自にボタンクラスを作成し、擬似的にコントロール配列を実現しています。
⇒参考サイト

このサイトでは電卓のボタンをコントロール配列として作成しているため、配列のインデックス番号をそのまま値として利用しているだけですが、これを1なら"実行処理"、2なら"リセット処理"・・・といった具合に処理分岐してコールする関数を変えてあげれば目的の動作が実現できそうです。

参考になれば幸いです。

投稿2017/10/20 05:44

jawa

総合スコア3013

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

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

King_of_Flies

2017/10/20 06:06 編集

なんとなく言いたいことはわかりました。 ボタンクリックのイベントを拾って、 ``` Sub Btn_Click(btnKind As Integer) Call BeforePro Select Case btnKind Case 0 実行() Case 1 リセット() Case 2 絞込() End Select Call AfterPro End Sub ``` ↑即興で作ったので動作保障ないですけど、 イメージこんな感じで作成してみてはいかがでしょうかっていう意味ですよね?
King_of_Flies

2017/10/20 06:07

あれ、コード記述にならないうえに、改行が消えました・・・
jawa

2017/10/20 07:25 編集

そんな感じですね。 独自にボタンクラスを作るなんてお手軽なVBAにとっては大がかりな改修の部類に入るので、もっとお手軽に実現したい場合は(今回は要件に沿わないようですが)daiveさんのアドバイスにあるようにシート機能との組み合わせで実装すると思います。 ※ちなみにコメント欄ではマークアップ記述はできないのです。
guest

0

外しているかもですが、

Microsoft Office系 VBA は、VS98 の Office 製品向けとして存在しています。
⇒Office製品の種類により、機能が異なります。過去にはLotus 123/Win にもVBAは実装されていました。

具体的には、
1.クラス化を考えてください。
2.クラス化に当たっては、VS98:1998年発売&MS-Office系は製品毎の違いがある、という制約に注意
3.クラス化の方法は、ネット検索で事例が検索可能です。

旧世代に属する、VBAですが、クラスが隠蔽されて見えなくなっているだけで、
VBA も 一様 オブジェクトモデルを採用して、クラスが使える様になっていますし、
標準モジュールで書いていても、実際はクラス/オブジェクト(隠ぺいされて見えないだけ)の一部です。
⇒VB4.0 からオブジェクトモデルを採用している。(VB3.0は日本未発売だったはず。)
Offcie 2003辺りまで、オブジェクトモデルの解説があったのですが、
現行バージョンでは、見当たらなくなっています。
VBE:Visual Basic Editor から、オブジェクトブラウザーを開いてあげれば、
オブジェクト確認は可能です。

投稿2017/10/20 03:00

daive

総合スコア2028

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問