質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.62%

登録されているイベントを実行したい

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 3,117

tec

score 9

前提・実現したいこと

TextBox の Validating イベントを直接呼び出したいのですが、
VBでのRaiseEventのようなことは出来ないでしょうか?

Invoke(textbox1.KeyDown) のように、登録されているだろうイベントを直接呼び出す方法を探しているのですが、
検索してもDelegateに登録して実行する等の結果ばかりでした。

該当のソースコード

public class CustomTextBox : TextBox {
  protected override void OnValidating(CancelEventArgs e) {
     // チェック処理
     if (チェックエラー) {
       MessageBox.Show("チェックエラーです");
       this.Focus();  // <- ここでチェック中以外のCustomTextBoxに元々フォーカスがあった場合、
                      // Validating 処理が走って、またチェックエラーメッセージを表示してしまうので、これを回避したい
       e.Cancel = true;
     }
  }
}

//  
// CustomTextBox を沢山配置したフォーム
//
public class Form1 {

  // 
  // このボタンクリックはForm上のどのコントロールにフォーカスが当たっていても
  // ショートカットキーで呼び出すことが可能
  // 
  private void button1_Click(object sender, EventArgs e)
  {
    ※現在フォーカスのあるCustomTextBox.Validateを実行; // これを実行して、ActiveControlのチェックを行いたい
    if (上記※の結果、e.Cancel == true) {
      処理終了;
    }

    if (フォーム上のCustomTextBoxのチェック処理 == 失敗) {
      処理終了;
    }
  }
}

試したこと

リフレクションで OnValidating を呼んでみましたが、実行された気配がありませんでした。

object[] args = { new CancelEventArgs() };
// OnValidating内でブレークポイントを張っていたが、止まらなかった。(呼ばれていない?)
ActiveControl.GetType().InvokeMember("OnValidating", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, ActiveControl, args);

var cea = args[0] as CancelEventArgs;
if (cea.Cancel) // Cancel の中身は falseだった。
{
    return;
}

補足情報(FW/ツールのバージョンなど)

VisualStudio 2015
Windowsフォーム アプリケーション
.NET Framework 4.6.2


追記

button1_click() は、フォーム上のどのコントロールにフォーカスが当たっていても、
ショートカットキーで直接呼び出すことが可能です。
そのため以下の順で処理が走ります。

  1. CustomTextBox上でショートカットキーを押す(KeyDown)
  2. button1_click() の処理が走る
  3. フォーム上のCustomTextBoxのチェック処理が走る
  4. CustomTextBoxのチェックが走って、「チェックエラーです」の後にフォーカスがCustomTextBoxに移動する。
  5. 4の時にCustomTextBoxのLeaveイベントが起動し、OnValidatingが呼ばれる

5.のLeaveイベントを呼ばれる前に、CustomTextBoxのOnValidatingを呼んで、
フォーム上のCustomTextBoxのチェック処理まで到達できないようにしたいのです。

追記2

Zuishinさんの指摘を受けて最終的にチェック処理が実装されていたら呼ぶようにしました。

public interface IValidator {
  // 不正値かどうかの判定
  // 不正値だった場合はメッセージを表示させて、コントロールにフォーカスを強制的に移動させる
  bool ValidateInvalidValue();
}

public class CustomTextBox : TextBox, IValidator {
  protected override void OnValidating(CancelEventArgs e) {
     if (ValidateInvalidValue()) return;

     e.Cancel = true;
  }

  public bool ValidateInvalidValue() {
     // チェック処理
     if (チェックエラー) {
       MessageBox.Show("チェックエラーです");
       this.Focus();
       return false;
     }
     return true;
  }
}

//  
// CustomTextBox を沢山配置したフォーム
//
public class Form1 {

  // 
  // このボタンクリックはForm上のどのコントロールにフォーカスが当たっていても
  // ショートカットキーで呼び出すことが可能
  // 
  private void button1_Click(object sender, EventArgs e)
  {
    var controls = 画面上のチェック対象コントロール全て;
    if (CheckAllControls(controls)) {
      return;
    }
  }

  private bool CheckAllControls(List<Control> controls) {
    // フォーカスの当たっているコントロールに不正な値が入っていないか確認
    var validator = ActiveControl as IValidator;
    if (validator != null) {
      if(! validator.ValidateInvalidValue()) {
        return false;
      }
    }

    // 全体のチェック
  }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • papinianus

    2019/06/13 19:29

    this.Focus()ってなんの意味でやってるのか、どなたか教えて下さい

    キャンセル

回答 3

checkベストアンサー

+6

イベントは能動的に呼び出すものではありません。イベントから呼び出して実際の仕事を行うメソッドを作り、それを複数箇所から呼び出してください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/14 09:31

