none
GotFocus, Enter, LostFocus, Leave, Validating, Validatedを動作させたくない RRS feed

  • 質問

  • C# 2017
    .NET Framework 4.5
    WinForms

    TextBoxを1つ、ComboBoxを1つ用意しており、予めComboBoxのプルダウンが開かれている状態を想定しています。
    いずれも継承したクラスで用意しています。(TextBoxEx, ComboBoxEx)

    TextBoxExにフォーカスがある状態でプルダウンの中身をクリックすると、TextBoxExのLostFocus, Leave, Validating, Validatedが動作します。
    プルダウンの中身をクリックしたタイミングでComboBoxExにフォーカスが移ってしまうため、TextBoxExにフォーカスを設定するようにしているのですが、そのタイミングで、当たり前ですがTextBoxExのGotFocus, Enterが動作します。

    ComboBoxEx側の動作上、TextBoxExからフォーカスが離れること自体は別に構わないのですが、TextBoxExのオーバーライドした対象のメソッドや対象のイベントが走行してほしくありません。
    見かけ上は、ComboBoxExは、TextBoxExの一部のコントロールである、という見せ方をしたいためです。

    TextBoxExとComboBoxExの組合せは常に対になるとは限らない作りになっていますが、ComboBoxExでは、対になるTextBoxExをControlオブジェクトとして把握しています。
    (よって、ComboBoxExの対になるオブジェクトが常にTextBoxExであるとは限りません)

    Application.AddMessageFilter()でTextBoxExのWM_GOTFOCUS, WM_KILLFOCUSで制御しようかと思いましたが、どうも制御できないようでした。
    仮に出来たとしても、TextBoxEx側でキャレットが壊れそうかなとも思っている状態です。

    上記状況にある時、TextBoxEx側ではなく、ComboBoxEx側の実装で、TextBoxExの表題の各動作を制御し、走行させないようにする方法はないでしょうか?
    やはり、どうしてもTextBoxEx側での制御が必要でしょうか?
    継承していないコントロールにもComboBoxExを紐づける形を想定しているので、ComboBoxExに紐づけられるコントロール側では制御したくないのです。


    • 編集済み takiru 2018年12月13日 7:14
    2018年12月13日 7:14

回答

  • NativeWindow を継承したクラスを使ってメッセージを無視させることは可能ですが、フォーカス関連に関して無視すると、いろんなところで破綻するのでやめたほうがいいです。

    コントロールにメッセージを処理させたうえで、イベントを発生しないようするのが最善かと思います。

    ではイベントは外部から抑止できるかとういうと、できなさそうです。

    継承したコントロールで

    	protected override bool CanRaiseEvents {
    	    get {
    	        return !SupressEvents;
    	    }
    	}
    	public virtual bool SupressEvents { get; set; } = false;
    
    とするくらいでしょう。

    SupressEvents = true のとき、Events リストで管理されたイベントは発生しなくなります。

    • 回答としてマーク takiru 2019年5月22日 5:23
    2018年12月13日 11:49

