- ベストアンサー
Excel2000 VBAスピンボタンの修正方法
- Excel2000のVBAを使用してスピンボタンの修正方法について教えてください。
- 現在、スピンボタンの値が一つずつしか変わらないため、データシートのオートフィルターでフィルターをかけた時に、スピンボタンの値を飛ばすように修正したいです。
- 現物ファイルを投稿しましたので、No5643で確認していただけると幸いです。
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
こんにちは。#1、#2、cjです。 いや私説明下手ですから無理もないかと。 一応、フォローしておこうかと思いますが、Yahoo!の方で付いている回答は SpinButton1_Changeイベントを一切使わない前提で書かれていますから、 SpinButton1_Change をコメントブロックしてからでないと試せないと思います。 あちらと同じコンセプトで書いたものを私も用意してはあったのです。 今回の目的を果たすだけならば、あちらの方が簡単ですが 今後更に仕様変更していく時に、どうかな?と思うことなどありましたので そのまま#1のように_Changeイベントで提示しました。 でも、一度、試した上で、どちらの方法を採るかちゃんと選んだ方が良いと思います。 今回の目的の部分だけでいえば、私が提示したものの方がメンテナンス が少しだけ難しいと思います。 もしや、あちらのコード試せなくてお困りなのではないかと思ってのレスです。
その他の回答 (2)
- cj_mover
- ベストアンサー率76% (292/381)
こんにちは。#1、cjです。お礼欄へのレスです。 さっそく説明に入ります。 仮に、SpinButton1.Value が元々 30 だった時にSpinDownボタンを押したとします。 この段階で SpinButton1.Value は 29 に変わり、続いてPrivate Sub SpinButton1_Change()がCallされます。 Private Sub SpinButton1_Change() SpinUp なら1、SpinDown なら-1、増減どちらなのか調べておきます。 SpBtnPrev は前回このプロシージャが呼ばれた時の SpinButton1.Value つまり、この例では30 この段階の SpinButton1.Value は 29 ですから、29-30=-1。つまり SpinDown だと判ります。 nSgn = Sgn(SpinButton1.Value - SpBtnPrev) SpinButton1.Value を 監視用の変数 SpBtnPrev に確保しておきます。 これは専ら nSgn を調べる為に使う変数です。 SpBtnPrev = SpinButton1.Value ここまでが下ごしらえ。 まず、Sheets("data").FilterMode をチェックして With Sheets("data") If .FilterMode Then フィルターが掛かっている時は一旦 i = SpBtnPrev 29 に対応した行 つまり Sheets("data").Rows(33) が非表示であるか調べます。 非表示なら、続けて 28、27、26、とそれぞれ対応した行を調べます。 非表示ではない行にあたった処でLoopを抜けその結果が変数 i になります。 Do While .Rows(i + 4).Hidden i = i + nSgn Loop 29 に対応した行 が非表示でない場合は↑このループは一度も実行されませんので i = SpBtnPrev のままです。その場合は従来の処理に進みます。 And そして、SpinButton1.Value でいうと 1 から 29 対応した行 つまり Sheets("data").Rows("5:33") がすべて非表示であった場合に変数 i は タイトル行 Sheets("data").Rows(4) を意味する 0 になり、 SpinButton1.Value = 0 にはできない設定になっているせいでエラーになるので この場合を条件分岐で除外します If i <> SpBtnPrev And i > 0 Then ここまでの処理で得られた変数 i の値 つまり "飛ばす"先に当たる数値を SpinButton1.Value に設定します。 SpinButton1.Value = i この↑処理によって、SpinButton1 の値が変更されるのに伴い、 Private Sub SpinButton1_Change() がCallされます。 "ここ"のプロシージャの処理は一旦保留になり、"ここ"での処理とは別に 新たに呼び出された(再帰的に呼び出された) Private Sub SpinButton1_Change() が処理されます。 ~そちら~↑では "SpinButton1.Value に対応した行が非表示ではない前提"で処理が始まるので まっさきに、従来の処理を済ませます。 再び、"ここ"のプロシージャでの処理に戻って来るのですが、 処理するべきことは先ほどCallしたプロシージャの方で済んでいて 他にやることもないので、プロシージャを抜けて終わりにします。 Exit Sub End If End If End With フィルターが掛かっていなければ、そのまま従来の処理をします。 TextBox1.Value = SpinButton1.Value Call hyouji End Sub /// なお、無限ループについては Private Sub SpinButton1_Change() ' ・ ' ・ ' ・ SpinButton1.Value = i ' ・ ' ・ ' ・ End Sub この↑ような記述に関しては きちんと Exit Sub するように注意した方がいいということです。 説明解りにくいようでしたら、また遠慮なく訊いてください。
お礼
ご無理言いましたが、にもかかわらずご親切丁寧な回答をありがとうございました。 理解するのに、時間がかかりましたが、イメージすることが出来ました。 上級者の方々の深い世界には、頭が下がります。
- cj_mover
- ベストアンサー率76% (292/381)
全体を見通す余裕がないので、SpinButton1_Changeから派生する処理しかチェックしていませんが 一応、お望みの"スピンボタンのNOを飛ばす"ことは出来ていると思います。 dataシートで表示されているレコードだけをフォームに表示するという理解であってますよね? dataシートで非表示のレコードをフォームに表示しない為にSpinButton1のValueを"飛ばす"と。 SpinButton1.Valueを監視する変数 SpBtnPrev これの宣言部は必ずUserFormモジュールの先頭に。 nSgn は SpinButton1 が増加したか減少したかを[ 1 | -1 ]で捉えます。 dataシート にフィルターが掛けられていれば、 SpinButton1.Value に対応した行が非表示である限り i は表示されているセルを nSgn 方向に探しに行きます。 当初のSpinButton1.Value に対応した行が非表示であった場合で、"飛ばす"候補となるインデックスが ゼロでない場合はSpinButton1.Valueを更新します。 SpinButton1.Valueを更新すると、Private Sub SpinButton1_Change()処理の途中で 再帰的に(二重に)Private Sub SpinButton1_Change()が呼び出され、そちらで通常の処理が済まされます。 元のPrivate Sub SpinButton1_Change()に戻ってからは、何も処理せず Exit Sub します。 手を加える時は、無限ループに気を付けてください。 Private Sub UserForm_Initialize()の追加分は nSgn を減少に定義してエラーを回避する為です。 細かな動作確認は出来ていませんので、何か問題があれば、詳しく具体的に伝えて下さい。 余談になりますが、今回ご相談の件でアドバイスを真摯に考えると どうしても、SpinButton 以外のコントロールを使うことを奨めたくなります。 例えば、CommandButtonふたつであれば、増減を監視する必要もない訳で、、、。 将来的に、ご自分で設計する余裕ができた頃に、思い出して検討してもらえたら、と思います。 今回は現状を活かして比較的簡素なものを提案しています。 Private SpBtnPrev As Long ' モジュール先頭で!!宣言 Private Sub SpinButton1_Change() Dim nSgn As Long Dim i As Long nSgn = Sgn(SpinButton1.Value - SpBtnPrev) SpBtnPrev = SpinButton1.Value With Sheets("data") If .FilterMode Then i = SpBtnPrev Do While .Rows(i + 4).Hidden i = i + nSgn Loop If i <> SpBtnPrev And i > 0 Then SpinButton1.Value = i Exit Sub End If End If End With TextBox1.Value = SpinButton1.Value Call hyouji End Sub Private Sub UserForm_Initialize() Dim fRange As Range Dim fRow As Long fRow = Sheets("data").Range("D50000").End(xlUp).Row - 3 SpBtnPrev = 65536 ' ←追加◆ SpinButton1.Value = fRow SpinButton1.SetFocus End Sub
お礼
回答ありがとうございます。試したところ、うまくいきました。 解説もご丁寧にいただけたので、なんとか理解したいと思い、なんども読み返して理解しようとしているのですが、下記部分がどうしても理解出来なくて苦しんでいます。 もしご迷惑でなければ、もう少し詳しく解説いただけないでしょうか。 ご無理ばかり申し上げてすみません。 「当初のSpinButton1.Value に対応した行が非表示であった場合で、"飛ばす"候補となるインデックスが ゼロでない場合はSpinButton1.Valueを更新します。 SpinButton1.Valueを更新すると、Private Sub SpinButton1_Change()処理の途中で 再帰的に(二重に)Private Sub SpinButton1_Change()が呼び出され、そちらで通常の処理が済まされます。 元のPrivate Sub SpinButton1_Change()に戻ってからは、何も処理せず Exit Sub します。」
お礼
ご丁寧に補足頂きありがとうございます。 いろんな方の回答がお聞きしたくて、YAHOOでも投稿させていただきました。 cj_mover様にコメント頂けたとおり、どちらのプロシージャでも、上手く行きました。 私としては、2通りの対処方を学ぶことが出来たので、夢のようにありがたい話です。 cj_mover様のコードですと、現コードに簡単に修正できますが、ご指摘とおり、 やはり第3者、並びに私自身がコードを見たときに、理解するのに苦しいという事も あり、残念ながら、SpinButton1_Change の使用を見送りました。 しかし、せっかく教わったこのプロシージャの知識は、何らかの機会で必ず活かせる と確信しています。 色々とご親切、ご丁寧にコメント頂き、ありがとうございました。