Поле ввода WinForms (C #) с автозаполнением на основе базы данных

Я пытаюсь создать поле ввода текста с функцией автозаполнения. Список доступных опций огромен (50 000+), и их нужно будет запросить в TextChanged (после ввода первых 3 символов).

У меня есть 99% -ное рабочее решение с TextBox, устанавливающее AutoCompleteCustomSource для моего нового AutoCompleteStringCollection в событии TextChanged, но это приводит к случайным нарушениям доступа к памяти из-за хорошо задокументированной ошибки в базовой реализации AutoComplete ...

Microsoft Служба поддержки говорит: «Не изменяйте список кандидатов для автозаполнения динамически во время ключевых событий» ...

Несколько потоков SO: 1, 2, 3

В этих потоках есть несколько предложений о том, как предотвратить исключения, но, похоже, ничто не устраняет их полностью, поэтому я ищу альтернативу. пытался переключиться на решение на основе ComboBox, но не могу заставить его вести себя так, как я хочу.

  • После того, как пользователь вводит третий символ, я обновляю DataSource ComboBox, но первый элемент выбирается автоматически. Пользователь не может продолжать вводить оставшуюся часть имени.

  • Элементы ComboBox не отображаются, пока пользователь не щелкнет треугольник, чтобы развернуть список.

  • Если пользователь выбирает введенный текст и начинает печатать, я устанавливаю для DataSource значение null, чтобы удалить список предложений. При этом курсор помещается в начало текста, поэтому их символы появляются в совершенно неправильном порядке!

Мой взгляд:

    public event EventHandler SearchTextChanged;
    public event EventHandler InstrumentSelected;

    public Instrument CurrentInstrument
    {
        get { return comboBoxQuickSearch.SelectedItem as Instrument; }
    }

    public IEnumerable<Instrument> Suggestions
    {
        get { return comboBoxQuickSearch.DataSource as IEnumerable<Instrument>; }
        set
        {
            comboBoxQuickSearch.DataSource = value;
            comboBoxQuickSearch.DisplayMember = "Name";
        }
    }

    public string SearchText
    {
        get { return comboBoxQuickSearch.Text; }
    }

    private void comboBoxQuickSearch_TextChanged(object sender, EventArgs e)
    {
        if (SearchTextChanged != null)
        {
            SearchTextChanged(sender, e);
        }
    }

    private void comboBoxQuickSearch_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Enter && InstrumentSelected != null)
        {
            InstrumentSelected(sender, e);
        }
    }

Мой ведущий:

    private void SearchTextChanged(object sender, EventArgs e)
    {
        lock (searchLock)
        {
            // Do not update list of suggestions if:
            // 1) an instrument has already been selected
            //    (the user may be scrolling through suggestion list)
            // 2) a search has taken place within the last MIN_SEARCH_INTERVAL
            if (DateTime.Now - quickSearchTimeStamp < minimumSearchInterval
                || (view.Suggestions != null && view.Suggestions.Any(i => i.Name == view.SearchText)))
            {
                return;
            }

            string searchText = view.SearchText.Trim();
            if (searchText.Length < SEARCH_PREFIX_LENGTH)
            {
                // Do not show suggestions
                view.Suggestions = null;
                searchAgain = false;
            }
            // If the prefix has been entered or changed,
            // or another search is needed to display the full sublist
            else if (searchText.Length == SEARCH_PREFIX_LENGTH
                  || searchText.Substring(0, SEARCH_PREFIX_LENGTH) != searchTextPrefix
                  || searchAgain)
            {
                // Record the current time and prefix
                quickSearchTimeStamp = DateTime.Now;
                searchTextPrefix = searchText.Substring(0, SEARCH_PREFIX_LENGTH);

                // Query matches from DB
                IList<Instrument> matches = QueryMatches(searchText);

                // Update suggestions
                view.Suggestions = matches;

                // If a large number of results was received, search again on the next chararacter
                // This ensures the full match list is presented
                searchAgain = matches.Count() > MAX_RESULTS;
            }
        }
    }

(Бит searchAgain остался от реализации TextBox, где AutoCompleteCustomSource не всегда показывает полный список, если он содержит слишком много элементов.)

Могу ли я заставить ComboBox работать так, как я хочу, предлагая предложения по типам пользователей, учитывая мое требование запрашивать эти предложения в TextChanged?

Есть ли другая комбинация элементов управления, которую я должен использовать для лучшего взаимодействия с пользователем, например ListBox?


person Chris B    schedule 09.05.2015    source источник
comment
Попробуйте выполнить обновление после обработки события textchanged. (т.е. добавить временную задержку)   -  person Christian Irwan Hadi Wicaksana    schedule 09.05.2015
comment
Спасибо за ответ. Как вы думаете, какой способ сделать это наиболее надежно? Интересно, что обновление AutoCompleteDataSource асинхронно после TextChanged не приводит к тому, что список предложений «опускается» под текстовое поле.   -  person Chris B    schedule 11.05.2015