先日も質問した者です。また分からない事がでてきました。
入力用フォームに選択用コンボボックスがいくつかあり、それにキー入力禁止をしたいと思いプロシージャを作成して各コンボボックスのKeyDownイベント時に呼び出そうとしましたが機能していません。(コンボに直接禁止コードを記載では機能しています)
【プロシージャ】
Private Sub proKey1()
'プロシージャの作成(名前:proKey1)
If KeyCode = 9 Or KeyCode = 17 Or KeyCode = 67 Then 'Tab・Ctrl・C キーのみ使える
Exit Sub
Else
KeyCode = 0 'Tab・Ctrl・C キー以外は禁止
End If
End Sub
【コンボのキークリック時】
Call proKey1 'プロシージャの実行・Tab・Ctrl・C キー以外は禁止
使い方が間違っていると思うのですが、それが分からなくて。
先ほど投稿したのですが1点間違いに気がつきました。プロシージャの Exit Subは他の箇所には無効となりますね。
そこを修正して試してみます。それで上手く行かないときはまた投稿します。
宜しくお願いします。
回答の前に、その「モジュール」はどのように作られたものですか?
(VBAとしてという意味ではなく)一般的には、そのプロシージャは動きません。構文エラーが発生します
https://www.google.com/search?q=option explicit
VBEのオプション設定のお勧めを徹底解説(hatena chipsさん)
KeyCode = 0 でキー入力をキャンセルしようとしているのだと思いますが、残念ながらそれでは動きません。
キークリック時の自動生成されるイベントプロシージャをみると、下記のようになっています。
引数に KeyCode というのがありますが、この引数に 0 を代入することでキー入力がなかったことになります。
別のプロシージャ内の変数 KeyCode とは何の関連もないので、別のプロシージャで KeyCode=0 としても意味はないです。
このような場合は、別プロシージャで、イベントプロシージャの KeyCode を参照渡しで渡せば、別のプロシージャ内でも、KeyCode にアクセスできるようになります。
フォームモジュール
引数には値渡し(ByVal)と参照渡し(ByRef)がありますが、その違いについてはWEB検索して研究してみてください。
hirotonさん:ありがとうございます。最初のコード記述(プロシージャ)は対象フォーム内のVBA画面の一番最後にしました。構文エラーはでなかったのですがCallで機能しませんでした。VBEお勧めもありがとうございます。じっくり読んで参考にします。
hatenaさん:ありがとうございます。そういう事だったのですね。戻り値のTrue、Falseをコード内に設定しても機能しないので再質問しようと思ってました。フォームモジュールとしての作成方法分からなかったので 挿入→クラスモジュールに下記を記述しました(クラスモジュールのClass1に作成されました)
If KeyCode = 9 Or KeyCode = 17 Or KeyCode = 67 Then 'Tab・Ctrl・C キーのみ使える
Exit Sub
Else
KeyCode = 0 'Tab・Ctrl・C キー以外は禁止
End If
そして
Private Sub コンボ_KeyDown(KeyCode As Integer, Shift As Integer)
Call proKey1(KeyCode)
End Sub
試しにキー入力すると”コンパイルエラー:Sub、Function、または Property が定義されていません。(Error 35)”
となりました。
⇒モジュールの作成が間違えていると思うのですが知識不足で解決出来てません。(すみません)
記述位置は
で合ってますよ。「フォームモジュール」がそこのことです
質問のコードは
KeyCode
が謎の存在です。VBA自体は変数宣言は必須ではありませんが、一般的にはOption Explicit
宣言により変数宣言を強制します。エラーが出ないということはOption Explicit
宣言がないということですが、基本的に使わないメリットよりも使うメリットが優る機能です。より深い知識を学ぶよりも先に、基本的にやって当たり前のことはできるようにしましょうhatenaさんの回答が答えですが、変数とは、名付けに従って思った通りに動くようなものではありません。
KeyCode
と記述したからと言って、思った通りのKeyCode
になるわけではないのです。思った通りに動くように、指示・命令・操作をしてあげなければなりません今回は、(コピペで解決するような)思ったような動作が組めたかったことよりも、謎の存在を生み出してしまったこと、また、それが謎の存在であると気づけなかったことのほうがよほど大きな問題でしょう
hirotonさん ありがとうございます。モジュール以前の事でした。変数宣言の重要性もわかりました。コピペだけで分かったつもりになってました。いつもプロパティからイベントでコード記述してたので今回も自動的にKeyCodeが変数になっている認識がありませんでした。Private Sub ***のあとの()内も大きな意味があるのですね。cancelなんかもそうなのですね。
hatenaさんの回答の意味もようやくわかりました(ByRefにしないとやりとり出来ない)。⇒コードを正しく記述したら今回の事は解決しました。
今回のプロシージャ作成も単純にコードを短縮文字に置き換えてるだけという認識しかありませんでした。もっともっと深いものでした。まだまだ浅いところしか理解できてないと思いますが、色々きずかせて頂きありがとうございました。
そうですね。モジュール(VBA記述画面)だけを見ていると理解できないんですが、各コントロールのプロパティで
[イベント プロシージャ]
を指定して自動生成できるプロシージャはその引数に特別な意味を持つ特別なプロシージャになりますこれらは、
コントロール名_イベント名([引数])
の書式になり(引数はイベントによりあったりなかったり)自動生成せずに、モジュールで直接記述(コピペ)してからコントロールに[イベント プロシージャ]
を設定しても同じです。データ上はただのテキストでしかありませんこのようなちょっと理解しにくい仕組みの上で、
コンボ_KeyDown
プロシージャの中でKeyCode
を操作すると、実際のACCESS上でのキー入力を制御できるという仕組みが成り立っています前の質問でも少し言葉が出ましたが、「スコープ」が問題になります
「このプロシージャで宣言されている
KeyCode
という引数を操作することにより、ほにゃららできる」というのが諸々の趣旨になりますが、引数はそのプロシージャの中でのみ有効ですhirotonの指摘の本質ですが、「
コンボ_KeyDown
プロシージャの中で記述したKeyCode
のみ」が、その時のキー操作の制御に影響します。その他の場所で記述されたKeyCode
は、それが有効となる宣言方法に従って動作しますやりたいことは「
コンボ_KeyDown
プロシージャの中KeyCode
の操作」なので、どんなに他の場所でKeyCode
の変数宣言を行っても「コンボ_KeyDown
プロシージャの中KeyCode
」を変更することはできません。これは、不意の動作が行われないように守られているとも言えますし、「KeyCode」という簡単な名前を至る所で使うことに問題がないようになっているとも言えます。これが、スコープ問題というやつですここまで理解した上でようやく、スコープが制限されている値を変更するためにはどうしたらいいか?スコープを跨ぐ方法は?となります。ここまで来れば、hatenaさんの回答につながる(回答を見て理解できる)でしょう
こんな回りくどい話になるつもりじゃなかったんですが・・・
エラーが出てないとなると、モジュールレベルで
KeyCode
を変数宣言してるとかなんですかねぇプロシージャを分割して、元のプロシージャの値を使いたいってだけなら引数で渡せばいいよで済んだんですが
Function
+戻り値にするかどうかは悩みどころよくわからないけどエラーも出ずに処理が終わるコードが出来上がってしまったほど怖いものはないですねぇ
まずは基本が理解できていないと、このスレッドの内容の理解は難しいかな。
私の説明もかなり端折ってますので。
すこしでも理解の助けになるように補足説明をしておきます。
まず、引数と何かを理解するようにしましょう。
一言では説明しきれないので「VBA 引数」でWEB検索して研究してみてください。
例えば、下記とか。
【Excel VBA入門】引数とは|エクセル マクロ | Gakushiki
引数がある程度理解出来たら、次はイベントプロシージャの引数ですが、
これは普通のプロシージャの引数とは少し違う働きをします。
例えば、キークリック時の場合は、
Private Sub コンボ_KeyDown(KeyCode As Integer, Shift As Integer)
の KeyCode As Integer, Shift As Integer の部分が引数になりますが、
KeyCode引数にはキーを押したときにどのキーを押したのかの情報が入っています。
Shift引数は、Shiftキー、Ctrlキー、Altキーが押されているかどうかの情報が入ってます。
さらに、この引数に値を代入することで、別のキーが押されたことに変更することもできます。
keyCode = vbKeySpace
とするとどのキーを押してもスペースキーが押されたことになります。そして、
keyCode = 0
とすると何もキーが押されなかったことになると決められています。
イベントにより引数があるものとないものがあります。これは決まっているのでユーザーが勝手に引数をいれることはできません。
他に、例えばフォームを開くときのイベントプロシージャなどのようにCancel引数があるものもあります。
このようなイベントは
Cancel = True
というようにTrueを代入するとイベント自体をキャンセル(なかったことにする)できます。
つまりフォームは開くことができないということになります。
このようにイベントプロシージャの引数は他のプロシージャの引数とはすこし役割が異なります。
次に、引数の値渡し(ByVal)と参照渡し(ByRef)の違いについて理解する必要が出てきます。
これもなかなか難解な概念なので、ここで説明するより「VBA ByVal ByRef」をキーワードに検索して、
いろいろなページで研究してください。
上のhirotonさんの投稿内で、下記のコードがありますが、
Private Sub proKey1(c As Integer)
これは、
Private Sub proKey1(ByRef c As Integer)
と同じコードになります。By〇〇 を省略したときは、ByRef になるというのがVBAの仕様なのです。
つまり、参照渡しをしていることになります。
参照渡しとは、簡単にいうと、変数そのものをそのまま渡しているということです。
いいかえると、一つの変数を別の場所から見たり、触ったりしているということです。
(これで伝わっているかな?)
つまり、下記の「これ」で指し示したものは、全て同じものです。
つまり、
proKey1内で、
c = 0
としたということは、
コンボ_KeyDown内で、
keyCode = 0
としたことと同じ意味になるということです。
なんか回りくどい説明になりましたが、少しは理解の助けになったでしょうか。
アレです。正直何も分からないままやってしまってもいいんじゃないかなって内容なんですよね。デフォルトがByRefだからとかそういうのも
VBAの使い始めなんかそのくらい適当でいいと思っているので
エラーが出ないのが不思議だと何回か指摘していますが、「なんでもかんでも
On Error~
していてうまくいっていないのにどこが原因か分からない」と同じ匂いを感じるんですよね。普通にやっていればそもそも動かないってこけているはずなのでhiroton自身も特別なとは言っていますが、イベントプロシージャの引数は別に他のプロシージャと比較して特別なモノでもないんじゃないですかね。それこそ「記述を省略した参照渡し」なので
大元の参照元(コントロールのイベント実行)から呼び出されるにあたって、「参照渡し」で引数を定義しているので、このプロシージャ内で操作した結果は元になるイベントの情報をそのまま書き換えている。と、それだけかと
大元の参照元が隠されているというだけで、プロシージャとしての動きは特別なモノでもないと思います
hirotonさん・hatenaさん 丁寧な解説ありがとうございます。大変参考になります。まだまださわりしか理解してませんが自分なりにイメージしました。今回の流れはおおざっぱには下記でしょうか?(間違いならすみません)
1:コンボキーダウン時に(プロシージャ)proKey1を呼び出し押されたキー内容を(変数)KeyCodeに入れてproKey1に引数として渡す
2:proKey1はKeyCodeを受取りそれは共有できている引数Cとして条件分岐をする
3:2の結果をコンボキーダウン時に返す。
hirotonさんのproKey1の引数CとしてKeyCodeと共有させているのはスコープ(範囲)対策でKeyCodeという変数を使わない様にするためでしょうか?(この解釈も間違いならすみません)
ACCESSの便利さが好きで自分で色々制御できるといいなと思い勉強中です。少しづつの理解しか出来ませんが頂いた回答はネットや本には載ってないような達人の生の意見は本当に貴重に思ってます。
プロシージャ内で宣言された変数(引数も含む)は、そのプロシージャ内でのみ有効、つまり他からは参照できないです。
スコープは Sub から End Sub までの範囲です。
つまり、コンボ_KeyDown内で宣言された変数(引数も含む)と、proKey1内で宣言された変数(引数も含む)ではスコープが異なるので、同じ変数名でも問題ないです。もちろん違っても問題ないです。
同じ名前を使うか、異なる名前にするかは、プログラマーの好みだと思います。
私は、参照渡しの場合は、同じものを共有することになるので、同じ名前にしておくことが多いです。
質問内容の経緯から、適切な宣言と、それに従った運用が必要であると示すためです
以下、要点を挙げてみると
(参照渡しの)引数によって、スコープを跨いで同じデータを扱うこと。
プロシージャを作ると、新たなスコープが作られること。
異なるスコープでは、そのスコープで使える名前を正しく把握する必要があること。
proKey1
で名前だけ同じKeyCode
を記述しても、それはコンボ_KeyDown
のKeyCode
ではないということですね。(そうなるよう記述しなければ)proKey1
はスコープが異なるのでKeyCode
という「名前」はproKey1
の中で自由に(新たに)使えます。解説目的(質問への回答目的ではない)でc
と表記していますが、普通に組んだらPrivate Sub proKey1(KeyCode As Integer)
とする(そうなるよう記述する)と思いますhirotonさん・hatenaさん ありがとうございました。納得しました。参照渡しでしておいたら同じ名前でも違う名前でも共有できるのですね。スコープとは関係のない事でしたね。
こつこつ勉強していきます。