すべての返信

  • 状況がよくわかりませんが、TextBox と ComboBox を併せて1つのコントロールとして扱いたいなら、それぞれを継承してコントロールを2つ作るのではなく、ユーザーコントロールに TextBox と comboBox を置き、1つとして扱えば良いのではないでしょうか。それぞれの動作を制御するためのフラグを設置することになると思いますけど。

    なんにしても、私はあなたではありませんので、あなたが考えていることを見通すことはできません。書いてないことを想像で小河なうことも難しいです。第三者に状況が理解できるように、詳細に説明してください。

    何を、どのように設定してある。どこで、どんなアクションを起こすと、どうなって欲しい。
    このように設定した場合は、同じアクションで、このようになって欲しい。
    こういったことを、「文章」ではなく、短文で。


    Jitta@わんくま同盟

    2018年12月13日 11:41
  • NativeWindow を継承したクラスを使ってメッセージを無視させることは可能ですが、フォーカス関連に関して無視すると、いろんなところで破綻するのでやめたほうがいいです。

    コントロールにメッセージを処理させたうえで、イベントを発生しないようするのが最善かと思います。

    ではイベントは外部から抑止できるかとういうと、できなさそうです。

    継承したコントロールで

    	protected override bool CanRaiseEvents {
    	    get {
    	        return !SupressEvents;
    	    }
    	}
    	public virtual bool SupressEvents { get; set; } = false;
    
    とするくらいでしょう。

    SupressEvents = true のとき、Events リストで管理されたイベントは発生しなくなります。

    • 回答としてマーク takiru 2019年5月22日 5:23
    2018年12月13日 11:49
  • やはり難しいですか。 ComboBoxはComponentクラスを継承したクラスでラッピングして、ただのTextBoxなどでも利用したいと思っていましたが、必ず継承が必要になったり、真っ当に動作させるためのコードを都度自前で書かなければならないとなるとあまり有用ではなさそうですね。
    • 編集済み takiru 2018年12月13日 12:51
    2018年12月13日 12:50
  • 箇条書きでは以下のようになります。

    ・ComboBoxはComponentクラスを継承したコンポーネント内で有しており、当該クラスを介して、紐づけを行うコントロールを決定する。
     利用者は当該コンポーネントをデザイナ上から利用して、コントロールの紐づけを行う。
    ・ComboBoxの組合せはTextBoxとは限らない。よってユーザーコントロールでは実現できない。
    ・ComboBoxのプルダウンの候補をクリックしたタイミングでTextBox側の表題メソッド、イベントを走行させたくない。
     その制御をTextBox側で行いたくない。
    ・紐づけを行うコントロールごとに、利用者側で表題メソッド、イベントを走行させないための実装を必要としたくない。

    上記が実際行いたいことですが、端的に求めている部分は

    ・ComboBoxのプルダウンの候補をクリックしたタイミングでTextBox側の表題メソッド、イベントを走行させたくない。
     その制御をTextBox側で行いたくない。
    ・紐づけを行うコントロールごとに、利用者側で表題メソッド、イベントを走行させないための実装を必要としたくない。

    になります。

    2018年12月13日 23:59
  • ComboBoxの継承は行っていませんが、最小限のことをシンプルに書くとこんなことです。

    using System;
    using System.ComponentModel;
    using System.Windows.Forms;
    
    namespace TestApp
    {
        class ComponentEx : Component
        {
            private ComboBox comboBox = new ComboBox();
            public Control TargetControl { get; set; }
    
            public ComponentEx()
            {
                this.comboBox.Items.Add("aaa");
                this.comboBox.Items.Add("bbb");
                this.comboBox.Items.Add("ccc");
    
                this.comboBox.SelectedValueChanged += ComboBox_SelectedValueChanged;
            }
    
            public void Open()
            {
                this.comboBox.Location = this.TargetControl.Location;
                this.comboBox.Size = this.TargetControl.Size;
                this.comboBox.Top = this.TargetControl.Top + this.TargetControl.Height;
                this.TargetControl.Parent.Controls.Add(this.comboBox);
                
                this.comboBox.DroppedDown = true;
            }
    
            private void ComboBox_SelectedValueChanged(object sender, EventArgs e)
            {
                this.TargetControl.Focus();
            }
        }
    }
    

    using System;
    using System.ComponentModel;
    using System.Windows.Forms;
    
    namespace TestApp
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
    
                textBox1.GotFocus += TextBox1_GotFocus;
                textBox1.LostFocus += TextBox1_LostFocus;
            }
    
            private void textBox1_TextChanged(object sender, EventArgs e)
            {
                this.componentEx1.Open();
            }
            private void TextBox1_GotFocus(object sender, EventArgs e)
            {
                Console.WriteLine("GotFocus 動作してほしくない");
            }
    
            private void textBox1_Enter(object sender, EventArgs e)
            {
                Console.WriteLine("Enter 動作してほしくない");
            }
    
            private void TextBox1_LostFocus(object sender, EventArgs e)
            {
                Console.WriteLine("LostFocus 動作してほしくない");
            }
    
            private void textBox1_Leave(object sender, EventArgs e)
            {
                Console.WriteLine("Leave 動作してほしくない");
            }
    
            private void textBox1_Validating(object sender, CancelEventArgs e)
            {
                Console.WriteLine("Validating 動作してほしくない");
            }
    
            private void textBox1_Validated(object sender, EventArgs e)
            {
                Console.WriteLine("Validated 動作してほしくない");
            }
        }
    }
    


    2018年12月14日 0:32
  • ComboBoxの目的はなんですか?
    この例では、TextBoxのAutoCompleteナントカでできそうですが?

    Jitta@わんくま同盟

    2018年12月14日 0:45
  • 自前のDataSource、DataBindingsからオブジェクトを利用して表記ゆらぎや部分一致検索を可能にするためのものです。
    実際の具体的な実装は大量すぎて記載できません。

    2018年12月14日 1:19
  • その独自の候補リストをTextBox.AutoCompleteCustomSourceプロパティで提供すればいいのでは?
    2018年12月14日 1:51
  • 候補は文字ではなくオブジェクトになります。

    私が今作っているのは、以下がすべて満たされなければなりません。
    そのために、すべての候補に対して、絞り込まれた候補をComboBoxのDataSourceとして利用し、画面上に表示する方式を取っています。
    そこでComboBoxを採用したのは、それ以外での実装を思いつくことができず、いくつかの動作を踏まえて、ComboBoxを採用すること最適だと判断したためです。

    ・すべての候補は、最低でもDataSet, DataTable, IList, IList<T>で指定できること。
    ・すべての候補をDataSourceとして指定できること。
    ・DataSourceをDataBindingsで設定できること。
    ・DisplayMember, ValueMemberの指定ができること。
    ・候補のドロップダウンを、任意のキー操作で開閉できること。
    ・候補のドロップダウンを、値入力されたタイミングでも開閉できること。
    ・候補のドロップダウンは、スクロールせず表示されるアイテム数を指定できること。
    ・候補はCompareOptionsで表記の揺らぎを制御できること。
    ・候補は前方一致、後方一致、部分一致、完全一致を制御できること。
    ・候補のドロップダウンは、テキストに対して表記の揺らぎ、一致条件に合致するもののみが表示されてくること。
    ・候補は、文字の直接入力または選択によって決定された時点で、イベント発火により、
     対象のオブジェクトをSelectedItem, SelectedValue, SelectedIndexとして得ることができ、
     任意の操作を行えること。
    ・TextBoxに関連づく場合、TextBoxで標準的に動作するイベントの発火順序は踏襲されること。



    それらをすべて実装した上で、表題の問題にひっかかり、特定のTextBoxクラスに依存することなく
    制御できないか、ということになります。
    依存して、それぞれOn~メソッドで制御すれば解決することも確認していますが、可能ならそれを
    したくありません。

    作り方を変えることにより表題の問題のみならず上記の動作もすべて満たすことができるならば
    何とか作り替えようかと思いますが、表題の問題を解決できても上記の動作のどこかに問題が
    生じる可能性があるならば、今回の問題を諦め、特定のTextBoxに依存させます。

    2018年12月14日 2:45
  • 最新のは読んでいません。
    https://stackoverflow.com/questions/3694720/combobox-autocomplete-on-substring
    これの1つ目2つ目にあるTextBoxを拡張したクラスとか。
    2つのコントロールを組み合わせるのではなく、「オートコンプリート機能を持つように拡張する」方向で。オープンソースのコードを見るなどして、オートコンプリートのメニューの出し方を真似するのがいいと思います。
    おっと、検索過程。「部分一致」の英語を調べて、「c# autocomplete partial match」で検索。

    Jitta@わんくま同盟


    • 編集済み Jitta 2018年12月14日 3:17 検索ワード追加
    2018年12月14日 3:15
  • 専用フォームを描画して表示する形式ですか。
    フォームが違うからそもそもフォーカス遷移しないのか、なるほど、と思いましたが、これはこれで、このコードのままでは至るところで問題があるようでした。

    ・フォームをドラッグしてもドロップダウンが閉じない。
    ・フォームの何もないところをクリックしてもドロップダウンが閉じない。
    ・Alt+Tabとかするとドロップダウンだけ残る。(HideTimer_TickのCntを減らせば済む模様)
    ・ドロップされているアイテムをクリックした直後にドロップダウンが閉じられる。
    ・上キー、下キーの制御ができていない。
    ・ドロップダウンを開いたり、スクロールバーの▲、▼を連打すると画面がちらつく。(フォームが非アクティブになることを防いでるせい?)

    まぁ作りの問題だけでどれも解消できるのかもしれませんが、私には想像つきません。

    私も散々調べましたが、どれも、フォームをはみ出せないとか、スクロールバーをドラッグした時点でフォーカスが遷移してしまってテキスト入力ができなくなるとか、
    どこかに問題があって、結局のところ作り方の思想を変えないと解消できない問題にぶちあたり、現在の仕組みに至っています。

    落としどころとして、どこかを諦めないとダメなのかなーという感じですかね。

    2018年12月14日 5:09