Excel VBA質問箱 IV

当質問箱は、有志のボランティア精神のおかげで成り立っています。
問題が解決したら、必ずお礼をしましょうね。
本サイトの基本方針をまとめました。こちら をご一読ください。

投稿種別の選択が必要です。ご注意ください。
迷惑投稿防止のため、URLの入力を制限しています。ご了承ください。


4161 / 13644 ツリー ←次へ | 前へ→

【58113】プロシージャの呼び出し方について そう 08/10/3(金) 1:28 質問[未読]
【58114】Re:プロシージャの呼び出し方について ichinose 08/10/3(金) 6:55 発言[未読]
【58115】Re:プロシージャの呼び出し方について そう 08/10/3(金) 9:29 発言[未読]
【58121】Re:プロシージャの呼び出し方について ichinose 08/10/4(土) 8:21 発言[未読]
【58122】Re:プロシージャの呼び出し方について そう 08/10/4(土) 9:59 お礼[未読]

【58113】プロシージャの呼び出し方について
質問  そう  - 08/10/3(金) 1:28 -

引用なし
パスワード
   VBA勉強中の者です。よろしくお願いいたします。

UserFormを右クリックするとメニューが現れ、その中の項目を選択すると
マクロが実行されるというものをつくる場合・・・・・・。
とりあえす以下のコードを考えました。


///////// UserForm1 フォームモジュール //////////////////////////

Dim myCB As CommandBar

Private Sub UserForm_Initialize()

  On Error Resume Next
  CommandBars("tbMenu").Delete
  On Error GoTo 0
  
  Set myCB = CommandBars.Add(Name:="tbMenu", _
       Position:=msoBarPopup, Temporary:=True)
  
  With myCB
    With .Controls.Add(Type:=msoControlButton)
      .Caption = "第1次変換モード"
      .OnAction = "FirstStringConv"
    End With
    
    With .Controls.Add(Type:=msoControlButton)
      .Caption = "第2次変換モード"
      .OnAction = "SecondStringConv"
      .BeginGroup = True
    End With
  End With

End Sub


Private Sub UserForm_MouseDown(ByVal Button As Integer, ByVal Shift As  Integer, ByVal X As Single, ByVal Y As Single)

  If Button = 2 Then
    myCB.ShowPopup
  End If


End Sub


Private Sub UserForm_Terminate()

  On Error Resume Next
  CommandBars("myCB").Delete
  On Error GoTo 0
  
End Sub


以上実行すると、フォームを右クリックするとメニューが表示され、
そのメニューから項目を選択すると標準モジュール内のプロシージャ
[ FirstStringConv ] 、[ condStringConv ] を呼び出せました。

ここからが質問なのですが、このメニューから標準モジュール内では
なく、フォームモジュール内に書かれてあるプロシージャを呼び出す
という場合には、 .OnAction = の次にどのような書き方で実行したい
プロシージャ名を指定すればよいのでしょうか?

さっぱりわかりません。
どなたかご指導お願いいたします。

【58114】Re:プロシージャの呼び出し方について
発言  ichinose  - 08/10/3(金) 6:55 -

引用なし
パスワード
   ▼そう さん:
おはようございます。


>UserFormを右クリックするとメニューが現れ、その中の項目を選択すると
>マクロが実行されるというものをつくる場合・・・・・・。
>とりあえす以下のコードを考えました。
>
>
>///////// UserForm1 フォームモジュール //////////////////////////
>
>Dim myCB As CommandBar
>
>Private Sub UserForm_Initialize()
>
>  On Error Resume Next
>  CommandBars("tbMenu").Delete
>  On Error GoTo 0
>  
>  Set myCB = CommandBars.Add(Name:="tbMenu", _
>       Position:=msoBarPopup, Temporary:=True)
>  
>  With myCB
>    With .Controls.Add(Type:=msoControlButton)
>      .Caption = "第1次変換モード"
>      .OnAction = "FirstStringConv"
>    End With
>    
>    With .Controls.Add(Type:=msoControlButton)
>      .Caption = "第2次変換モード"
>      .OnAction = "SecondStringConv"
>      .BeginGroup = True
>    End With
>  End With
>
>End Sub
>
>
>Private Sub UserForm_MouseDown(ByVal Button As Integer, ByVal Shift As  Integer, ByVal X As Single, ByVal Y As Single)
>
>  If Button = 2 Then
>    myCB.ShowPopup
>  End If
>
>
>End Sub
>
>
>Private Sub UserForm_Terminate()
>
>  On Error Resume Next
>  CommandBars("myCB").Delete
>  On Error GoTo 0
>  
>End Sub
>
>
>以上実行すると、フォームを右クリックするとメニューが表示され、
>そのメニューから項目を選択すると標準モジュール内のプロシージャ
>[ FirstStringConv ] 、[ condStringConv ] を呼び出せました。
>
>ここからが質問なのですが、このメニューから標準モジュール内では
>なく、フォームモジュール内に書かれてあるプロシージャを呼び出す
>という場合には、 .OnAction = の次にどのような書き方で実行したい
>プロシージャ名を指定すればよいのでしょうか?
提示されたコードのように、Onactionプロパティには、
標準モジュールのプロシジャーを指定して、

