• 締切済み

Excel VBA トグルボタンのコントロール

お世話になります。 現在、ユーザーフォームにて管理アプリもどきを作成しているのですが、 以下の様な動作を行うことが可能かどうか、 また可能であればどのような方法があるのかをご教授ください。 ////////////////////////////////////////////////////////// フォーム上に、トグルボタンを複数個配置します。 例として、下図をトグルボタンを5つ配置したものと仮定します。 Valueプロパティは全てFalseを初期値とします。  □□□□□ 左から2つ目のトグルボタン上でマウスクリックし、 そのままクリックを押し込んだままの状態にします。 この時点で、2つ目のボタンのValueプロパティをTrueに変更します。  □■□□□    ↑クリック(押し込んだまま) クリックを押し込んだまま、マウスを右に移動させます。 左から3つ目、4つ目のボタン上にカーソルがきた時点で 3つ目、4つ目のValueプロパティをTrueに変更します。  □■■■□       ↑クリック(押し込んだまま) 左から4つ目のトグルボタン上でクリックを離します。 クリックされていない状態でマウスカーソルが上に乗っても Valueの変更は行われません。 ////////////////////////////////////////////////////////// MouseMoveを試してみましたが、ドラッグ中は処理が発生しないようなので どうしたものか困ってしまいました。 ご助力、よろしくお願い致します。

みんなの回答

  • mitarashi
  • ベストアンサー率59% (574/965)
回答No.3

#1-2ですが、ひょっとしてUserForm_MouseDownなら、OnTimeのお世話にならなくてもいけるのではと思ってトライしてみるとOKでした。おかげで動作も軽快になりました。ついでに物好きな御仁のために若干のバグフィックス(誤動作防止)もしてあります。 ☆標準モジュール Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) Public Declare Function GetActiveWindow Lib "user32" () As Long Public Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long Public Declare Function ScreenToClient Lib "user32" (ByVal hWnd As Long, lpPoint As POINTAPI) As Long Public Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Long Public Type POINTAPI X As Long Y As Long End Type Public Type RECT Left As Long Top As Long Right As Long Bottom As Long End Type Public Const VK_LBUTTON = &H1 '[LeftClick] Public Const VK_RBUTTON = &H2 '[RightClick] Sub test() UserForm1.Show End Sub ☆UserForm1モジュール Dim hWnd As Long Private myToggle() As MSForms.ToggleButton Private myRect() As RECT Private initialColor As Long 'UserFormのハンドル取得。 ScreenToClient APIで使用。 Private Sub UserForm_Activate() hWnd = GetActiveWindow() End Sub Private Sub UserForm_Initialize() Dim myControl As Control Dim scaleFactor As Single scaleFactor = 96 / 72 '配列の添え字0の要素は使わない ReDim myToggle(0 To 0) ReDim myRect(0 To 0) For Each myControl In Me.Controls If TypeName(myControl) = "ToggleButton" Then myControl.Enabled = False myControl.Caption = "" ReDim Preserve myToggle(0 To UBound(myToggle) + 1) ReDim Preserve myRect(0 To UBound(myRect) + 1) Set myToggle(UBound(myToggle)) = myControl With myRect(UBound(myRect)) .Left = CLng(myControl.Left * scaleFactor) .Top = CLng(myControl.Top * scaleFactor) .Right = CLng((myControl.Left + myControl.Width) * scaleFactor) .Bottom = CLng((myControl.Top + myControl.Height) * scaleFactor) End With End If Next initialColor = myToggle(1).BackColor End Sub 'Private Sub UserForm_Click()だと、ボタンを離さないとEventが発生しない Private Sub UserForm_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single) Dim tempToggle As MSForms.ToggleButton Dim initialState As Boolean Dim toggleNo As Long, currentToggleNo As Long '最初のクリック箇所を保持 If Button <> VK_LBUTTON Then Exit Sub currentToggleNo = getToggleNo() With myToggle(currentToggleNo) initialState = .Value .Value = Not (initialState) .BackColor = IIf(initialState, initialColor, vbBlue) End With '無限ループでマウスのモニタ Do DoEvents: DoEvents: DoEvents Sleep 10 toggleNo = getToggleNo If toggleNo <> 0 Then Set tempToggle = myToggle(toggleNo) With tempToggle .Value = Not (initialState) .BackColor = IIf(initialState, initialColor, vbBlue) End With Set tempToggle = Nothing End If 'マウスの左ボタンを離すまでループ Loop While GetAsyncKeyState(VK_LBUTTON) End Sub 'マウスの存在する位置のトグルボックスのNoを取得。取得失敗は0を戻す。 Private Function getToggleNo() As Long Dim pos As POINTAPI Dim ret As Long Dim i As Long 'Screen座標→Client座標に変換。RECT配列内の値はUserForm座標→Client座標に変換済み。 GetCursorPos pos ret = ScreenToClient(hWnd, pos) For i = 1 To UBound(myRect) With myRect(i) If (pos.X >= .Left) And (pos.X <= .Right) And (pos.Y >= .Top) And (pos.Y <= .Bottom) Then getToggleNo = i Exit Function End If End With Next i getToggleNo = 0 End Function

