web-dev-qa-db-ja.com

C#コンボボックスまたはテキストボックスのオートコンプリートエントリを動的に変更するにはどうすればよいですか?

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()を呼び出します。
  • Clear()ではなく、既存のすべての文字列でRemove()を呼び出します。
  • 文字列を更新している間にコンボボックスからテキストを消去し、後で追加し直します。
  • 文字列を変更している間にAutoCompleteModeを「なし」に設定し、その後で「SuggestAppend」に戻します。
  • TextUpdateの代わりにKeyPressまたはTextChangedイベントをフックします。
  • 既存のAutoCompleteCustomSourceを毎回新しいAutoCompleteStringCollectionに置き換えます。

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

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

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

C#マネージコードを使用する場合、アクセス違反の例外を取得することは不可能であると考えられていました。 Avram これを修正するために「ロック」を使用することを提案しましたが、何をロックすべきかわかりません-SyncRootメンバーを持つ唯一のものはAutoCompleteStringCollectionであり、ロックはアクセス違反の例外を防止しません。 ComboBoxまたはTextBoxをロックしようとしましたが、それも助けにはなりませんでした。私が理解しているように、ロックは他のロックを防止するだけなので、基になるコードがロックを使用していない場合、それを使用しても違いはありません。

これらすべての結果は、現在、TextBoxまたはComboBoxを動的オートコンプリートで使用できないことです。誰が私がこれを達成できるかについての洞察を持っていますか?

更新:

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

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

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

C#マネージコードを使用する場合、アクセス違反の例外を取得することは不可能であると考えられましたが、とにかく、現在、TextBoxまたはComboBoxをanyと一緒に使用できないということです動的オートコンプリートの一種。誰が私がこれを達成できるかについての洞察を持っていますか?

アップデート2:

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

47
Sam Hopkins

私は同じ問題を抱えていて、非常に簡単な回避策を見つけました。ここにいる他のみんなと同じように、コンポーネントの動作を制御する手段を見つけることができなかったので、受け入れなければなりませんでした。

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

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

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

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

お役に立てれば。

13
Alexandre Mafra

これは私にとってはうまくいきました。同じaddRangeAutoCompleteStringCollectionするのではなく、毎回新しいものを作成します。

form.fileComboBox.TextChanged += (sender, e) => {
    var autoComplete = new AutoCompleteStringCollection();
    string[] items = CustomUtil.GetFileNames();
    autoComplete.AddRange(items);
    form.fileComboBox.AutoCompleteCustomSource = autoComplete;
};
2
Jaanus

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

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;
}
1
Adam Haile

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

Comboboxクラス自体のドキュメントをご覧ください。

1
Spence

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

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;
}

手順:

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

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

0
steloe

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

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

PropertyInfo[] props = [object].GetType().GetProperties({flags go here});
props[0].SetValue(this, new object[] { 0 });
0
fixitchris

これは私が知っている非常に古い問題ですが、今日でも存在する問題です。私の回避策は、オートコンプリートモードとソースプロパティを「なし」に設定し、KeyUpイベントでアイテムを手動で更新することでした。

確かにそれはハッキーだと思いますが、データの入力速度に関係なく、かなり長い間問題なく完璧に機能し、髪の毛が伸び始めます。

提案するか、提案して追加するかを選択することもできます。誰かの助けになるといいのですが。

private void comboBox1_KeyUp(object sender, KeyEventArgs e)
    {

        if (string.IsNullOrWhiteSpace(comboBox1.Text))
        {
            e.Handled = true;
            return;
        }
        if (comboBox1.Text.Length < 3)
        {
            e.Handled = true;
            return;
        }

        if (e.KeyCode == Keys.Down || e.KeyCode == Keys.Up)
        {
            e.Handled = true;
            return;
        }
        else if (e.KeyCode == Keys.Back)
        {
            e.Handled = true;
            return;
        }

        string text = comboBox1.Text;

        if (e.KeyCode == Keys.Enter)
        {
            comboBox1.DroppedDown = false;
            comboBox1.SelectionStart = text.Length;
            e.Handled = true;
            return;
        }

        List<string> LS = Suggestions(comboBox1.Text);

        comboBox1.Items.Clear();
        comboBox1.Items.AddRange(LS.ToArray());

        //If you do not want to Suggest and Append
        //comment the following line to only Suggest
        comboBox1.Focus();

        comboBox1.DroppedDown = true;
        comboBox1.SelectionStart = text.Length;

        //Prevent cursor from getting hidden
        Cursor.Current = Cursors.Default;
        e.Handled = true;
    }
0
TEDSON

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

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


  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;
            }
        }
    }
0
Avram

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

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

0
Boog
if(!textBox3.AutoCompleteCustomSource.Contains(textBox3.Text))
   textBox3.AutoCompleteCustomSource.Add(textBox3.Text);
0
Randy

これを試していないが、あなたの特定のケースでは、次のようなコードを書くことができます:

    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 );

        }

    }
0
Luca Fagioli

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

秘Theは、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);
        }
    }
0
Jim Cramer

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

私は this 回答が有用であるとわかりましたが、ビジュアルc ++でコーディングされており、toolstripcomboboxですが、コンセプトは同一です。とにかく、.netのc#とc ++には大きな類似性があり、ソリューションを理解する上で問題になることはありません。

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

0
catzilla