このプロシジャーの中からユーザーフォームのプロシジャーを呼び出す方法も
考えられます。

標準モジュール

sub FirstStringConv()
  userform1.フォームのプロシジャー名
end sub

(尚、この時、ユーザーフォームの呼び出すプロシジャーは 
Public宣言されている必要があります)


public sub フォームのプロシジャー名()


が、
コードや仕様にもよりますが、
各モジュール間の結合度が強くなってしまうことが懸念されます。


ユーザーフォームのモジュールを

Option Explicit
Dim myCB As CommandBar
Private WithEvents btn_FirstStringConv As Office.CommandBarButton
Private WithEvents btn_SecondStringConv As Office.CommandBarButton

Private Sub btn_FirstStringConv_Click(ByVal Ctrl As Office.CommandBarButton, CancelDefault As Boolean)
  MsgBox "FirstStringConvの中のコードを実行"
'ここに第1次変換モードというボタンがクリックされたときの
'コードを記述します。

End Sub

Private Sub btn_SecondStringConv_Click(ByVal Ctrl As Office.CommandBarButton, CancelDefault As Boolean)
  MsgBox "SecondStringConvの中のコードを実行"
'ここに第2次変換モードというボタンがクリックされたときの
'コードを記述します。

End Sub

Private Sub UserForm_Initialize()
  On Error Resume Next
  CommandBars("tbMenu").Delete
  On Error GoTo 0
  Set myCB = CommandBars.Add(Name:="tbMenu", _
       Position:=msoBarPopup, Temporary:=True)
  With myCB
    Set btn_FirstStringConv = .Controls.Add(Type:=msoControlButton)
    With btn_FirstStringConv
      .Caption = "第1次変換モード"
    End With
  
    Set btn_SecondStringConv = .Controls.Add(Type:=msoControlButton)
    With btn_SecondStringConv
      .Caption = "第2次変換モード"
      .BeginGroup = True
    End With
  End With

End Sub


Private Sub UserForm_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)

  If Button = 2 Then
    myCB.ShowPopup
  End If


End Sub


Private Sub UserForm_Terminate()

  On Error Resume Next
  myCB.Delete
  On Error GoTo 0
 
End Sub


このようにOnactionではなく、イベントプロシジャーで処理することを
考えると他のモジュールとの関わり合いが少なくなり、結合度が弱くなりそうです。

検討してみてください。

【58115】Re:プロシージャの呼び出し方について
発言  そう  - 08/10/3(金) 9:29 -

引用なし
パスワード
   ▼ichinose さん:

>提示されたコードのように、Onactionプロパティには、
>標準モジュールのプロシジャーを指定して、
>
>このプロシジャーの中からユーザーフォームのプロシジャーを呼び出す方法も
>考えられます。
>
>標準モジュール
>
>sub FirstStringConv()
>  userform1.フォームのプロシジャー名
>end sub
>
>(尚、この時、ユーザーフォームの呼び出すプロシジャーは 
>Public宣言されている必要があります)
>
>例
>public sub フォームのプロシジャー名()
>
>
>が、
>コードや仕様にもよりますが、
>各モジュール間の結合度が強くなってしまうことが懸念されます。
>
>
>ユーザーフォームのモジュールを
>
>Option Explicit
>Dim myCB As CommandBar
>Private WithEvents btn_FirstStringConv As Office.CommandBarButton
>Private WithEvents btn_SecondStringConv As Office.CommandBarButton
>
>Private Sub btn_FirstStringConv_Click(ByVal Ctrl As Office.CommandBarButton, CancelDefault As Boolean)
>  MsgBox "FirstStringConvの中のコードを実行"
>'ここに第1次変換モードというボタンがクリックされたときの
>'コードを記述します。
>
>End Sub
>
>Private Sub btn_SecondStringConv_Click(ByVal Ctrl As Office.CommandBarButton, CancelDefault As Boolean)
>  MsgBox "SecondStringConvの中のコードを実行"
>'ここに第2次変換モードというボタンがクリックされたときの
>'コードを記述します。
>
>End Sub
>
>Private Sub UserForm_Initialize()
>  On Error Resume Next
>  CommandBars("tbMenu").Delete
>  On Error GoTo 0
>  Set myCB = CommandBars.Add(Name:="tbMenu", _
>       Position:=msoBarPopup, Temporary:=True)
>  With myCB
>    Set btn_FirstStringConv = .Controls.Add(Type:=msoControlButton)
>    With btn_FirstStringConv
>      .Caption = "第1次変換モード"
>    End With
>  
>    Set btn_SecondStringConv = .Controls.Add(Type:=msoControlButton)
>    With btn_SecondStringConv
>      .Caption = "第2次変換モード"
>      .BeginGroup = True
>    End With
>  End With
>
>End Sub
>
>
>Private Sub UserForm_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
>
>  If Button = 2 Then
>    myCB.ShowPopup
>  End If
>
>
>End Sub
>
>
>Private Sub UserForm_Terminate()
>
>  On Error Resume Next
>  myCB.Delete
>  On Error GoTo 0
> 
>End Sub
>
>
>このようにOnactionではなく、イベントプロシジャーで処理することを
>考えると他のモジュールとの関わり合いが少なくなり、結合度が弱くなりそうです。
>
>検討してみてください。