全文を見る
すると、全ての回答が全文表示されます。
  • mitarashi
  • ベストアンサー率59% (574/965)
回答No.2

#1です。 だいぶ苦労の果てに動いたので、思わず投稿してしまいましたが、本来デバッグを容易にするために動的に配置したトグルボタンを対象にしていたのでした。既設トグルボタンを対象とする様に改造しました。一部のプロシージャの置き換え程度で可能です。なお、マウスの動かし初めで漏らしてしまう現象は、最初のトグルボタンの状態が変わるまで一呼吸待つと良い事がわかりました。ご参考まで。 変更部分のみ記載します。 ☆ 標準モジュール toggleControlModule '構造体追加 Public Type RECT Left As Long Top As Long Right As Long Bottom As Long End Type ☆ UserForm1モジュール '変数の追加 Private myRect() As RECT Private initialColor As Long '関数の置き換え Private Sub UserForm_Initialize() Dim myControl As Control Dim scaleFactor As Single scaleFactor = 96 / 72 '配列の添え字0の要素は宣言しているだけで使っておりません '都度添え字の最大を求めるのは誉められないと思いますが... ReDim myToggle(0 To 0) ReDim myRect(0 To 0) '既設トグルボタンと、その座標を配列に取り込む For Each myControl In Me.Controls If TypeName(myControl) = "ToggleButton" Then myControl.Enabled = False myControl.Caption = "" ReDim Preserve myToggle(0 To UBound(myToggle) + 1) ReDim Preserve myRect(0 To UBound(myRect) + 1) Set myToggle(UBound(myToggle)) = myControl With myRect(UBound(myRect)) .Left = myControl.Left * scaleFactor .Top = myControl.Top * scaleFactor .Right = (myControl.Left + myControl.Width) * scaleFactor .Bottom = (myControl.Top + myControl.Height) * scaleFactor End With End If Next initialColor = myToggle(1).BackColor End Sub '少しは高速かと思い、座標の照合はRECT構造体と行う様にしてみました Private Function getToggleNo() As Long Dim pos As POINTAPI Dim ret As Long Dim i As Long GetCursorPos pos ret = ScreenToClient(hWnd, pos) For i = 1 To UBound(myRect) With myRect(i) If (pos.X >= .Left) And (pos.X <= .Right) And (pos.Y >= .Top) And (pos.Y <= .Bottom) Then getToggleNo = i Exit Function End If End With Next i getToggleNo = 0 End Function 'おまけ トグルボタンの色を変える Public Sub ontimesub()の中で、 .Value = Not (initialState) のところに、下記を追加する。 .BackColor = IIf(initialState, initialColor, vbBlue)

全文を見る
すると、全ての回答が全文表示されます。
  • mitarashi
  • ベストアンサー率59% (574/965)
