投稿者 魔界の仮面弁士  (社会人) 投稿日時 2023/12/13 19:14:40
> 結局、ボタンにフォーカスがある状態で Enter や Space を押した場合、
ボタンにフォーカスが無い状態であっても、
Form の AcceptButton / CancelButton が設定されていた場合は
Enter や Esc で反応しますね。

さらにいえば、Button1.Text = "&Button1" などのようにニーモニックキーが設定されていた場合、
Alt+B のショートカット操作で、Button1 の Click イベントが発生します。


> 逆にこれをキャンセルする方法があれば知りたいです。
基本的にはデザイン時の Button の DialogResult 動作に任せるのが簡単ですが、
後から変更したい場合は、事前処理(PreviewKeyDown など)のタイミングではなく、
Click イベントなどで Me.DialogResult を調整するという手があります。

Button1 ではなく、「右上の "×" ボタンで閉じられた場合」の既定値を示したいなら、
FormClosing イベントなどで Me.DialogResult を調整するのも良いでしょう。


Me.Close にしても Me.DialogResult にしても、それを呼び出したからといって、
直ちにフォームが閉じられるわけではありません。実際に閉じられるのは、
設定した時の処理が End Sub に到達したのち、次のアイドル状態に遷移した後です。

そのため、PreviewKeyDown のタイミングで書き換えても、優先度の高い既存処理によって
Me.DialogResult の書き換えが発生してしまったのが今回の状況ですね。

標準で行われる本来の処理が邪魔になる場合は、
 案1) 本来の処理を抑制して、独自の処理に置き換える
 案2) 本来の処理をいったん行わせてしまい、後から追加の処理で補正する
などとします。

前者は、WndProc メソッドや Process何某 メソッドを上書きしたり、
CancelEventArgs や HandledEventArgs を継承した引数を持つイベントで処理するなど。
後者は  Click イベントや FormClosing で処理するなど。


> まぁ、Button1.DialogResult = DialogResult.OK でいいですか。
現行のコードからの修正量も少ないでしょうし、選択肢の一つになりえるでしょうね。
Me.DialogResult から Button1.DialogResult に変更した意図が後で分かりにくくならないよう、
自分なりの説明コメントを入れておいた方が良いかもしれません。

ただ、PreviewKeyDown は独自処理に置き換えることが想定されているイベントではないため、
個人的にはこのタイミングでの操作は避けるようにしています。
(今回の処理は大丈夫かもしれませんが、一応念のため)

たとえば PreviewKeyDown の段階では、判定結果をフィールド変数に保持するにとどめておき、
Click イベントや FormClosing など)で、Me.DialogResult に反映させるというのも手かと思います。
その場合は変更箇所が恐らく増えてしまうので、今回そこまで変更する必要があるかというと微妙なところですが…
単純化する前の元のソースを見ていないので、どう改修するのが良いのかは何とも言えません。



> Dim f As New Form1
> Dim f As New Form2
有効期間の短いローカル変数の話であれば、変数名の違いは大局に影響しないので、
分かりやすいか否かで決めてしまえば良いでしょう。同じでも違っていても構いません。

一方、有効期間の長い変数や後で使いまわされるインスタンスの場合は別です。
その場合、変数名が f や f1 と f2 と名付けられるべきではないでしょう。
たとえば「メール検索ダイアログ」なのか「新規注文登録画面」なのか
「パスワード入力画面」なのか「ハイスコア一覧画面」なのか
どのようなインスタンスであるのかが分かるような名前を付けるべきです。


> いつでも  f2.TextBox1.text  などの値が取れると云う事でしょうか?。
できます。が、その f2 をどこで宣言しているのか、そしてそのインスタンスの
生成や破棄をどこで行っているのかを、きちんと把握しておくことが肝要です。

その f2 変数が宣言されている箇所が、Module のフィールド変数なのか、
Form1 のフィールド変数なのか、Click イベントなどのローカル変数なのか、
あるいはメソッド引数で f2 As Form とか f2 As Form2 とか f2 As Object なのかなど。

いずれにせよ、そのインスタンスを複数の箇所で使いまわすのなら、通常はフィールド変数で管理します。
使いまわさずに一か所でしか使わないのであれば、ローカル変数、それもできれば局所変数にするのが良いでしょう(Using ブロックなど)。


そして同じインスタンスを使いまわす場合、New は何度も行う必要がありません。
閉じた後で ShowDialog すれば、先ほどの画面がそのまま再表示されるからです。
(New しなおす場合は、以前のインスタンスを Dispose することを忘れずに!)

ただし再表示できるのは、ShowDialog メソッドによる「モーダル ダイアログ」の場合です。
Show メソッドを使って「モードレス ダイアログ」として呼び出す場合はこの限りではありません。
Show の場合は Close 時点で破棄(Dispose)されるため、再利用には向きません。もしもモードレス時に
画面を使いまわすのであれば、Close ではなく Hide メソッドで隠す方法が使えます。(または Visible プロパティ)


で…使いまわすかどうかとは別の話として、「f2.TextBox1.Text 」という書き方はできれば避けましょう。
オブジェクト指向プログラミングの「カプセル化」の視点からみると、
f2.TextBox1 を直接操作するのは、本来は望ましくありません。

VB の場合、各コントロールがデザイン時に
 GenerateMember = True
 Modifiers = Friend
になっているために、外部からコントロールを直接読み書きできてしまいますけれどね…。
(C# の場合、Modifiers の既定値が private なので、自フォーム以外からは操作できない)

本来であれば、コントロールを読み書きするのは「そのフォーム自身」に任せるのが望ましいです。
そうしないと、そのダイアログの画面構成が変化したときに、ダイアログだけでなく、
それを呼び出す側のコードも併せて変更しなければならなくなってしまいますから。

たとえば「CheckBox1 がチェックされていた場合は、TextBox1 ではなく TextBox2 を使う」といった
追加改修が入ってきた場合、呼び出し元すべてにそのための If 文を追加して回るのは手間ですよね?

外部から操作させたいのであれば、ダイアログ側に Public なメソッドなりプロパティなりを追加して、
その中で Me.TextBox1 を読み書きするように設計します。そうすれば、呼び出し側の処理を変えることなく
ダイアログの実装を追加修正することができ、設計変更に強いコードになります。

もしも複数のフォームで統一的な操作にしたいのであれば、いわゆるポリモーフィズムの概念に従い、
操作用の Interface を自作して、それを各フォームに Implements するのが良いでしょう。
(継承フォームを使ってポリモーフィズムを実現する方法もありますが、設計難易度が高いのでおすすめしません)