ありがとうございます。
教えていただいたコードで問題は無事解決いたしました。

ところで、教えていただいた解説の中で OnAction を使用すると各モジュール間の結合度が強くなってしまうことが懸念されるとあるのですが、これはいったいどんな不具合に関わってくるのでしょか?
と言うのも、もしなるべく OnAction でプロシージャを呼び出すこと自体(呼び出したいプロシージャがフォームモジュールではなく標準モジュールに置かれていても)避けるべきで、極力呼び出したいプロシージャはフォームモジュール内に置いてイベントプロシージャで処理をした方が安全(?)なのならば、今後もこの教えていただいた書き方を活かして行きたいと思っているのですが・・・・・・。
いかがなものなのでしょうか?

ご指導よろしくお願いいたします。

【58121】Re:プロシージャの呼び出し方について
発言  ichinose  - 08/10/4(土) 8:21 -

引用なし
パスワード
   ▼そう さん:
>ところで、教えていただいた解説の中で OnAction を使用すると各モジュール間の結合度が強くなってしまうことが懸念されるとあるのですが、これはいったいどんな不具合に関わってくるのでしょか?

不具合ねえ・・、

プログラムの構造的な問題ですから、このプログラム作成後の問題です。


プログラムのメンテナンスを行うのにそれがしやすい構造になっているか

再利用可能な構造、つまり、他のプログラムで簡単に運用できるような構造になっているか


こんな目的のための判断です。


>と言うのも、もしなるべく OnAction でプロシージャを呼び出すこと自体(呼び出したいプロシージャがフォームモジュールではなく標準モジュールに置かれていても)避けるべきで、極力呼び出したいプロシージャはフォームモジュール内に置いてイベントプロシージャで処理をした方が安全(?)なのならば、今後もこの教えていただいた書き方を活かして行きたいと思っているのですが・・・・・・。

「OnActionプロパティは使うべきではない」という事ではないですよ!!

今回、問題対象になっているコマンドバー(ポップアップメニュー)を
標準モジュールで一括管理するようなプログラム構造ならば、
その中でOnactionプロパティに標準モジュールのプロシジャー名を指定することは
まったく問題ないと思います。

むしろ、事象によっては、Onactionプロパティの方が、安定した動作が保証されます。

Private WithEvents btn_FirstStringConv As Office.CommandBarButton
Private WithEvents btn_SecondStringConv As Office.CommandBarButton

これらの変数が何かのタイミングで初期化されてしまうという現象が発見されています。
(シートやExcelの機能を自由にユーザーが使えるような仕様で その間も上記のような
イベントを発生させるようなオブジェクト変数の中身をきちんと保持させたい
場合は、要注意です。
今回の場合だと、ユーザーフォームがモーダレスで表示するような仕様だと問題が発生する可能性があります。私は、モーダル表示を前提にしての投稿です)


が、

今回の場合は、Onactionプロパティに直接ユーザーフォーム内のプロシジャーが
指定できないので中継役として標準モジュールのプロシジャーを使っているのですよね?

そもそもコマンドバーのボタンクリック時の処理として、
ユーザーフォーム内のプロシジャーを指定したいということは、このコマンドバーが
ユーザーフォームと深く関わっているからだと思います。
(ユーザーフォームが表示されている時だけ、使用可能なコマンドバーで、
しかもその機能が当該ユーザーフォームに特化した機能の場合)

もし、標準モジュールを中継にした場合、


このユーザーフォームの仕様書には、

但し書きとして、

「このユーザーフォームを運用の際は、
標準モジュールに中継としてのプロシジャーとして、

以下のプロシジャーを記述しなけれなならない」

なんて、記述が必要になってしまいます。

Excelが提供しているWorkbookやWorksheetやRange等のオブジェクトに
こんな但し書きがあったら、使いづらいことこの上ないですよね?

ユーザーフォームもオブジェクトです。

出来る限り、こういう但し書きが要らない仕様にするべきだという
立場からの意見です。


但し、前述したような不具合に対処するためなら、これらの構造的なことは
目をつぶらなければならないでしょうねえ・・・。

プログラム構造に重きをおくあまり、バグ連発ではお話になりませんから・・・。

【58122】Re:プロシージャの呼び出し方について
お礼  そう  - 08/10/4(土) 9:59 -

引用なし
パスワード
   ▼ichinose さん:

丁寧な回答ありがとうございました。

そういう意味だったのですね。今回教えていただいた
貴重なアドバイス、無駄にしないよう今後の勉強に
活かして行きます。

4161 / 13644 ツリー ←次へ | 前へ→
ページ:  ┃  記事番号:
2610219
(SS)C-BOARD v3.8 is Free