Как я могу динамически изменять записи автозаполнения в поле со списком C # или текстовое поле? - PullRequest
46 голосов
/ 05 февраля 2009

У меня есть поле со списком в 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 (), я, наконец, сдался и просто реализовал свой собственный выпадающий список автозаполнения, используя список. Он гораздо более отзывчивый, чем встроенный, и я потратил на это меньше времени, чем пытался заставить встроенный работать, поэтому урок для всех, кто хочет такого поведения, - вы, вероятно, лучше реализовать это самостоятельно.

Ответы [ 14 ]

0 голосов
/ 18 июня 2010

Я пришел сюда вначале в поисках решения, но теперь нашел свое.

Хитрость заключается не в том, чтобы вызывать Clear () в AutoCompleteCustomSource, а в том, чтобы удалить все элементы в цикле 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 голосов
/ 26 апреля 2010

Сэм, ты понял это? Я сталкиваюсь с той же ситуацией. Clear (), кажется, вызывает исключение. Я удалил вызов, чтобы очистить, и я получаю правильное событие предложения, хотя коллекция продолжает расти ...

Кроме того, что касается частных пользователей: вы можете получить к ним доступ, используя отражение:

PropertyInfo[] props = [object].GetType().GetProperties({flags go here});
props[0].SetValue(this, new object[] { 0 });
0 голосов
/ 28 апреля 2009
if(!textBox3.AutoCompleteCustomSource.Contains(textBox3.Text))
   textBox3.AutoCompleteCustomSource.Add(textBox3.Text);
0 голосов
/ 08 февраля 2009

обновление: Основная причина поставить замок на это место

это работает :) большинство «таинственных исключений», которые у меня когда-либо были, после этого трюка исчезают


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