webentwicklung-frage-antwort-db.com.de

Dynamische Autovervollständigung in C # winforms combobox

Mein Problem ist ähnlich wie dieses: Wie kann ich automatisch vervollständigte Einträge in einer C # -Combobox oder einem Textfeld ändern? Aber ich finde immer noch keine Lösung.

Das Problem kurz:

Ich habe eine ComboBox und eine große Anzahl von Datensätzen, die darin angezeigt werden. Wenn der Benutzer mit der Eingabe beginnt, möchte ich Datensätze laden, die mit dem eingegebenen Text beginnen, und den Benutzer für die automatische Vervollständigung anbieten. Wie im obigen Thema beschrieben, kann ich sie nicht in сomboBox_TextChanged laden, da ich die vorherigen Ergebnisse immer überschreibe und sie nie sehe.

Kann ich dies nur mit ComboBox implementieren? (nicht TextBox oder ListBox)

Ich verwende diese Einstellungen:

сomboBox.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
сomboBox.AutoCompleteSource = AutoCompleteSource.CustomSource;
18
algreat

Hier ist meine endgültige Lösung. Es funktioniert gut mit einer großen Datenmenge. Ich verwende Timer, um sicherzustellen, dass der Benutzer den aktuellen Wert finden möchte. Es sieht aus wie komplex, aber nicht. Danke an Max Lambertini für die Idee.

        private bool _canUpdate = true; 

        private bool _needUpdate = false;       

        //If text has been changed then start timer
        //If the user doesn't change text while the timer runs then start search
        private void combobox1_TextChanged(object sender, EventArgs e)
        {
            if (_needUpdate)
            {
                if (_canUpdate)
                {
                    _canUpdate = false;
                    UpdateData();                   
                }
                else
                {
                    RestartTimer();
                }
            }
        }

        private void UpdateData()
        {
            if (combobox1.Text.Length > 1)
            {
                List<string> searchData = Search.GetData(combobox1.Text);
                HandleTextChanged(searchData);
            }
        }       

        //If an item was selected don't start new search
        private void combobox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            _needUpdate = false;
        }

        //Update data only when the user (not program) change something
        private void combobox1_TextUpdate(object sender, EventArgs e)
        {
            _needUpdate = true;
        }

        //While timer is running don't start search
        //timer1.Interval = 1500;
        private void RestartTimer()
        {
            timer1.Stop();
            _canUpdate = false;
            timer1.Start();
        }

        //Update data when timer stops
        private void timer1_Tick(object sender, EventArgs e)
        {
            _canUpdate = true;
            timer1.Stop();
            UpdateData();
        }

        //Update combobox with new data
        private void HandleTextChanged(List<string> dataSource)
        {
            var text = combobox1.Text;

            if (dataSource.Count() > 0)
            {
                combobox1.DataSource = dataSource;  

                var sText = combobox1.Items[0].ToString();
                combobox1.SelectionStart = text.Length;
                combobox1.SelectionLength = sText.Length - text.Length;
                combobox1.DroppedDown = true;


                return;
            }
            else
            {
                combobox1.DroppedDown = false;
                combobox1.SelectionStart = text.Length;
            }
        }

Diese Lösung ist nicht sehr cool. Wenn also jemand eine andere Lösung hat, teilen Sie es mir bitte mit.

17
algreat

Ich stelle auch diese Art von Anforderungen vor kurzem fest. Ich habe die folgenden Eigenschaften festgelegt, ohne den Code zu schreiben, der funktioniert. Sehen Sie, ob dies Ihnen hilft.

 enter image description here

26
Devanathan.S

Ja, das kannst du sicher ... aber es braucht etwas Arbeit, damit es nahtlos funktioniert. Dies ist ein Code, den ich mir ausgedacht habe. Bedenken Sie, dass es nicht die automatischen Vervollständigungsfunktionen von combobox verwendet, und es könnte ziemlich langsam sein, wenn Sie es verwenden, um durch viele Elemente zu blättern ... 

string[] data = new string[] {
    "Absecon","Abstracta","Abundantia","Academia","Acadiau","Acamas",
    "Ackerman","Ackley","Ackworth","Acomita","Aconcagua","Acton","Acushnet",
    "Acworth","Ada","Ada","Adair","Adairs","Adair","Adak","Adalberta","Adamkrafft",
    "Adams"

};
public Form1()
{
    InitializeComponent();
}

private void comboBox1_TextChanged(object sender, EventArgs e)
{
    HandleTextChanged();
}

