50

C# にコンボボックスがあり、オートコンプリートの提案を使用したいのですが、有効なエントリが多すぎてAutoCompleteStringCollection起動時に入力できないため、ユーザーの入力に応じてオートコンプリートのエントリを変更できるようにしたいと考えています。

例として、ユーザーに名前を入力させているとします。可能な名のリスト ("Joe"、"John") と姓のリスト ("Bloggs"、"Smith") がありますが、それぞれが 1000 個ある場合、可能な文字列は 100 万個になりますオートコンプリート エントリに入れるには多すぎます。したがって、最初は最初の名前だけを提案として ("Joe", "John") たいと思います。次に、ユーザーが最初の名前 ("Joe") を入力したら、既存のオートコンプリート エントリを削除して置き換えます。選択された名前とそれに続く可能な姓 ("Joe Bloggs"、"Joe Smith") で構成される新しいセットを使用してそれらを作成します。これを行うために、次のコードを試しました。

void InitializeComboBox()
{
    ComboName.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
    ComboName.AutoCompleteSource = AutoCompleteSource.CustomSource;
    ComboName.AutoCompleteCustomSource = new AutoCompleteStringCollection();
    ComboName.TextChanged += new EventHandler( ComboName_TextChanged );
}

void ComboName_TextChanged( object sender, EventArgs e )
{
    string text = this.ComboName.Text;
    string[] suggestions = GetNameSuggestions( text );

    this.ComboQuery.AutoCompleteCustomSource.Clear();
    this.ComboQuery.AutoCompleteCustomSource.AddRange( suggestions );
}

ただし、これは正しく機能しません。Clear() を呼び出すと、コンボ ボックスに次の文字が表示されるまでオート コンプリート メカニズムが「オフ」になるようですが、もちろん、次の文字が表示されると、上記のコードは Clear() を再度呼び出すため、ユーザーは決して実際にオートコンプリート機能が表示されます。また、コンボ ボックスの内容全体が選択されるため、キーを押すたびに既存のテキストを選択解除する必要があり、使用できなくなります。Clear() の呼び出しを削除すると、オートコンプリートは機能しAddRange()ますが、追加した新しい提案がオートコンプリートのドロップダウンに表示されないため、呼び出しは効果がないようです。

これに対する解決策を探していて、さまざまな提案がありましたが、どれも機能しません。オートコンプリート機能が無効になっているように見えるか、新しい文字列が表示されません。ここに私が試したことのリストがあります:

  • BeginUpdate()弦交換前と交換後の呼び出しEndUpdate()
  • Remove()Clear() の代わりにすべての既存の文字列を呼び出します。
  • 文字列の更新中にコンボボックスからテキストをクリアし、後で追加し直します。
  • 文字列を変更している間は「なし」に設定AutoCompleteModeし、後で「SuggestAppend」に戻します。
  • TextUpdateの代わりにまたはKeyPressイベントをフックしTextChangedます。
  • 毎回AutoCompleteCustomSource新しいものと既存のものを交換します。AutoCompleteStringCollection

さまざまな組み合わせであっても、これらのどれも役に立ちませんでした。 SpenceComboBoxは、オートコンプリートで使用する文字列のリストを取得する関数をオーバーライドしてみることを提案しました。リフレクターを使用して、ComboBox有望に見えるクラスGetStringsForAutoComplete()内のいくつかのメソッドを見つけましたSetAutoComplete()が、それらは両方ともプライベートであるため、派生クラスからアクセスできません。私はそれ以上取ることができませんでした。

ComboBoxオートコンプリート インターフェースは同じなので、をに置き換えてTextBoxみましたが、動作が少し異なることがわかりました。を使用するTextBoxと、オートコンプリートの追加部分が適切に機能するように見えますが、提案部分は機能しません。提案ボックスは一時的に点滅しますが、すぐに消えます。

そこで、「さて、サジェスト機能なしで生きて、代わりに追加を使用する」と思ったのですが、AutoCompleteMode追加に設定すると、アクセス違反の例外が発生します。SuggestAppendサジェストでも同じことが起こります。サジェスト部分が正しく動作しなくても、例外をスローしない唯一のモードはです。

