У меня есть поле со списком в C #, и я хочу использовать предложения автозаполнения с ним, однако я хочу иметь возможность изменять записи автозаполнения по мере ввода пользователем, потому что возможных допустимых записей слишком много, чтобы заполнить AutoCompleteStringCollection
при запуске.
В качестве примера предположим, что я разрешаю пользователю вводить имя. У меня есть список возможных имен («Джо», «Джон») и список фамилий («Блоггс», «Смит»), но если у меня будет тысяча каждого из них, то это будет миллион возможных строк - слишком много, чтобы положить в автозаполнение записей. Итак, сначала я хочу иметь только первые имена в качестве предложений («Джо», «Джон»), а затем, как только пользователь набрал первое имя («Джо»), я хочу удалить существующие записи автозаполнения и заменить они с новым набором, состоящим из выбранного имени, за которым следуют возможные фамилии («Джо Блоггс», «Джо Смит»). Для этого я попробовал следующий код:
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
на «None», пока я меняю строки, а затем возвращение к «SuggestAppend».
- Перехват события
TextUpdate
или KeyPress
вместо TextChanged
.
- Замена существующего
AutoCompleteCustomSource
новым AutoCompleteStringCollection
каждый раз.
Ничто из этого не помогло, даже в различных комбинациях. Спенс предложил мне попробовать переопределить функцию ComboBox
, которая получает список строк для использования в автозаполнении. Используя отражатель, я нашел несколько методов в классе ComboBox
, которые выглядят многообещающе - GetStringsForAutoComplete()
и SetAutoComplete()
, но оба они закрытые, поэтому я не могу получить к ним доступ из производного класса. Я не мог продолжать это.
Я попытался заменить ComboBox
на TextBox
, потому что интерфейс автозаполнения такой же, и я обнаружил, что поведение немного отличается. С TextBox
, кажется, он работает лучше, так как часть добавления автозаполнения работает правильно, а часть «Предлагать» - не работает - окно предложений кратковременно мигает, но затем сразу исчезает.
Поэтому я подумал: «Хорошо, я буду жить без функции« Предложить »и просто буду использовать« Добавить »» », однако, когда я установил AutoCompleteMode
на« Добавить », я получил исключение нарушения прав доступа. То же самое происходит с Suggest - единственный режим, который не генерирует исключения, это SuggestAppend
, хотя часть Suggest тогда не будет работать правильно.
Я думал, что при использовании управляемого кода на C # было невозможно получить исключения нарушения доступа. Avram предложил использовать «lock», чтобы исправить это, но я не знаю, что мне нужно блокировать - единственное, что имеет член SyncRoot, это AutoCompleteStringCollection
, и блокировка, которая не мешает исключения нарушения доступа. Я также попытался заблокировать ComboBox
или TextBox
, но это тоже не помогло. Насколько я понимаю, блокировка предотвращает только другие блокировки, поэтому, если базовый код не использует блокировку, то использование ее не будет иметь никакого значения.
Результатом всего этого является то, что в настоящее время я не могу использовать TextBox
или ComboBox
с динамическим автоматическим завершением. У кого-нибудь есть идеи, как мне этого добиться?
Обновление:
У меня все еще нет этой работы, но я узнал еще кое-что. Возможно, что-то из этого вдохновит кого-то другого на решение проблемы.
Я попытался заменить ComboBox
на TextBox
, потому что интерфейс автозаполнения такой же, и я обнаружил, что поведение немного отличается. С TextBox
он работает лучше, так как часть «Автоматическое завершение» «Добавить» работает правильно, а «Предлагать» - нет, поле с подсказкой кратковременно мигает, но затем сразу исчезает.
Поэтому я подумал: «Хорошо, я буду жить без функции« Предложить »и просто вместо этого буду использовать« Добавить »» », однако, когда я установил для« * 1072 »значение« Добавить », я получил исключение нарушения прав доступа. То же самое происходит с Suggest - единственный режим, который не генерирует исключения, это SuggestAppend
, хотя часть Suggest тогда не будет работать правильно.
Я думал, что при использовании управляемого кода C # было невозможно получить исключения нарушения доступа, но в любом случае, в результате я не могу использовать TextBox
или ComboBox
с каким-либо динамическим Авто завершено. Кто-нибудь знает, как мне этого добиться?
Обновление 2:
После попытки различных других вещей, таких как изменение автозаполнения в рабочем потоке и использование BeginInvoke()
для имитации поведения типа PostMessage (), я, наконец, сдался и просто реализовал свой собственный выпадающий список автозаполнения, используя список. Он гораздо более отзывчивый, чем встроенный, и я потратил на это меньше времени, чем пытался заставить встроенный работать, поэтому урок для всех, кто хочет такого поведения, - вы, вероятно, лучше реализовать это самостоятельно.