private void HandleTextChanged()
{
    var txt = comboBox1.Text;
    var list = from d in data
               where d.ToUpper().StartsWith(comboBox1.Text.ToUpper())
               select d;
    if (list.Count() > 0)
    {
        comboBox1.DataSource = list.ToList();
        //comboBox1.SelectedIndex = 0;
        var sText = comboBox1.Items[0].ToString();
        comboBox1.SelectionStart = txt.Length;
        comboBox1.SelectionLength = sText.Length - txt.Length;
        comboBox1.DroppedDown = true;
        return;
    }
    else
    {
        comboBox1.DroppedDown = false;
        comboBox1.SelectionStart = txt.Length;
    }
}

private void comboBox1_KeyUp(object sender, KeyEventArgs e)
{
    if (e.KeyCode == Keys.Back)
    {
        int sStart = comboBox1.SelectionStart;
        if (sStart > 0)
        {
            sStart--;
            if (sStart == 0)
            {
                comboBox1.Text = "";
            }
            else
            {
                comboBox1.Text = comboBox1.Text.Substring(0, sStart);
            }
        }
        e.Handled = true;
    }
}
9
Max Lambertini

Ich habe so etwas geschrieben ....

private void frmMain_Load(object sender, EventArgs e)
{
    cboFromCurrency.Items.Clear();
    cboComboBox1.AutoCompleteMode = AutoCompleteMode.Suggest;
    cboComboBox1.AutoCompleteSource = AutoCompleteSource.ListItems;
    // Load data in comboBox => cboComboBox1.DataSource = .....
    // Other things
}

private void cboComboBox1_KeyPress(object sender, KeyPressEventArgs e)
{
    cboComboBox1.DroppedDown = false;
}

Das ist alles (J)

7
Tonky75

Ich habe die Antwort von Max Lambertini sehr hilfreich gefunden, aber seine HandleTextChanged-Methode als solche geändert:

    //I like min length set to 3, to not give too many options 
    //after the first character or two the user types
    public Int32 AutoCompleteMinLength {get; set;}

    private void HandleTextChanged() {
        var txt = comboBox.Text;
        if (txt.Length < AutoCompleteMinLength)
            return;

        //The GetMatches method can be whatever you need to filter 
        //table rows or some other data source based on the typed text.
        var matches = GetMatches(comboBox.Text.ToUpper());

        if (matches.Count() > 0) {
            //The inside of this if block has been changed to allow
            //users to continue typing after the auto-complete results
            //are found.
            comboBox.Items.Clear();
            comboBox.Items.AddRange(matches);
            comboBox.DroppedDown = true;
            Cursor.Current = Cursors.Default;
            comboBox.Select(txt.Length, 0);
            return;
        }
        else {
            comboBox.DroppedDown = false;
            comboBox.SelectionStart = txt.Length;
        }
    }
3
JamesFaix

In früheren Antworten gibt es Nachteile. Bietet eine eigene Version mit der Auswahl in der Dropdown-Liste den gewünschten Artikel:

    private ConnectSqlForm()
    {
        InitializeComponent();
        cmbDatabases.TextChanged += UpdateAutoCompleteComboBox;
        cmbDatabases.KeyDown += AutoCompleteComboBoxKeyPress;
    }

    private void UpdateAutoCompleteComboBox(object sender, EventArgs e)
    {
        var comboBox = sender as ComboBox;
        if(comboBox == null)
        return;
        string txt = comboBox.Text;
        string foundItem = String.Empty;
        foreach(string item in comboBox.Items)
            if (!String.IsNullOrEmpty(txt) && item.ToLower().StartsWith(txt.ToLower()))
            {
                foundItem = item;
                break;
            }

        if (!String.IsNullOrEmpty(foundItem))
        {
            if (String.IsNullOrEmpty(txt) || !txt.Equals(foundItem))
            {
                comboBox.TextChanged -= UpdateAutoCompleteComboBox;
                comboBox.Text = foundItem;
                comboBox.DroppedDown = true;
                Cursor.Current = Cursors.Default;
                comboBox.TextChanged += UpdateAutoCompleteComboBox;
            }

            comboBox.SelectionStart = txt.Length;
            comboBox.SelectionLength = foundItem.Length - txt.Length;
        }
        else
            comboBox.DroppedDown = false;
    }

    private void AutoCompleteComboBoxKeyPress(object sender, KeyEventArgs e)
    {
        var comboBox = sender as ComboBox;
        if (comboBox != null && comboBox.DroppedDown)
        {
            switch (e.KeyCode)
            {
                case Keys.Back:
                    int sStart = comboBox.SelectionStart;
                    if (sStart > 0)
                    {
                        sStart--;
                        comboBox.Text = sStart == 0 ? "" : comboBox.Text.Substring(0, sStart);
                    }
                    e.SuppressKeyPress = true;
                    break;
            }

        }
    }
2
VadimSm