C# マネージ コードを使用する場合、アクセス違反の例外を取得することは不可能であると考えられていました。 Avramは、これを修正するために「ロック」を使用することを提案しましたが、何をロックすればよいかわかりAutoCompleteStringCollectionません。ComboBoxまたはもロックしようとしましTextBoxたが、どちらも役に立ちませんでした。私が理解しているように、ロックは他のロックを防ぐだけなので、基になるコードがロックを使用していない場合、ロックを使用しても違いはありません。

このすべての結果は、現在、ダイナミックオートコンプリートでaTextBoxまたは aを使用できないことです。ComboBoxどうすればこれを達成できるかについての洞察を持っている人はいますか?

アップデート:

私はまだこれを機能させていませんが、さらにいくつか発見しました。たぶん、これのいくつかは、他の誰かが解決策を考え出すきっかけになるでしょう.

ComboBoxオートコンプリート インターフェースは同じなので、をに置き換えてTextBoxみましたが、動作が少し異なることがわかりました。を使用するTextBoxと、オートコンプリートの追加部分が適切に機能するように見えますが、提案部分は機能しません。提案ボックスは一時的に点滅しますが、すぐに消えます。

そこで、「さて、サジェスト機能を使わずに Append を使用することにしよう」と考えましたが、AutoCompleteModeAppend に設定すると、アクセス違反の例外が発生します。SuggestAppendサジェストでも同じことが起こります。サジェスト部分が正しく動作しなくても、例外をスローしない唯一のモードはです。

C# マネージ コードを使用する場合、アクセス違反の例外を取得することは不可能であると考えられていましたが、いずれにせよ、現在、動的オート コンプリートでaTextBoxまたは aを使用することはできません。ComboBoxどうすればこれを達成できるかについての洞察を持っている人はいますか?

更新 2:

ワーカー スレッドでオートコンプリートを変更したり、PostMessage() 型の動作をシミュレートするために使用したりするなど、さまざまなことを試した後、BeginInvoke()最終的にあきらめて、リスト ボックスを使用して独自のオート コンプリート ドロップダウンを実装しました。組み込みのものよりもはるかに応答性が高く、組み込みのものを機能させるために費やした時間よりも少ない時間でした。自分で実装します。

4

14 に答える 14

13

同じ問題が発生し、非常に簡単な回避策が見つかりました。ここにいる他のみんなと同じように、コンポーネントの動作を制御する手段が見つからなかったので、それを受け入れる必要がありました。

自然な動作は次のとおりです。ユーザーがテキストボックスに入力するたびにリストに動的にデータを入力することはできません。一度入力する必要があります。そうすれば、オートコンプリートメカニズムが制御を引き継ぎます。結論は次のとおりです。AutoCompleteCustomSourceにデータベース内のすべての可能なエントリを入力して、希望どおりに機能させる必要があります。

もちろん、リストに入力するレコードが数百万ある場合、これは実行可能ではありません。データ転送とオートコンプリートメカニズム自体のパフォーマンスの問題により、それを行うことはできません。

私が見つけた妥協案は、テキストの長さが正確にN文字(私の場合は3文字)に達するたびにAutoCompleteCustomSourceに動的にデータを入力することでした。複雑さが大幅に軽減されたため、これは機能しました。これらの3つの初期文字に一致するデータベースからフェッチされるレコードの数は、パフォーマンスの問題を回避するのに十分な数でした。

主な欠点は、ユーザーがN番目の文字を入力するまでオートコンプリートリストが表示されないことです。しかし、3文字を入力する前に、ユーザーは意味のあるオートコンプリートリストを実際には期待していないようです。

お役に立てれば。

于 2011-12-06T06:38:26.747 に答える
1

リフレクターを取り出して、コンボボックス自体のオートコンプリート動作をオーバーライドすることを検討してください。オートコンプリートは、オートコンプリート リストにアクセスする関数を呼び出すと確信しています。この関数を見つけてオーバーライドできる場合は、必要な動作を使用できます。

