初心レベルの者です。リスト型フォームにフィルター条件用のコンボボックスがあります。コンボはcb1、cb2・・・とします。各コンボの更新後イベントに下記の様なコード記述してます。
Dim strFilter1
strFilter1 = "取引先ID =" & Me.[cb1]
Me.Filter = strFilter1
Me.FilterOn = True
例えばコンボボックスが3ヶある場合、選択有無で組合せは下記になります。(○が選択ありで×が未選択)
cb1 cb2 cb3
○ × ×
○ ○ ×
○ ○ ○
× ○ ×
× ○ ○
× × ○
これに対応する為にベタな方法でcb1・cb2・cb3の各更新後イベントの条件記述を下記でしてます。
(例)変数strFilter1~3に抽出式を入れて
If Not IsNull(Me.[cb1]) And Not IsNull(Me.[cb2]) and Not IsNull(Me.[cb3])Then
Me.Filter = strFilter1 & " AND " & strFilter2 & " AND " & strFilter3
としてフィルター実行させています。If条件の組合せで各コンボに記述しています。条件コンボがもっと増えたりすると記述量も多くなり大変だなと感じてます(それも各コンボに)。
もっとスマートなアイデアがないかなと思い投稿しました。
通報 ...
プログラミングの基本ですが、共通の処理は関数にまとめるものです
なので、「共通になるような処理を考えてそれを呼び出すようにする」とスマートです
ACCESSのフィルター処理は結構テンプレート的な感じで、ここでも同じような回答を何度かしてますが、自作のプロシージャを作ってそれを呼び出すようにすると良いです
例えば、以下のように
setFilter
プロシージャを作成し、各コンボボックスの更新後処理でそれを呼び出すようにします各イベントが、プロシージャの呼び出ししかしないのであれば、そのプロシージャを関数にして、ユーザー定義関数の呼び出しの形にするとコードはもっとすっきりしますね
モジュール
各コントロールの更新後処理プロパティ
未選択(Null)の場合は条件なしということですね。
条件式変数に選択してあれば条件を追加していくという方法でいいでしょう。
フィールド名は適当、データ型は数値型の場合ですので、実際のものに合わせて修正してください。
具体的なフィルタ文字列生成部分に関しては、複数の組み合わせに対して、「直前の出力があった場合だけ
"AND"
を出力する」ということになりますが、これを毎回チェックするのはめんどくさいので、「出力があればとりあえず" AND "
を付ける」「最後に無駄な" AND "
を消す」ようにしますこれ入力してたらコード例をhatenaさんが投稿されたのでコードはそちらを参考に
このように、「似たような処理」を「共通になるような処理」に落とし込めればすっきりしたコード記述ができるようになります
hirotonさん haetnaさん ありがとうございました。
haetnaさんの回答にある Mid(strFilter, 6) は何かで見た事がありますがやっと意味がわかりました。”はカウントせずに半角スペースはカウントに入るのですね。因みにMe.FilterOn = (strFilter <> "")の右辺はTrueと同じ意味になるのでしょうか?
hirotonさんの回答にある Private Sub setFilter() 'フィルタをかける処理 ですが変数自体にコード記述が出来るのですね? フォーム上のイベントにはそれは出来ないのでVBA画面に直接記述したらいいのでしょうか?
知識不足で、すみません。
(strFilter <> "")
は、strFilter が ""(空文字列) でなければ True、""(空文字列) なら False になります。下記のコードと同義になります。
5行のコードが1行で済むのでついつい使ってしまいますが、コードの意味のつかみやすさからいけば、5行で書いた方がいいですね。
VBAに直接記述します。
Private Sub setFilter() は変数ではなくプロシージャです。
Subプロシージャ、Functionプロシージャについて調べてください。
#1 のhirotonさんの最初のコードの
'フィルタをかける処理
の部分を #2 の私のコードに置き換えればOKです。hatenaさん ありがとうございます。Subプロシージャ、Functionプロシージャの事は少し調べました。何となくしか分かってませんが実際に試そうと思いまして(別のコードで同じフォームに記述しているものがあるので)下記の様な内容のでプロシージャを作成しました。
Public Sub record_idou()
If IsNull(Me.[F1]) Then
MsgBox"F1未入力時はレコード移動できません"
Exit Sub
End If
End Sub
これをフォームのレコード移動ボタンに下記を記述しました。
Call record_idou
DoCmd.GoToRecord , , acPrevious
試すとF1未入力時・・・・のメッセージは出るのですがレコード移動はしてしまいます。Exit Subが効いてません。
何か記述不足しているのでしょうか?
Exit Sub
は「現在のSubプロシージャを終了する」命令ですは、イメージとしては
となり、
record_idou
プロシージャが終了して、それに続くDoCmd.GoToRecord , , acPrevious
が実行されますつまり、この例では意味のない
Exit Sub
ですね(続く処理がないのでどうせ何もせずプロシージャが終了する)理由はhirotonさんから回答済みです。
このような場合の対処法としては、SubではなくFunctionにして戻り値で次の処理を振り分けます。
ただし、F1フィールドを入力必須にしたいのなら、テーブルのデザインビューでF1フィールドのプロパティの「値要求」を「はい」(テキスト型の場合はさらに「空文字列の許可」を「いいえ」)にしておけば済みますので、実際に使う必要性はないですね。
hirotonさん haetnaさん ありがとうございました。浅はかでした。
hatenaさんの記述で試して上手くいきました。あと初歩的な質問ですみません参考にまでお聞きします(ルールが分かってないので)、
・Callで呼び出す必要はなかったのですね?
・Public Function record_idou() As BooleanのAsデータ型を()内に記述したものを見た事があるのですが、それはケースbyケースなのでしょうか?
VBAにはいろいろな記述省略のルールがあります。
Call
は省略しても良い記述ですねCall ステートメント
VBAの省略可能な記述について(エクセルの神髄さん)
プロシージャの呼び出しのための
Call
は上記リンク先エクセルの神髄さんでは省略しないほうがいいとも言っていますね。見慣れないモノや、わかりにくいモノは省略できても記述しておいたほうが無難かもしれません引数(ひきすう)と言います
Microsoft公式ドキュメントでも解説はありますが、さすがにわかりにくいと思うので別なサイトのリンクを貼っておきます
第107回.プロシージャーの引数(エクセルの神髄さん)
hirotonさん ありがとうございます。中々プロシージャは奥の深いものですね。頭がこんがりますが、少しづつ勉強していきます。
何度もすみません、最後に一つだけ質問があります。hatenaさんから最初に頂いた回答の下記を使いプロシージャを作成しました(setFilter1として)。
If Me.[cb1].Value <> "" Then
strFilter = strFilter & " AND フィールド1=" & Me.[cb1].Value
End If
***
****
strFilter = Mid(strFilter, 6) '先頭の" AND "を削除
Me.Filter = strFilter
***
そしてコンボ更新後イベントにCall setFilter1で実行しました⇒上手くいきました。(これはすごく便利です!)
質問はIf Me.[cb1].Value <> "" Thenの右辺です。ここを<>Nullにすると条件に引っかかりません。もう少し様子みるとこのフォームにはフィルター解除ボタンがあり Me.[cb1] = Null *** Me.FilterOn = False
で解除しており、その実行直後なら<>Nullでもいけますが、コンボ値を他の値に選択では<> ""でないと駄目です。
これで機能しているので問題ないのですが、それが不思議で。””とNullの違いはネットで確認しました。
この場合は<>””が妥当なのでしょうか?
NULL
は比較演算子による演算ができません詳しいことはかなり複雑なので、一通り情報に目を通してみてください
https://www.google.com/search?q=VBA NULL 比較
本来、
Null
の可能性がある場合の比較は、特別な処理・特別な判定をする必要がありますが、「VBAにおいてはNull
を比較演算に使うと全てFalse
として扱われる」という特徴があります。(コードの実装としてこれを活かすのは本来よろしくありません)つまり、
とすると、コンボボックスの値に関わらず常に
False
の処理が為されるため、意図しない動作をします逆に、
<>""
の判定においてコンボボックスの値がNull
の場合はFalse
を返して欲しいときにはFalse
だった場合の処理がされるので表面上は問題なく動作しているというわけです今回のような場合であれば、コンボボックスの値をチェックして
Null
比較をしないようにするのが正解で、事前にコンボボックスの値をチェックしてコンボボックスの値がNull
だったら""
とみなすようにします。たとえばNz関数を使ってNz 関数
蛇足
Null
なら空文字とみなしたいなら「"" & Me!cb1 <> ""
」くらいの手抜きで済ませちゃうんですけどね色々とありがとうございます。だからMe.FilterOn = (strFilter <> "")もいけたのですね。
厳密にしようとすると細かい事が一杯ありますね。全然知らないのと少しでも知っているのでは意図しない動作の時に違うなと思いました。これも奥の深いことですね。さらっと出来る方々がすごいです。