[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]
『コマンドボタンのイベントをクラスで一括処理』(隠居じーさん)
○ Excel Q&Aサロン(VBA)で教えていただいた事を流用させていただき シート上にマクロ分岐のメニューを作成しようとしています。 http://excelfactory.net/excelboard/excelvba/excel.cgi?mode=all&namber=188364&rev=0 ○ 今年はクラスを勉強したいと思っています。
シート名、Sheet1、上にアクテイブXのコマンドボタンを8個配置(VBA) イベントをクラスで纏め、 アクテイブ(フォーカスをもつ?)なコマンドボタンを赤色に その他を既定値、若しくは白にしたいのですが。うまく 配色出来ません。なにせ初めてクラス、作りましたので。 基本が間違っているかもしれません。 クリック、とキーダウンは動いています。 ※使用方法についてもアドバイスお願いします。 不安点。。。 ループ回しているのですが、testpが実行された時点で 抜けているのか(多分抜けていない気がするのですが^^;) 抜けていないのかよくわかりません。???
下記に作成したコードを貼り付けますので、ご教授戴ければ幸いです 宜しくお願い致します つっこみ大歓迎です ^^
作成したクラス、myButton Option Explicit Public WithEvents btn As MSForms.CommandButton Private btnno As Long Private s1 As Worksheet Private id As Long Private Sub btn_click() Application.Run "Module1.testp", Right(btn.Name, 1) End Sub Private Sub btn_GotFocus() btn.BackColor = &H1000FF End Sub Private Sub btn_LostFocus() btn.BackColor = &HFFFFFF End Sub Private Sub btn_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer) Set s1 = Worksheets("Sheet1") id = Right(btn.Name, 1) If Shift = 0 Then Select Case KeyCode Case vbKeyDown KeyCode = 0 s1.OLEObjects("CommandButton" & (id Mod 8 + 1)).Activate Case vbKeyUp KeyCode = 0 s1.OLEObjects("CommandButton" & IIf(id = 1, 8, id - 1)).Activate Case vbKeyReturn KeyCode = 0 Application.Run "Module1.testp", id End Select End If End Sub Private Sub Class_Terminate() Set btn = Nothing Set s1 = Nothing End Sub 標準モジュール Option Explicit Public Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Long Sub main() Dim s1 As Worksheet Dim i As Long Dim btn(1 To 8) As myButton Set s1 = Worksheets("Sheet1") For i = 1 To 8 Set btn(i) = New myButton Set btn(i).btn = s1.OLEObjects(i).Object Next s1.OLEObjects(1).Activate Do If GetAsyncKeyState(27) <> 0 Then Exit Do End If DoEvents Loop End Sub Sub testp(ByVal id As Long) MsgBox id End Sub
******************************* もしお試し戴けるなら、コマンドボタン配置コードです。^^; シート名、Sheet1が対象です。
Private Sub Ax_Sh_Set() Dim i As Long, br As Range, btn() Dim sbr Dim menustr menustr = Array("1: DB 入力", "2: 印 刷", "3: 印刷プレビュー", _ "4: EXCELに戻る", "5: 終 了", "6: CSVダンプ", _ "7: 予 備 A", "8: 予 備 B") sbr = Array("B3:J4", "B7:J8", "B11:J12", "B15:J16", "L3:T4", "L7:T8", "L11:T12", "L15:T16") With Worksheets("Sheet1") If .OLEObjects.Count > 0 Then Call btn_delete End If For i = 0 To UBound(menustr) Set br = .Range(sbr(i)) ReDim Preserve btn(i) Set btn(i) = .OLEObjects.Add(ClassType:="Forms.CommandButton.1", _ Left:=br.Left, Top:=br.Top, Width:=br.Width, Height:=br.Height) Next For i = 0 To UBound(btn) btn(i).Object.Caption = menustr(i) btn(i).Object.Font.Size = 16 btn(i).Object.BackStyle = 1 Next End With End Sub Private Sub btn_delete() Dim btn As Object For Each btn In ActiveSheet.OLEObjects btn.Delete Next End Sub
< 使用 Excel:Excel2016、使用 OS:Windows10 >
私も以前ここで勉強させていただいた時に教えていただいたことを掻い摘んでツッコミ入れさせてもらいますね。
■あんまり得意じゃないけど、btnはMSForms.CommandButtonオブジェクトなわけですよね? であればGotFocusというイベントはないと思うのですが・・・。 https://kosapi.com/post-4082/
参照先の掲示板を斜め読みしましたが、色を変える部分はClickイベントとして紹介されていたように 見えます。
■話を戻して、隠居じーさんさんが「アクティブ」と考える状態がわかりません。 1)マウスをのせたとき? 2)クリックした処理中? 3)トグル方式? http://kabu-macro.com/word/ta-to/toggle_button.html
1だとすると、MouseOverイベントがなかった記憶があるので、かなり難しいと思います。(MouseMoveイベントで代用?) 2の場合、クリックイベントに色を付け、他のボタンの色を消す処理をすればよいかと思います。 3の場合、最初からトグルボタンコントロールを使ったほうが自然かもしれません。
■別件で、hatenaさんが提案されていた「使いまわしのきくもの」について、コードを拝見するに 標準モジュールとクラスモジュールの結合度が高いように思います。 Application.Run "Module1.testp", Right(btn.Name, 1) ~~~~~~~~~~~~~~この部分とか 下線部分を含めて、クラスオブジェクトに渡しておけば、呼び出す側(シートのボタン)から 「クリックしたらどのプロシージャを動かすか」がわかっていいですよね。 http://www.kogures.com/hitoshi/webtext/kj2-module/index.html
以上、気になった点でした。 (稲葉) 2019/01/08(火) 15:44
GetFocusイベントは、OLEObjectのイベントであり、MSForms.CommandButton型にはないです。
じゃあ、btnをOLEObject型にすればいいじゃないか、となりますが、 実際やってみると、実行時エラーになります。
ネットで検索すると、どうもWithEventsが付いているとダメらしい。 【Accessing OLEObject events in Excel VBA using custom class】 https://stackoverflow.com/questions/10761973/accessing-oleobject-events-in-excel-vba-using-custom-class
【このコンポーネントは、このイベント セットをサポートしていません (エラー 459)】 https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/this-component-doesn-t-support-this-set-of-events-error-459?f1url=https%3A%2F%2Fmsdn.microsoft.com%2Fquery%2Fdev11.query%3FappId%3DDev11IDEF1%26l%3Dja-JP%26k%3Dk(vblr6.chm1000459)%3Bk(TargetFrameworkMoniker-Office.Version%3Dv16)%26rd%3Dtrue ObjectEvent を発生させるために必要な型情報を実行時に使用できないため、 プライベートの UserControl では WithEvents はサポートされない。
どうも、理屈通りにはいかないようです。
KeyDownイベントで、どのボタンをアクティブにするかは分かっているので、 逆算して、どのボタンがそれまでアクティブだったかも判断できると思いますので、 KeyDownイベントの方に色の着脱の仕組みを盛り込んだらどうでしょうか?
( 半平太) 2019/01/08(火) 16:58
稲葉さま 早々のご返信ありがとうございます。 ■1: やはり! w ^^; 無いのでしょうね。、ただ、シートモジュールで【】▼オブジェクトを commandbutton1にして【】プロシージャー▼をクリックすると有って Private Sub CommandButton1_GotFocus() End Sub が自動で記述されたりするもので、でもなぜかclassモジュールだと稲葉さま、のご指摘通り 表示一覧には有りませんでした。 ■2: 矢印キーで選択、リターンが押せる状態を想定いたしておりました。 ■3: すうごぉ〜く勉強になりそうですね。チラ見しかできていませんが今から良く読んでみます。 サイト紹介有難うございました。 クラス側に掘りこんじゃへってことでしょうかね。
イベント。。。ループなど必要ないですよね。 シートモジュールににイベントをボタン(8)*(4イベント、 クリック、ゴットフォーカス、ロストフォーカス、キーダウン、分、 記述方法では思い通りのものが出来ております。 クラス使用分は ループ外せば動きません。 いま何かきつねにつつまれたような錯覚に陥っています^^; クラスの書き方、使い方。。。あっていますでしょうか。な〜んか変だな。。。 とか思い、自信が持てない状況です。A^_^; 配色についてはなにか他の方法を考えてみます。 今赤色のボタン以外全て色を変えるとか。。。 貴重なお時間をおさきいただき有難うございます。 (隠居じーさん) 2019/01/08(火) 17:02
半平太さま いつもおせわになります。 >>KeyDownイベントの方に色の着脱の仕組みを盛り込んだらどうでしょうか? はい やってみます。 解説サイトのご紹介、有難う御座います。 いまからゆっくり読んでみますね。 とりいそぎ お礼まで。 有難うございました。m(__)m
(隠居じーさん) 2019/01/08(火) 17:06
>クラス使用分は >ループ外せば動きません。 >いま何かきつねにつつまれたような錯覚に陥っています^^;
シートにあるボタンは、常時、実存していますが、 クラスのボタンは、mainが動いている間(=ループが回っている間)だけ 存在しているものですからねぇ・・
まぁ、myBtnの変数宣言をプロシージャレベルからモジュールレベルへ格上げ(?)すれば、存在はキープされます。
ただし、以前、別のトピックで話題になった通り、 「意図しないタイミングで保持されていた Public 変数の値が破棄される」ことがあります。
なので、常に実存している、とまでは言えないのが悩みの種です。
( 半平太) 2019/01/08(火) 19:10
半平太さん 勉強になりました。 >じゃあ、btnをOLEObject型にすればいいじゃないか、となりますが、 >実際やってみると、実行時エラーになります。 私の指摘はいつも的外れですね。
隠居じーさんさん ちょっと思い出しながら自分ならこうするかなというの作りました。 だけど、シートモジュールレベルの変数って、よからぬタイミングで消えた記憶があるので たぶん、本番では使わないかな・・・
楽しい課題をありがとうございました。
'◆シートモジュール Option Explicit Private dic As Object '============================================================ 'シートがアクティブになった時に、シートレベル変数のdicにクラスを配列として入れる '============================================================ Private Sub Worksheet_Activate() Dim i As Long Dim obj As OLEObject Set dic = CreateObject("Scripting.Dictionary") i = 1 For Each obj In Me.OLEObjects dic.Add i, New clsButton With dic(i) Set .obj = obj.Object 'OLEオブジェクトをクラスに登録 Set .Parent = Me 'このシートをクラスに登録 .Method = "CallBack" 'このシートのプロシージャをクラスに登録 .ID = i 'ボタンのナンバーをクラスに登録 .Caption = obj.Object.Caption 'ボタンのキャプションをクラスに登録 End With i = i + 1 Next obj End Sub
'============================================================ 'シートがアクティブじゃなくなった時に、dicとクラスを開放する '============================================================ Private Sub Worksheet_Deactivate() Set dic = Nothing End Sub
'============================================================ 'クラスから呼び出されるプロシージャ '============================================================ Public Sub CallBack(ParamArray arg() As Variant) 'arg引数表 '0:イベント名 '1:ID '2:コントロールオブジェクト '3:コントロールキャプション '4:キー Select Case arg(0) Case "Click" MsgBox arg(3) & "が押されました" Case "KeyDown" Call KeyDownEvent(arg(4), arg(1)) End Select End Sub
'============================================================ 'キーダウンイベントの処理 '============================================================ Private Sub KeyDownEvent(k As Variant, ID As Variant) Dim moveID As Long Select Case k Case vbKeyLeft: If ID > 4 Then moveID = -4 Case vbKeyRight: If ID < 5 Then moveID = 4 Case vbKeyUp: If Not (ID = 1 Or ID = 5) Then moveID = -1 Case vbKeyDown: If Not (ID = 4 Or ID = 8) Then moveID = 1 Case vbKeyReturn With dic(ID) Call CallBack("Click", ID, .obj, .Caption) End With End Select dic(ID).obj.BackColor = &HFFFFFF With dic(ID + moveID).obj .Activate .BackColor = &H1000FF End With End Sub
'◆クラスモジュール 名前clsButton Option Explicit Public WithEvents obj As MSForms.CommandButton
'============================================================ '共通の設定 '============================================================ Public Parent As Object Public ID As Long Public Method As String Public Caption As String
'============================================================ 'イベント一覧 '============================================================ 'CallBack引数表 '0:イベント名 '1:ID '2:コントロールオブジェクト '3:コントロールキャプション '4:キー Private Sub obj_Click() CallByName Parent, Method, VbMethod, "Click", ID, obj, Caption End Sub
Private Sub obj_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer) CallByName Parent, Method, VbMethod, "KeyDown", ID, obj, Caption, KeyCode End Sub
'============================================================ '使った後の処理 '============================================================ Private Sub Class_Terminate() Set obj = Nothing Set Parent = Nothing End Sub
(稲葉) 2019/01/08(火) 19:12
半平太さん 、稲葉さん 再度、有難う御座います。 半平太さん >>GetFocusイベントは、OLEObjectのイベントであり、MSForms.CommandButton型にはないです。 ご紹介いただいた英語サイトは。。。眺めるだけで終了致しました。^^; クラスでは使えないと言う事で理解致します。 ループの件、解りやすいご説明ありがとうございます。 稲葉さん、コード、有難う御座います。 ゆっくり、謹んで拝見させて戴きます。 まだ試せておりませんが、明日、試して見ます。
本校の過去ログの(クラスの件) 稲葉さんと、半平太さん、ichinoseさん等とのやりとりも拝見させていただきましたが 正直、私のレベルではついていけませんでした。ただ、Public変数消え去り対策になると推測(わたしの)される、参照設定、せずにアドインを使う方法はすごく興味があります。 どぉするのでしょうか。 消え去り対策としては参照設定でも十分なのでしょうか。 グローバル変数消え去り問題、はクラスモジュールも対象外では無いのでしょうか。 質問ばかりで済みません。お手すきの時にでも、ご教授賜れば幸甚です。 m(_ _)m
(隠居じーさん) 2019/01/08(火) 22:42
(隠居じーさん) 2019/01/08(火) 22:42の >>グローバル変数消え去り問題、はクラスモジュールも対象外では無いのでしょうか。 については 過去ログ調べていましたら マイクロソフト様の説明引用箇所に下記の様な文言があったかと記憶しております。 >補足 : この文書で説明する Public 変数とは、 >すべてのモジュール レベル変数および静的変数を指します。 ということは クラスよ、おまえもか! との理解でよろしいのでしょうか。 だとすると ^^;www クラスはメンバ変数ってモジュールレベルそのものなのでは。。 今回のような場合とかユーザーフォームを使う場合、イベント処理もどっさり。。。 こわいですね〜おそろしいですね〜 とりあえず 以前、別のトピックで教えていただいた アドイン、参照設定で対応しようと思います。 え、でもすると アドインにクラス書かないといけないのでしょうか (。。。危うきに。。。とか)隠居じーさん ↑ 漠然と【え!消えるの】と危機感はあるものの、何がどう危ないのかあまりわかっていないA^_^;でした。 洋上で羅針盤をなくし、漂う小舟をなにとぞ港まで。。。。。 m(__)m
(隠居じーさん) 2019/01/09(水) 07:52
これでは、クラスにする意味がほとんどないです。
Sheet1モジュールにコードを記述したほうがよほどシンプルかつメンテナンスしやすいです。
クラスにするからには、複数のブック、シートで利用できて、ボタンの数も自由に設定できるようにした方がいいでしょう。
今回の要件は、
シート上のActiveXコマンドボタンをグループ化して、その中の一つだけを選択できるようにする。
選択はクリックだけでなくキーボードの上下キーでも移動できるようにして、
Enterキーでクリックと同じ処理を実行する。
ということですよね。
VBA - VBAのボタンの色に関して|teratail
https://teratail.com/questions/162580#reply-243205
上記の私の回答をもとにキーボードでも選択できるように改修してみました。
◆クラスモジュール selectButton Option Explicit Private OleObj As OLEObject Private WithEvents btn As MSForms.CommandButton Private Slected_ As Boolean '選択 Private ParentGroup_ As SelectButtonGroup Private Index_ As Long
'クラスにコマンドボタンと属する親グループ(SelectButtonGroup)を登録する。 Public Sub setBtn(ByVal Obj As OLEObject, ByVal Parent As SelectButtonGroup, Index As Long) Set OleObj = Obj Set btn = Obj.Object btn.BackColor = vbButtonFace Set ParentGroup_ = Parent Let Index_ = Index End Sub '選択状態の設定 Public Property Let Slected(ByVal new_Slected As Boolean) If Slected_ <> new_Slected Then Slected_ = new_Slected If Slected_ Then btn.BackColor = vbRed OleObj.Activate Else btn.BackColor = vbButtonFace End If End If End Property Private Sub btn_Click() ParentGroup_.SelectIndex = Index_ End Sub Private Sub btn_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer) If Shift = 0 Then Select Case KeyCode Case vbKeyDown KeyCode = 0 ParentGroup_.SelectIndex = Index_ + 1 Case vbKeyUp KeyCode = 0 ParentGroup_.SelectIndex = Index_ - 1 Case vbKeyReturn KeyCode = vbKeySpace End Select End If End Sub
◆クラスモジュール SelectButtonGroup Option Explicit Private BtnGrp As Collection Private SelectIndex_ As Long '選択されているボタンのインデックス '選択されるボタンを設定 Public Property Let SelectIndex(ByVal newIndex As Long) If SelectIndex_ > 0 Then BtnGrp(SelectIndex_).Slected = False End If If newIndex < 1 Then SelectIndex_ = BtnGrp.Count ElseIf BtnGrp.Count < newIndex Then SelectIndex_ = 1 Else SelectIndex_ = newIndex End If BtnGrp(SelectIndex_).Slected = True End Property '選択されているボタンのインデックスを取得 Public Property Get SelectIndex() As Long SelectIndex = SelectIndex_ End Property 'コマンドボタンをクラスに登録 Public Sub Add(ByVal OleObj As OLEObject) Dim selBtn As selectButton Set selBtn = New selectButton selBtn.setBtn OleObj, Me, BtnGrp.Count + 1 BtnGrp.Add selBtn End Sub Private Sub Class_Initialize() Set BtnGrp = New Collection End Sub Private Sub Class_Terminate() Dim selBtn As selectButton For Each selBtn In BtnGrp Set selBtn = Nothing Next Set BtnGrp = Nothing End Sub
'◆ThisWorkbookモジュール Option Explicit Private MenuBtnGrp As SelectButtonGroup Private Sub Workbook_BeforeClose(Cancel As Boolean) Set MenuBtnGrp = Nothing End Sub Private Sub Workbook_Open() Dim i As Long With Worksheets("Sheet1") Set MenuBtnGrp = New SelectButtonGroup For i = 1 To 8 MenuBtnGrp.Add .OLEObjects("CommandButton" & i) Next End With MenuBtnGrp.SelectIndex = 1 End Sub
'◆Sheet1モジュール Option Explicit Private Sub CommandButton1_Click() MsgBox "コマンド1" End Sub Private Sub CommandButton2_Click() MsgBox "コマンド2" End Sub Private Sub CommandButton3_Click() MsgBox "コマンド3" End Sub
(hatena) 2019/01/09(水) 10:29
hatenaさん おはようございます。^^ コードありがとうございます。 >>今回の要件は、 につきましてはご指摘のとおりです。
いま 稲葉さんのコードを印刷して拝見いたしておりました。 コールバック関数、さらにそのサブルーチンからコールバックを再帰、等 感嘆、感激しておりました。何とかソースは追えるものの、いざ自分が コーディング!となれば、なかなか思いつきません。^^; すごく勉強になります。 後ほど hatenaさんのコードをゆっくり、勉強させて戴きます。 取り急ぎ御礼まで。 半平太さま、稲葉さま、hatena様、 お忙しい中、貴重な情報を賜り恐縮でございます。 深く感謝もうしあげます。ありがとうございました。
(隠居じーさん) 2019/01/09(水) 11:18
>クラスよ、おまえもか! >との理解でよろしいのでしょうか。
その通りだと思います。
>クラスはメンバ変数ってモジュールレベルそのものなのでは。。 >今回のような場合とかユーザーフォームを使う場合、イベント処理もどっさり。。。 >こわいですね〜おそろしいですね〜
ちょっと意味が分からないですが、心配し過ぎじゃないですか?
> 漠然と【え!消えるの】と危機感はあるものの、
何せ、「意図しないタイミングで・・」と言っているんですから、漠然とならざるを得ないです。
ichinoseさんのアドバイスは、このスレですよね?
『モジュール内での変数について』(ゆき夫) [[20100731000846]] http://www.excel.studio-kazu.jp/kw/20100731000846.html
ichinoseさんの対策はおおむね以下かと。 --------------------------------------------------------------- 一般的な対策としては、できるだけ実データで保持する。 例えば、シートオブジェクトであっても、シート名をどこかに(EX.セル)文字データとしてメモしておけば、 必要な時に再構築(再参照)できる。
上述代替方法が無い場合、 (例えば、イベントを有しているオブジェクト等)
「別ブック内に変数だけを配置する」
Book1がメインプログラムがあるブックに対し、 Book2が変数だけを配置するブックだとすると、
Book2のThisWorkbookのモジュールに
Private bk as workbook
Book1にて
Sub AppSet() Set Workbooks("Book2").bk=thisworkbook MsgBox workbooks("book2").bk.name end sub
いまところ、変数を別ブックに配置し、そのブックは、非表示又は、専用アドインブックにしておく ことで値を保持してくれています。
これで大丈夫という結論ではありませんが、別ブックに変数を配置する方法も試してみてください。 -------------------------------------------------------------
以上に従うと、
1.別ブックを作る(仮にブック名を shelter とする)
2.shelterブックのThisWorkbookモジュールに以下の変数宣言をする Public bk As Workbook ↑ ※ichinoseさんの記述ではPrivateですが、それでは動かないのでPublicにする
3.上記ブックをアドインブックとして保存する。
4.エクセルのオプションでshelterのアドインを有効にする
5.本体のプログラムがある方に
Sub AppSet() Set Workbooks("shelter.xlam").bk = ThisWorkbook '自ブックのオブジェクトをshelterの変数に格納する End Sub
これを実行して置けば、絶対大丈夫とは言わないが、 こちらのブックのモジュールレベルの各変数の値は勝手に消失しない
・・と言っているのではないか・・
そう考えないと、ThisWorkBookを退避用に格納する意味が分からなくなります。 (ThisWorkbookは常に実存するので、消える訳ないので)
本人に解説してもらえるといいんですが、最近、ichinoseさんの訪問は無いですからねぇ・・
まぁ、効果の程も確かめようがないので、 今、考えついたもっと確実なアイデア・・ ↓ よく使うコマンドボタン(無ければ、「開始」とか「リセット」とかを新設して)、 その特殊ボタンは従来通りの方法でのイベントも処理させ、 そこでクラス配列を再組成させるように組んどけば確実。
ユーザーなんて、おかしくなると「あれ?」とか言いながら、そこらじゅうの ボタンをクリックし始めるので、その動物的習性を利用する。
( 半平太) 2019/01/09(水) 13:44
>ユーザーなんて、おかしくなると「あれ?」とか言いながら、そこらじゅうの >ボタンをクリックし始めるので、その動物的習性を利用する。 今年の初笑い!
たまに挙動がおかしくなるプログラムがあるので仕込んでみます。
(稲葉) 2019/01/09(水) 14:29
半平太さん 【ichinoseさんの対策】解説ありがとうございます。 もういちど読み直してみます。
わたしもおかしく成ったらやたらボタン押したりしてます、@@:(笑)
プログラムそのものをこっそり、再起動させる。? でしょうか(開始メソッドをもう一度呼び込むとか)
(隠居じーさん) 2019/01/09(水) 15:25
皆様、おはようございます^^ 稲葉さま、hatena様、ご提示いただいた、コード、使わせていただきました。 さすがですね。感動です。 Callbyname って知りませんでした。^^; 今後、クラスの作成にあたり、よきお手本とさせていただきます。 矢印キーの左、右でも左右に上下で1〜8,8〜1、循環、見たいに お二方の矢印キーの動きを合わせたような動きに変更してみます。 ご教示いただきました、全ての皆様にあらためてお礼申し上げます、ありがとうございました。 確かにこれだとボタンがもっと増えても、シートモジュールに、山ほどイベントプロシジャーを 書かなくてすみますね。少しですがクラスの便利さみたいなのが解ったような気がします。 半面、難しさも。。。
m(_ _)m
(隠居じーさん) 2019/01/10(木) 10:58
いいお手本が得られて良かったですね。
ところで、このオブジェクトはどうなっちゃたんですか? ↓ Dim btn(1 To 8) As myButton
それがないと、私には分かりにくくてしょうがないんですけど。 コレクションに入れたぁ? でも分かりにくいことに変わりはないなぁ・・
コレクションに入れないといけないんですかね?
( 半平太) 2019/01/10(木) 17:43
↑ すみませーん。 m(__)m
私が分かりにくいだけかも知れないので、無視してください。
( 半平太) 2019/01/10(木) 18:06
コレクションに入れる必要、まったくありませんでした。 おっしゃる通り、型宣言でクラスを指定したほうが、可読性よくなりました。
ご指摘ありがとうございます。 (稲葉) 2019/01/10(木) 18:21
隠居じーさんWrote
確かにこれだとボタンがもっと増えても、シートモジュールに、山ほどイベントプロシジャーを 書かなくてすみますね。
ということですね。
今回の質問は下記のような仕様です。
シート上のActiveXコマンドボタンをグループ化して、その中の一つだけを選択できるようにする。
選択はクリックだけでなくキーボードの上下キーでも移動できるようにして、
Enterキーでクリックと同じ処理を実行する。
この仕様の場合、キーボードイベントとクリックイベントに結構な量のコードを記述する必要があります。
稲葉さんのクラスは、このコードをクラスからのコールバック関数としてシートモジュールに記述することで一つに纏めてます。
同じ仕様のボタングループを別のシートやブックで使用したい場合、その都度、コールバック関数を記述する必要があります。
このコードもクラスの方にいれておけば、シートモジュールには、
クラスに登録するコードだけですみます。
私のクラスはこれを目指して作成しました。
このような設計にしておけば、もし仕様の変更、機能の追加があったとき、クラスモジュールだけを変更すればすみます。
逆に言えば、この仕様のボタンを一か所だけでしか使わないのなら、クラスにするメリットはほとんどありません。
キーボードイベントに記述するコードをSubプロシージャとして取り出して、
キーボードイベントからCallするようにするぐらいでいいかと思います。
その方か分かりやすいと思います。
ボタングループ内のボタンの数は自由に設定できるようにするには、
ボタンのクラスと、それを複数登録するボタングループのクラスが必要になりました。
そのために、クラスの構成はかなり複雑になりました。
あちこちで使いまわさないと割にあわないとも言えます。
ちなみに、私の場合、Workbook_Open でクラスを登録するようにしましたが、
稲葉さんのは、Worksheet_Activate でクラス登録してます。
私もどちらにしようか迷ったのですが、
このボタンを配置したシートはメニュー的なもので、ここで処理を選択して他のシートで作業して、
また、ここに戻って処理を選択するという使い方かと思いましたので、
戻るたびに初期化されるのはちょっと違うかなという気がしたので ThisWorkbookの方に記述しました。
(hatena) 2019/01/10(木) 22:13
hatenaさん こんばんは ^^ 当初クラスを一度作成してみようと思った動機がご推察の様な事でしたので。 あまり、深く、考えていませんでした。 確かに、どのシートにも、対応出来、個数にも対応可能だと、一段と汎用性はアップ、 どのBOOKのどのシートでも、メニューがすぐに作れますね。 詳細にわたる、解りやすいご説明、並びに、クラス設計の大切さも教えていただき。 ありがとうございます。大変勉強になります。 私のスキルでは、仮に設計段階で分かっていたとしても、組めなかったと思います。 クラスの中で他のクラスを使おうとは多分考えが及ばなかったのではと思います。 当初、チラッと。。。シート名、と、個数と、表示タイトルをパラメーターにとる メニュー自動作成クラス。。。え! そんなの、いきなり、無理無理 無謀なことを考えるでない。。。( ̄▽ ̄)。。。 とか、頭の片隅をよぎっていました。(笑)
また、とんでもないクラス(専門家の方がみれば、腹抱えて笑い出すよ〜なの) 作るかもしれませんが、こちらで、アップさせていただく 機会などありましたら。突っ込みお願いいたします。ありがとうございました。 感謝致します。m(_ _)m
(隠居じーさん) 2019/01/10(木) 23:30
隠居じーさん(敬称略で呼ばせていただきます。)
こちらこそ、興味深い題材を提供していただいて、 楽しく取り組めました。 作成するなかで、いろいろ勉強にもなりました。 感謝します。
メニュー自動作成クラス。。。私もチラッと思いました。
具体的には、SelectButtonGroup クラスに ParentSheetプロパティとCreateMenuメソッドを追加して、 下記のようなコードで生成して登録する感じかなぁ?
Private MenuBtnGrp As SelectButtonGroup
Private Sub Workbook_Open() Set MenuBtnGrp = New SelectButtonGroup MenuBtnGrp.ParentSheet = Worksheets("Sheet1") MenuBtnGrp.CreateMenu "1: DB 入力", "B3:J4" MenuBtnGrp.CreateMenu "2: 印 刷", "B7:J8" '中略 MenuBtnGrp.SelectIndex = 1 End Sub
簡単に実装できそうですので、ぜひ、トライしてみてください。 (hatena) 2019/01/11(金) 00:33
稲葉さん
指摘とまで受け取られると恐縮です。
隠居じーさんの目線(考え方)で考えれば、 これを生かして話が進められないか、と思ったことから発しています。 ↓ Dim btn(1 To 8) As myButton
クラスの話になると、必ず首をつっこんでくる人種がいるんですが(まぁ、私もその一人ですけど) 今回は、誰も出てこないですね。鬼籍に入っちゃったんですかねぇ。
その人種は必ず、小難しい理屈をこねて、クラスの敷居を高くしちゃうんですよねぇ。
私のスタンスは「標準モジュールやシートモジュールなどに便利なモジュールがもう1種類追加されただけ。」 あれこれMustを並べるより、自分で使いこなせるようになってもらうのが先決、と思っています。
hatenaさんの「あちこちで使いまわさないと割にあわない」且つ分かりにくいもののなんて掲示してどうすんの? と思っていたら、隠居じーさんの構想にも有った、と聞いて、凄い読みだなぁと思っています。
・・にしても、これはがっかりだなぁ。結局、そのイベント使うの? と思っちゃいます。 ↓ > '◆Sheet1モジュール >Option Explicit >Private Sub CommandButton1_Click() > MsgBox "コマンド1" >End Sub
なら、クラスを使う意味を感じないなぁ、稲葉さんのコールバック方式の方がしっくり来る、私としては。
( 半平太) 2019/01/11(金) 08:17
おはようございます。 多分 > '◆Sheet1モジュール >Option Explicit >Private Sub CommandButton1_Click() > MsgBox "コマンド1" >End Sub は わたしに、わかりやすくするために、分岐はこんな感じでなので。 後は、クラスに入れるなり、そのまま、残り(3個分記述なので)5個分コード書いて使うなり 好きなよ〜にしろ。 って 意味なのかなって思っていました。 hatenaさんにお聞きしないと真実はわかりませんが。。。^^; さて メニュー自動作成クラス。。。 私にとっては今もってエベレスト登山!みたいなものですが hatenaさん、稲葉さん、から戴いたコード、並びにヒント、半平太さんに教えていただいた。 モジュールレベルの変数対策をよく勘案、テストなどして、気長に、挑戦してみます。 今年は研究課題が出来、楽しめそぉです。 でわ m(_ _)m
(隠居じーさん) 2019/01/11(金) 09:10
半平太さん
遠回しにいわれて気づかなければそれまでですけど、答えがあっていたようでよかったです。
>あれこれMustを並べるより、自分で使いこなせるようになってもらうのが先決、と思っています。 こちらは同感です。 先生方には感謝いたしております。
隠居じーさんさん >わたしに、わかりやすくするために、分岐はこんな感じでなので。 >後は、クラスに入れるなり、そのまま、残り(3個分記述なので)5個分コード書いて使うなり >好きなよ〜にしろ。 違うと思いますよ。
クリックイベントはこれだけで、クラスモジュールから他モジュールへの接点は設定されていませんから、 おそらく私のコードを組み合わせて、
という無言の激励だと思います。
クリックイベント >Private Sub btn_Click() > ParentGroup_.SelectIndex = Index_ >End Sub
クラスから他モジュールへの接点 コマンドボタンしか渡してないので、イベントを拾っても、クラスモジュールからは、どのモジュールの どのプロシージャを実行しなければいけないかわかりません。 > Set MenuBtnGrp = New SelectButtonGroup > For i = 1 To 8 > MenuBtnGrp.Add .OLEObjects("CommandButton" & i) > Next
(稲葉) 2019/01/11(金) 10:28
おはようございます。 > > '◆Sheet1モジュール > >Option Explicit > >Private Sub CommandButton1_Click() > > MsgBox "コマンド1" > >End Sub > なら、クラスを使う意味を感じないなぁ、稲葉さんのコールバック方式の方がしっくり来る、私としては。
この部分に関しては、使いまわせるものではない、ので、コールバック方式にしなくてもいいのでは、 という感じでそうしました。クラスは、共通のイベントはクラス側に書いて、個別のイベント処理は このように使う側で書けるのがメリットの一つと思っています。
稲葉さんは、CallByName でクラスのカスタムイベントを実装していましたが、
実は、RaiseEventステートメント でカスタムイベントを実装する方法があります。
これを使うともっとシンプルにコーディング可能です。
VBA クラスモジュールに自作のEventを実装する - t-hom’s diary https://thom.hateblo.jp/entry/2016/10/13/005105
ただ、私のクラスは、 selectButtonGroup と selectButton の2段構成になっていますので、 難易度は高くなりますので、実装するのは控えてました。
話題になったので、私のクラスに クリックイベントを実装してみます。
selectButtonGroup と selectButton の2段構成になっていますが、 selectButton はコレクションに格納するのでそこでイベントは実装できません。
selectButtonGroup にカスタムイベントを実装して、 selectButton のクリックイベントから、それをコールするようにします。
クラスモジュール selectButtonGroup Option Explicit Private BtnGrp As Collection Private SelectIndex_ As Long '選択されているボタンのインデックス Public Event Click() 'クリックイベントの実装
'子クラスから呼ばれるイベント励起メソッド Public Sub RaiseClick() RaiseEvent Click End Sub
以下略
これでselectButtonGroupにカスタムイベントが実装できます。
クラスモジュールselectButtonには、※のコードを追加します。
Private Sub btn_Click()
ParentGroup_.SelectIndex = Index_ ParentGroup_.RaiseClick '※親クラスのクリックイベントを呼び出す End Sub
'ThisWorkbook モジュール
Option Explicit
Private WithEvents MenuBtnGrp As SelectButtonGroup 'WithEvents を付ける
Private Sub MenuBtnGrp_Click()
MsgBox MenuBtnGrp.SelectIndex & "番目のメニューがクリックされました。" End Sub
通常のイベントプロシージャと同様に、
Private Sub クラス変数名_イベント名()
という記述になります。
「「あちこちで使いまわさないと割にあわない」且つ分かりにくいものの」 という指摘はごもっともです。 しかし使いこなせれば、それをうわまわるメリットもあると思っていますし、 別の掲示板でのやりとりからの 流れてこちらにきているのですが、それを踏まえて、 すぐには理解できなくても、今後の役にたつことは あるのかなと思ってコード提示しました。 (hatena) 2019/01/11(金) 10:40
こんにちは ^^ >>コールバックくらい自分で完成させなさい はは〜〜〜〜 m(_ _)m おおせのままに。 hatenaさん さらなる、アドバイス。ありがとうございます。 さまざまな方法があるのですね。 いずれにしましても。まずは 諸先生のロジックのさらなる研究から始めようとおもいます。A^_^:
(隠居じーさん) 2019/01/11(金) 13:05
hatenaさん
> 「あちこちで使いまわさないと割にあわない」且つ分かりにくいもの
すみません、隠居じーさんにはニーズがあるとのことなので、割に合うクラスと認識を変えております。
・・で、'ThisWorkbook内の自作イベントプロシージャで全ボタンに対して 柔軟な処理ができる仕掛けになったので、しっくり来ています。
大変失礼いたしました。 m(__)m
( 半平太) 2019/01/11(金) 14:48
[ 一覧(最新更新順) ]
YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki.
Modified by kazu.