コンボボックス クラス自体に関するドキュメントを参照してください。

于 2009-02-05T11:51:34.883 に答える
1

私はこれをテストしていませんが、試してみる価値があるかもしれません。

AutoCompleteCustomSource をクリアする代わりに、2 つのインスタンスを保持してダブル バッファを作成します。テキストが変更されたら、GetNameSuggestions() を呼び出して、現在使用されていない文字列を作成し、ComboName.AutoCompleteCustomSource を設定したばかりのものに設定します。

私はそれがこのように見えるべきだと思います。

AutoCompleteCustomSource accs_a;
AutoCompleteCustomSource accs_b;
bool accs_check = true; //true for accs_a, false for accs_b
void InitializeComboBox()
{
    ComboName.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
    ComboName.AutoCompleteSource = AutoCompleteSource.CustomSource;

    accs_a = new AutoCompleteStringCollection();
    accs_b = new AutoCompleteStringCollection();

    ComboName.AutoCompleteCustomSource = accs_a;
    ComboName.TextChanged += new EventHandler( ComboName_TextChanged );
}

void ComboName_TextChanged( object sender, EventArgs e )
{
    string text = this.ComboName.Text;

    if(accs_check)
    {
       accs_b.Clear();
       accs_b.AddRange(GetNameSuggestions( text ));
       accs_check = false;
    }
    else
    {
       accs_a.Clear();
       accs_a.AddRange(GetNameSuggestions( text ));
       accs_check = true;
    }

    this.ComboQuery.AutoCompleteCustomSource = accs_check? accs_a : accs_b;
}
于 2009-02-05T18:01:05.060 に答える
0

私にとっての秘密は、TextChanged イベントを使用し、KeyDown/Up/Press などを使用しないことでした。

更新: AutoCompleteCustomSource を動的に変更する際に他の問題が発生した後、最終的に組み込みのオートコンプリート機能の使用を放棄し、最初に無駄にしたよりもはるかに短い時間で独自の機能を実装しました。ComboBox コントロールを実装するアンマネージ コードに問題があるようです。具体的には、必要なときに TextChanged イベント ハンドラーが発生するという問題がありました。カスタム実装では OnKeyDown/Press/Up ハンドラーのみを使用することにしましたが、その方が信頼性が高いようです。

于 2011-09-21T20:41:23.283 に答える
0

更新:この場所にロックをかける主な理由は

その作業:)このトリックが消えた後、私が今までに持っていた「神秘的な例外」のほとんど


  1. このコードのようなロックは、例外に役立ちます
  2. 前に述べたように、テキストボックスを使用すると問題が少なくなります
  3. このコードでは、SuggestAppend は正常に動作しています


    private void Form1_Load(object sender, EventArgs e)
    {
        textBox1.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
        textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;

        textBox1.TextChanged+=new EventHandler(textBox1_TextChanged);

        col1.AddRange(new string[] { "avi avi", "avram avram" });
        col2.AddRange(new string[] { "boria boria", "boris boris" });

        textBox1.AutoCompleteCustomSource = col1;
        textBox1.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
    }
    AutoCompleteStringCollection col1 = new AutoCompleteStringCollection();
    AutoCompleteStringCollection col2 = new AutoCompleteStringCollection();

    object locker = new object();
    private void textBox1_TextChanged(object sender, EventArgs e)
    {
        lock (locker)
        {
            if (textBox1.Text.StartsWith("a") && textBox1.AutoCompleteCustomSource != col1)
            {
                textBox1.AutoCompleteCustomSource = col1;
            }
            if (textBox1.Text.StartsWith("b") && textBox1.AutoCompleteCustomSource != col2)
            {
                textBox1.AutoCompleteCustomSource = col2;
            }
        }
    }
于 2009-02-07T21:54:25.400 に答える
0