回答No.1

もうご覧になっていないかもしれませんが、それらしい動作が実現出来たのでUpしておきます。 自分でも来週には分からなくなるかも知れないので珍しくコメントを沢山入れました... ToggleButtonのイベントを使うとうまくいかなかったので、DisableにしてUserFormのイベントで操作しています。 押し込んでない状態のボタンからスタートすると、通過したボタンを押し込み、押し込んだボタンからスタートすると逆の動作をします。特に動かし初めは、あまり速くマウスを動かすと漏らしますのでご注意下さい。 実用的ではないと存じますが、話の種にどうぞ。 ☆標準モジュール toggleControlModule Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) Public Declare Function GetActiveWindow Lib "user32" () As Long Public Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long Public Declare Function ScreenToClient Lib "user32" (ByVal hWnd As Long, lpPoint As POINTAPI) As Long Public Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Long Public Type POINTAPI X As Long Y As Long End Type Public Const VK_LBUTTON = &H1 '[LeftClick] Public Const VK_RBUTTON = &H2 '[RightClick] Private nextTriggerTime As Date Sub test() UserForm1.Show End Sub 'UserForm側でApplication.Ontimeが実行出来ないので仲立ちをする Public Sub setOnTime() nextTriggerTime = [now()+"00:00:00.50"] Application.OnTime nextTriggerTime, "onTimer" End Sub Public Sub onTimer() UserForm1.ontimesub End Sub ☆UserForm1モジュール toggleButtonは動的に設置するので、コントロールは配置無用 Dim hWnd As Long Private myToggle() As MSForms.ToggleButton Private xframe As Single, yframe As Single Private myStartToggle As Long Const BUTTONCOUNT As Long = 15 Const COLUMNCOUNT As Long = 5 Const SIDELENGTH As Long = 40 Private Sub UserForm_Initialize() Dim i As Long With Me xframe = .Width - .InsideWidth yframe = .Height - .InsideHeight End With ReDim myToggle(1 To BUTTONCOUNT) For i = 1 To BUTTONCOUNT Set myToggle(i) = Controls.Add("Forms.ToggleButton.1") With myToggle(i) 'UserFormのイベントを用いるためにtoggleButtonはDisableにする。値の変更は可能。 .Enabled = False .Width = SIDELENGTH .Height = SIDELENGTH .Left = SIDELENGTH * ((i - 1) Mod COLUMNCOUNT) .Top = SIDELENGTH * ((i - 1) \ COLUMNCOUNT) End With Next i With Me .Width = xframe + SIDELENGTH * COLUMNCOUNT .Height = yframe + SIDELENGTH * (BUTTONCOUNT \ COLUMNCOUNT) End With End Sub Private Sub UserForm_Activate() hWnd = GetActiveWindow() End Sub 'Private Sub UserForm_Click()だと、ボタンを離さないとEventが発生しないらしい Private Sub UserForm_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single) '最初のクリック箇所を保持 Me.startToggle = getToggleNo() 'イベントを直ぐに抜けるために、次の処理はApplication.OnTimeで起動する 'UserFormからは直には使えないので、標準モジュールに仲立ちをしてもらう toggleControlModule.setOnTime End Sub Public Sub ontimesub() Dim tempToggle As MSForms.ToggleButton Dim initialState As Boolean With myToggle(Me.startToggle) initialState = .Value '状態変更の前にEnabled=True,変更後にEnabled=Falseにする必要があると考えたが無くてもOKだった .Value = Not (initialState) End With Do DoEvents: DoEvents: DoEvents Sleep 10 Set tempToggle = myToggle(getToggleNo()) With tempToggle .Value = Not (initialState) End With Set tempToggle = Nothing 'マウスの左ボタンを離すまでループ Loop While GetAsyncKeyState(VK_LBUTTON) End Sub Private Function getToggleNo() As Long Dim pos As POINTAPI Dim ret As Long Dim toggleId As Long GetCursorPos pos ret = ScreenToClient(hWnd, pos) With pos 'UserForm座標系の値に戻す .X = .X * 72 / 96 .Y = .Y * 72 / 96 getToggleNo = (.X \ SIDELENGTH) + (.Y \ SIDELENGTH) * COLUMNCOUNT + 1 End With End Function Public Property Let startToggle(toggleNo As Long) myStartToggle = toggleNo End Property Public Property Get startToggle() As Long startToggle = myStartToggle End Property

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • VBAのトグルボタン

    初心者なもので、「なにをこんな」とお思いかもしれませんが、教えてください。 ユーザーフォーム内にトグルボタンを設置しています。当たり前なのですが、一度トグルボタンを押し再度ユーザーフォームを呼び出すとトグルボタンが押された状態のまま表示されます。 理想はトグルボタンを押した状態でユーザーフォームを閉じて、またユーザーフォームを呼び出しても押されていない状態で表示することです。 ネット等でも調べましたが、これというものがなかなかなかったので質問しました。よろしくお願いします。

  • トグルスイッチをVBAでコントロール

    マイクロソフトアクセス2000で作成しています。 メインフォームとサブフォームを使っています。 メインフォームにあるトグルスイッチを押した時に、サブフォームの最新レコードに移動したいと 考えています。 ボタンを押したときに、VBAの命令は動くのですがトグルスイッチが押した状態になりません。 VBAで押した状態にしようとしても命令言語がわかりません。 ご教授をお願い致します。 Me.トグルスイッチ1=true <- ここが動きません。 DoCmd.GoToControl "サブフォーム名" DoCmd.GoToRecord , , acNewRec

  • Excel トグルボタンについて教えて下さい

    今朝、コマンドボタンについて質問をして、 ボタンを押すごとに、特定のセルに1とブランク(=0)を 交互に入力する方法を教えてもらったのですが、その中で 新しい、トグルボタンという機能を教えてもらい、 本などを見て自分なりにやってみたのですが、 回答が見つからず、またまた書き込んでしまいました;; 先ほどは、ABS()の絶対値を使って、0と1を繰り返す方法と 下記のプログラムを教えて頂いたのですが、 Private Sub CommandButton1_Click() If Range("D1").Value <> "" Then   Range("D1").Value = "" Else    Range("D1").Value = 1 End If End Sub 作りたい書類には1を入力したときに凹ませたいのですが、 教えてもらった回答では どうしても、0の時に凹んで 1のときに凸るんです。 内容を色々変えてやってみたものの成功には至らず、 本を見ても回答に見合った例がなく、うまく出来ません。 っというか、コマンドボタンと同様の式では、 凹んだときにTrueを返して、凸ったときにFalseを返すという 折角のトグルボタンの意味が無いですよね? とどのつまりが、お知恵をお貸し頂きたいということで、 毎度ながら申し訳ないのですが 何卒宜しくお願いします。

  • ExcelのVBAで、順次動作の実現

    Excelのシートに、トグルボタンを3つ配置します。 ToggleButton1~3 そして、次のマクロを実行すると、2秒ごとに順番にトグルボタンが押されるのかと思いきや、6秒後に一斉にトグルボタンがへっこみます。 どうにか?2秒ごとに順番にトグルボタンが押されるように出来ないでしょうか? Sub test() Dim MyWait As String MyWait = 2 ToggleButton1.Value = True Application.ScreenUpdating = True DoEvents 'この間にもマクロを入れたい(0.1秒以内に処理できるものです) Application.Wait (Now + TimeValue("0:00:" & MyWait)) ToggleButton2.Value = True DoEvents Application.Wait (Now + TimeValue("0:00:" & MyWait)) ToggleButton3.Value = True Application.ScreenUpdating = True DoEvents End Sub

  • エクセルVBAのオプションボタンがうまくいきません 

    エクセルのVBAでフォームをつくり 3つのオプションボタンを配置し チェックした項目のとき、指定したセルに「レ」の印を書き込ませたいのですが、うまくいきません。 同じフォーム内のテキストボックスやコンボボックスの内容はうまくセルに書き込めるのですが・・・ オプションボタンのグループは設定してあります。 下のように記述したのですが、なぜ思うように動作しないか教えてください。 If オプション(3) = True Then ActiveCell.Value = "レ" ElseIf オプション(1) = True Then Range("H21").Value = "レ" ElseIf オプション(2) = True Then Range("H23").Value = "レ" End If

  • Access VBAでボタンの背景色を変更したい

    お世話になっております。 今回、Access2007で開発を行っております。 その中で、フォームに配置されているボタンがマウスプレスされた時に一時的にボタンの背景色を変更したいのですがそのようなプロパティが見つかりません。 方法はあるのでしょうか?

  • エクセルVBAオプションボタンの不具合

    複数あるオプションボタンを選択時、一つだけでは無く二つ選択された状態になる(見た目のみ) 二つ目以降を選択すると、前回に選択したボタンの部分に残像が残ってる状態です。 ・ユーザーフォームではなく、ワークシート上に直接オプションボタンを配置 ・選択したボタンにより、リストからデータを引っ張ってきて、指定したセルに格納する式を組み込み済 ・選択したオブジェクトのみValueプロパティが'True'になっている(残像が残っている部分は'False') ・同じグループに入っている 以上です。 要はValue値にはTrueは一つしか入らないので、動作上は問題は無いのですが見た目だけが残像が残り気になる、といった所です。 ボタン選択時に値を引っ張る式が無ければ残像も出なくなるのですが、式が入っていると現象が起こる様です・・・。 何か解決策はありますでしょうか?よろしくお願い致します。

  • VBA トグルボタンで楕円表示 非表示方法

    お世話になります VBAユーザーフォーム内でトグルボタンを生成しクリック時に セル指定及び座標表示、非表示をさせたいのですがわかる方よろしくお願いします。 又、同じフォーム内で複数使用します

  • ACCESS 帳票フォームとデータシートをトグルボタンで切り替え

    フォーム上でサブフォームの帳票フォームとデータシートをトグルボタンかコマンドボタンで切り替えることはできないでしょうか? 現在、抽出したデータをサブフォーム(帳票フォーム)に表示させており、そのデータにある金額の合計を表示させています。 帳票フォームは見やすいのですが、データシートですと抽出したデータの一部をコピーしEXCELで使用したりできます。見やすさを諦めてデータシートに変更すると抽出したデータの合計が計算できません。 できれば、帳票フォームとデータシートどちらも使用したいのですが、いちいちデザインからサブフォームのプロパティで規定のビューから変更するのが面倒です。 上記にありますようにトグルボタンかコマンドボタンで切り替えることができれば便利なのですが・・・。 良い方法がありましたらお知恵を貸して下さい

  • エクセルvbaでフレーム上のカレンダーコントロール

    エクセルvbaでユーザーフォームの中のフレーム上にカレンダーコントロール をデフォルト非表示で配置しています。ボタンをクリックするとカレンダを表示状態にして、 日付をクリックしたらその値をテキストボックスに表示してカレンダの 表示を消すというコードを作りました。以下がそのコードです。 Private Sub 表示ボタン_Click() cldCalendar.Visible = True cldCalendar.SetFocus End Sub Private Sub cldCalendar_Click() With cldCalendar テキストボックス1 = .Value テキストボックス1.SetFocus .Visible = False End With End Sub このコードだとカレンダーの日付をクリックしない限り、 ずっとカレンダは表示されたままになってしまいます。 カレンダからフォーカスが移った時非表示にしたいのですが、どうすればよいのでしょうか? また非表示にしたいのは、フォーム上の特定のオブジェクトをクリックしたときだけではなく、 フォーム上の何もない場所など、とにかくカレンダ以外のすべてにおいてクリックしたり、 フォーカスが移動したらカレンダを非表示にしたいのです。 注文が細かくて恐縮なのですが、どなたか詳しい方教えてください。

専門家に質問してみよう