    javascript の trigger('イベント名')みたいなのが欲しかったのですが、
    c#では出来ないものなんですね。

    ActiveControlが何か判断がつかないので、
    OnValidating() を呼ぶための CallValidating() を持ったインターフェースを実装して、
    間接的に呼ぶようにしてみます。

    キャンセル

  • 2019/06/14 09:39

    そうではなく、OnValidating の中身を丸々別の public なメソッドに移し、OnValidating からそれを呼んでください。処理は正しい文脈で呼ばなければ保守が大変になります。

    キャンセル

  • 2019/06/14 09:55

    「登録されているイベントを個別に実行するインターフェース」よりも、
    「入力チェックするインターフェース」の方が良いのでしょうか?

    たしかに「イベント」を呼びたいわけではなく「イベント内の処理」を呼びたいので、
    教えて頂いたやり方の方が良い気がしてきます

    キャンセル

  • 2019/06/14 10:00

    そうです。イベントを起こすのは UI の役目なので、コードで発生させるのは他にどうしようもない時の最後の手段です。またその方がテストも作りやすくなります。

    キャンセル

+2

上記のコードであるなら、CustomTextBox から button1 にフォーカスが移動した時点で CustomTextBox.Validating が呼ばれます。また、「現在フォーカスのあるCustomTextBox.Validateを実行」と書かれていますが、Button.Click が呼ばれた時点でフォーカスは移動しています。どうやって「現在(というか、直前に)フォーカスのある CustomTextBox」を取得しますか。
いろいろなイベントをとりあえ全てハンドリングして、デバッグでイベントの名前を出力することで、どのようなときに、どのような順序でイベントが発生するか、調べるといいと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/14 09:18

    申し訳ありません、前提条件が抜けていましたので追記しました。
    button1_click()はForm上のどこからでも直接呼び出せる実装なので、
    実際にはOnValidatingより先にbutton1_click()が呼ばれることになります。

    キャンセル

  • 2019/06/14 11:13

    C#は object oriented language です。objectは「目的」、orientedは「深く考える」です。
    それぞれのメソッドの目的は何ですか。その目的に合致した内容になっていますか。

    キャンセル

+2

イベントの概念についての理解がおかしい気がします。

イベントというのは何らかの事象が発生したことをそのコンポーネント(クラス)の「外部」に通知するためのものですので、今回の例で言えばCustomTextBoxのイベントをForm上から発火しようとしているのがまずイベントの使い方として間違っています。

Form上で実装するのはそのイベントの通知を受けて実行する処理(イベントハンドラ)です。

実際のチェックはそのイベントハンドラ内で「チェック処理」というメソッドを呼び出すようにしてやればいいので、今回のケースだとCustomTextBoxでOnValidatingをオーバーライドしているのもちょっと意図がよくわかりません。

VBのRaiseEventを例として挙げられていますが、それもイベントを発火するクラス(今回の例で言えばCustomTextBox)の「内部」で使うものであり、「外部」から使うものではありません。(つまりC#だからできない、というわけではありません)

リフレクションを使う方法も、仮にそれで上手くいったとしても論外です。

入力値検証をValidatingイベントを使ってやる場合、そのタイミングのコントロールはForm.AutoValidateプロパティやControl.CausesValidationでタイミングを制御するものですので、そこら辺の使い方を調べられると良いと思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/14 11:55

    「イベント」は「呼ばれるもの」であり「呼ぶもの」ではないということですよね。
    その辺りの感覚がまだ理解できていないと思います。

    >今回のケースだとCustomTextBoxでOnValidatingをオーバーライドしているのもちょっと意図がよくわかりません。
    CustomTextBoxはどの画面でも共通で利用するコントロールとして使っています。
    その際に決まった入力値エラー(例えばありえない日付を入力した等)の場合の処理をOnValidating内で実装しています。
    その他のチェック処理(必須だとか、大小だとか、画面によるもの)に関しては、全体のチェック処理の中で行っており、
    そこにOnValidatingで行っているチェック処理を入れるというのは不要だと思っていました。
    全体チェック時に不正値を検出すれば良いという方針も有りですし、
    不正値の場合は入力が無かったことにするというのも有りだとは思いますが、
    システムの方針上その場でユーザに再入力させるものとして、今回のような実装にしてます。

    >Form.AutoValidateプロパティやControl.CausesValidationでタイミングを制御するものですので、そこら辺の使い方を調べられると良いと思います。
    全体チェックの直前に「Form.AutoValidate = AutoValidate.Disable」とした場合、
    不正な入力値が残ったまま、その他のコントロールにフォーカスが当たってしまうため、
    採用できませんでした。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.62%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る