これを試したことはありませんが、特定のケースでは、次のようにコーディングできます。

    private void txtAutoComplete_KeyUp(object sender, KeyEventArgs e)
    {

        String text = txtAutoComplete.Text;

        if (text.EndsWith(" "))
        {

            string[] suggestions = GetNameSuggestions( text ); //put [text + " "] at the begin of each array element
            txtAutoComplete.AutoCompleteCustomSource.Clear();
            txtAutoComplete.AutoCompleteCustomSource.AddRange( suggestions );

        }

    }
于 2011-02-27T15:25:56.600 に答える
0

ここで提供されているすべてのソリューションを (成功せずに) 試した後、自分に合ったものを見つけました。

private void CellBox_TextChanged(object sender, EventArgs e)
{
    ((TextBox)sender).TextChanged -= CellBox_TextChanged;
    ((TextBox)dataGridView1.EditingControl).AutoCompleteMode = AutoCompleteMode.None;
    ((TextBox)dataGridView1.EditingControl).AutoCompleteCustomSource = null;                
    aCSC.Clear();
    foreach (string value in Autocompletevalues())
    {
        aCSC.Add(value);
    }
    ((TextBox)dataGridView1.EditingControl).AutoCompleteCustomSource = aCSC;
    ((TextBox)dataGridView1.EditingControl).AutoCompleteMode = AutoCompleteMode.Suggest;
    ((TextBox)sender).TextChanged += CellBox_TextChanged;
}

手順:

  • イベントハンドラを無効にする
  • オートコンプリート モードを無効にする
  • ソースを null に設定
  • AutoCompleteStringCollection (aCSC) の更新
  • Source を更新された AutoCompleteStringCollection に設定します
  • オートコンプリート モードを有効にする
  • イベントハンドラーを有効にする

私はそれが誰かを助けることを願っています..

于 2018-11-29T12:58:05.393 に答える
0

サム、これでわかった?私は同じ状況に陥っています。Clear() は例外を引き起こすようです。clear の呼び出しを削除し、コレクションは増え続けていますが、正しい提案イベントを取得しています...

また、プライベート メンバーに関しては、リフレクションを使用してアクセスできます。

PropertyInfo[] props = [object].GetType().GetProperties({flags go here});
props[0].SetValue(this, new object[] { 0 });
于 2010-04-26T18:39:54.057 に答える
0

最初は解決策を探してここに来ましたが、今では独自の解決策を見つけました。

秘訣は、AutoCompleteCustomSource で Clear() を呼び出すのではなく、for ループ内のすべての項目を削除してから、新しいデータでリストを再構築することです。私の場合 (書籍コレクション アプリケーション) では、データベースから著者名をすべて取得するのではなく、特定の開始文字で取得しています。これは、コンボボックスのテキストボックス部分が空であるか空になった場合にのみ機能することに注意してください。

    private void cboAuthor_KeyDown(object sender, KeyEventArgs e)
    {
        if (cboAuthor.Text.Length == 0)
        {
            // Next two lines simple load data from the database in the
            // into a collection (var gateway), base on first letter in
            // the combobox. This is specific to my app.
            var gateway = new AuthorTableGateway();
            gateway.LoadByFirstLetter(Char.ConvertFromUtf32(e.KeyValue)[0]);

            // Clear current source without calling Clear()
            for (int i = 0; i < authorsAutoComplete.Count; i++)
                authorsAutoComplete.RemoveAt(0);

            // Rebuild with new data
            foreach (var author in gateway)
                authorsAutoComplete.Add(author.AuthorName);
        }
    }
于 2010-06-18T17:10:15.783 に答える
0

これに対する最善の解決策は、コンボボックスのイベント ハンドラーを使用することです。textUpdate KeyDown DropDownChangeCommitを使用 することで、オートコンプリート モードを模倣し、検索する内容とドロップダウンに表示する内容をカスタマイズできます。

この回答は便利だと思いましたが、ビジュアル C++ でコーディングされており、toolstripcombobox ですが、概念は同じです。とにかく、.net には c# と c++ の大きな類似点があり、解決策を理解する上で問題になることはありません。

Visual C++ での ToolStripCombobox のカスタマイズされた自動検索

于 2015-01-07T08:23:28.377 に答える