Dieser Code wird auf das Laden von Formularen geschrieben. Es zeigt die gesamte Tour in der Datenbank an, wenn der Benutzer einen Buchstaben in das Kombinationsfeld eingibt. Dieser Code schlägt automatisch die richtige Auswahl vor und hängt sie dem Benutzer an. 

            con.Open();
            cmd = new SqlCommand("SELECT DISTINCT Tour FROM DetailsTB", con);
            SqlDataReader sdr = cmd.ExecuteReader();
            DataTable dt = new DataTable();
            dt.Load(sdr);
            combo_search2.DisplayMember = "Tour";
            combo_search2.DroppedDown = true;

            List<string> list = new List<string>();
            foreach (DataRow row in dt.Rows)
            {
                list.Add(row.Field<string>("Tour"));
            }
            this.combo_search2.Items.AddRange(list.ToArray<string>());
            combo_search2.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
            combo_search2.AutoCompleteSource = AutoCompleteSource.ListItems;
            con.Close();
2
shoaib shaikh
 using (var client = new UserServicesClient())
 {
     var list = new AutoCompleteStringCollection();
     list.AddRange(client.ListNames(query).ToArray());
     comboBoxName.AutoCompleteCustomSource = list;
 }
0
knatz

Dies war ein großer Schmerz, um arbeiten zu können. Ich habe eine Menge Sackgassen geschlagen, aber das Endergebnis ist ziemlich einfach. Hoffentlich kann es für jemanden von Nutzen sein. Vielleicht braucht es ein bisschen Spucke und Politur, das ist alles.

Hinweis: _addressFinder.CompleteAsync gibt eine Liste von KeyValuePairs zurück.

public partial class MyForm : Form
{
    private readonly AddressFinder _addressFinder;
    private readonly AddressSuggestionsUpdatedEventHandler _addressSuggestionsUpdated;

    private delegate void AddressSuggestionsUpdatedEventHandler(object sender, AddressSuggestionsUpdatedEventArgs e);

    public MyForm()
    {
        InitializeComponent();

        _addressFinder = new AddressFinder(new AddressFinderConfigurationProvider());

        _addressSuggestionsUpdated += AddressSuggestions_Updated;

        MyComboBox.DropDownStyle = ComboBoxStyle.DropDown;
        MyComboBox.DisplayMember = "Value";
        MyComboBox.ValueMember = "Key";
    }

    private void MyComboBox_KeyPress(object sender, KeyPressEventArgs e)
    {
        if (char.IsControl(e.KeyChar))
        {
            return;
        }

        var searchString = ThreadingHelpers.GetText(MyComboBox);

        if (searchString.Length > 1)
        {
            Task.Run(() => GetAddressSuggestions(searchString));
        }
    }

    private async Task GetAddressSuggestions(string searchString)
    {
        var addressSuggestions = await _addressFinder.CompleteAsync(searchString).ConfigureAwait(false);

        if (_addressSuggestionsUpdated.IsNotNull())
        {
            _addressSuggestionsUpdated.Invoke(this, new AddressSuggestionsUpdatedEventArgs(addressSuggestions));
        }
    }

    private void AddressSuggestions_Updated(object sender, AddressSuggestionsUpdatedEventArgs eventArgs)
    {
        try
        {
            ThreadingHelpers.BeginUpdate(MyComboBox);

            var text = ThreadingHelpers.GetText(MyComboBox);

            ThreadingHelpers.ClearItems(MyComboBox);

            foreach (var addressSuggestions in eventArgs.AddressSuggestions)
            {
                ThreadingHelpers.AddItem(MyComboBox, addressSuggestions);
            }

            ThreadingHelpers.SetDroppedDown(MyComboBox, true);

            ThreadingHelpers.ClearSelection(MyComboBox);
            ThreadingHelpers.SetText(MyComboBox, text);
            ThreadingHelpers.SetSelectionStart(MyComboBox, text.Length);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
        finally
        {
            ThreadingHelpers.EndUpdate(MyComboBox);
        }
    }

    private class AddressSuggestionsUpdatedEventArgs : EventArgs
    {
        public IList<KeyValuePair<string, string>> AddressSuggestions { get; private set; }

        public AddressSuggestionsUpdatedEventArgs(IList<KeyValuePair<string, string>> addressSuggestions)
        {
            AddressSuggestions = addressSuggestions;
        }
    }
}

ThreadingHelpers ist nur eine Reihe statischer Methoden der Form:

    public static string GetText(ComboBox comboBox)
    {
        if (comboBox.InvokeRequired)
        {
            return (string)comboBox.Invoke(new Func<string>(() => GetText(comboBox)));
        }

        lock (comboBox)
        {
            return comboBox.Text;
        }
    }

    public static void SetText(ComboBox comboBox, string text)
    {
        if (comboBox.InvokeRequired)
        {
            comboBox.Invoke(new Action(() => SetText(comboBox, text)));
            return;
        }

        lock (comboBox)
        {
            comboBox.Text = text;
        }
    }
0
kraeg