Подтвердить что ты не робот

Как я могу динамически изменять автоматически заполняемые записи в поле со списком С# или текстовом поле?

У меня есть комбобокс на С#, и я хочу использовать с ним автоматически полные предложения, однако я хочу иметь возможность изменять автоматически заполненные записи по типу пользователя, потому что допустимые допустимые записи слишком многочисленны, чтобы заполнить AutoCompleteStringCollection при запуске.

В качестве примера предположим, что я разрешаю пользователю вводить имя. У меня есть список возможных первых имен ( "Joe" , "John" ) и список фамилий ( "Bloggs", "Smith" ), но если у меня будет тысяча, то это будет миллион возможных строк - слишком много, чтобы добавить автозаполнение. Поэтому сначала я хочу иметь только первые имена в качестве предложений ( "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() после.
  • Вызов Remove() для всех существующих строк вместо Clear().
  • Удаление текста из выпадающего списка при обновлении строк и добавлении его обратно.
  • Установка AutoCompleteMode в "None", когда я меняю строки, и затем снова вернусь к "SuggestAppend".
  • Приобретение события TextUpdate или KeyPress вместо TextChanged.
  • Каждый раз заменяя существующий AutoCompleteCustomSource на новый AutoCompleteStringCollection.

Ни одна из них не помогла, даже в различных комбинациях. Spence предложил, чтобы я попытался переопределить функцию ComboBox, которая получает список строк для автоматического завершения. Используя рефлектор, я нашел несколько методов в классе ComboBox, которые выглядят многообещающими - GetStringsForAutoComplete() и SetAutoComplete(), но они оба являются частными, поэтому я не могу получить к ним доступ из производного класса. Я больше не мог этого делать.

Я попробовал заменить ComboBox на TextBox, потому что интерфейс автозаполнения один и тот же, и я обнаружил, что поведение немного отличается. С TextBox он работает лучше, поскольку часть Append автозаполнения работает правильно, но в разделе "Предложение" нет - окно предложений кратковременно вспыхивает, а затем сразу исчезает.

Итак, я подумал: "Хорошо, я буду жить без функциональности" Предложить "и просто использовать" Добавить "), однако, когда я устанавливаю AutoCompleteMode в Append, я получаю исключение нарушения доступа. То же самое происходит и с предложением - единственный режим, который не генерирует исключений, - это SuggestAppend, даже несмотря на то, что часть предложения не ведет себя правильно.

Я думал, что при использовании управляемого кода на С# было невозможно получить исключения нарушения прав доступа. Avram предложил использовать "блокировку", чтобы исправить это, но я не знаю, что я должен заблокировать - единственное, что имеет член SyncRoot, - это AutoCompleteStringCollection и блокировка, которая не препятствует исключениям нарушения доступа. Я также попытался заблокировать ComboBox или TextBox, но это тоже не помогло. Как я понимаю, блокировка только предотвращает другие блокировки, поэтому, если базовый код не использует блокировку, то мой использование этого не будет иметь никакого значения.

Итогом всего этого является то, что я не могу использовать TextBox или ComboBox с динамическим автоматическим завершением. Кто-нибудь знает, как я могу это достичь?

Обновление:

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

Я попробовал заменить ComboBox на TextBox, потому что интерфейс автозаполнения один и тот же, и я обнаружил, что поведение немного отличается. С TextBox он работает лучше, поскольку часть Append автозаполнения работает правильно, но в разделе "Предложение" нет - окно предложений кратковременно вспыхивает, а затем сразу исчезает.

Итак, я подумал: "Хорошо, я буду жить без функции" Предложить "и просто использовать Append вместо этого", однако, когда я устанавливаю AutoCompleteMode в Append, я получаю исключение нарушения доступа. То же самое происходит и с предложением - единственный режим, который не генерирует исключений, - это SuggestAppend, даже несмотря на то, что часть предложения не ведет себя правильно.

Я думал, что при использовании управляемого кода С# было исключено исключение из-за нарушения прав доступа, но в любом случае результат заключается в том, что я не могу использовать TextBox или ComboBox с любым типом динамического автоматическое завершение. Кто-нибудь знает, как я могу это достичь?

Обновление 2:

Попробовав различные другие вещи, такие как изменение автозаполнения в рабочем потоке и использование BeginInvoke() для моделирования поведения типа PostMessage(), я, наконец, сдался и только что реализовал свой собственный автоматический полный раскрывающийся список, используя окно списка. Это гораздо более отзывчивое, чем встроенное, и я потратил меньше времени на это, чем на попытку заставить встроенный работать, поэтому урок для всех, кто хочет этого поведения, - вам, вероятно, лучше реализуя его самостоятельно.

4b9b3361

Ответ 1

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

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

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

Я нашел компромиссное решение: динамически заполнять AutoCompleteCustomSource каждый раз, когда длина текста достигает точно N символов (3 в моем случае). Это сработало, потому что сложность была резко сокращена. Количество записей, полученных из базы данных, соответствующих этим 3 начальным символам, было достаточно маленьким, чтобы избежать каких-либо проблем с производительностью.

Основной недостаток: пользователям не будет представлен список автозаполнения, пока они не наберут N-й char. Но похоже, что пользователи не ожидают значимого списка автозаполнения до ввода трех символов.

Надеюсь, что это поможет.

Ответ 2

Я думаю, вы, возможно, захотите выйти из рефлектора и посмотреть, как переопределить поведение автозаполнения в самой панели. Я уверен, что автозаполнение вызовет функцию, которая обращается к списку автозаполнения. Если вы можете найти эту функцию и переопределить ее, вы можете использовать любое поведение, которое вы хотите.

Посмотрите, какую документацию вы можете найти в самом классе combobox.

Ответ 3

Я не тестировал это, но это может стоить того.

Вместо очистки AutoCompleteCustomSource, двойной буфер, сохраняя два экземпляра. Когда текст изменяется, вызовите 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;
}

Ответ 4

Это сработало для меня, вы не addRange к тому же AutoCompleteStringCollection, а скорее создаете новый каждый раз.

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

Ответ 5

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

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


  • блокировка, как в этом коде, может помочь с вашим исключением
  • как вы упомянули ранее, проблема с использованием текстового поля меньше.
  • в этом коде, 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;
            }
        }
    }

Ответ 6

if(!textBox3.AutoCompleteCustomSource.Contains(textBox3.Text))
   textBox3.AutoCompleteCustomSource.Add(textBox3.Text);

Ответ 7

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

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

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

Ответ 8

Я пришел сюда изначально ища решение, но теперь нашел свое.

Хитрость заключается не в вызове 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);
        }
    }

Ответ 9

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

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

        }

    }

Ответ 10

Для меня секрет заключался в использовании события TextChanged и ни одного из KeyDown/Up/Press и т.д.

Обновление: После того, как у меня возникли другие проблемы с динамическим изменением AutoCompleteCustomSource, я в конце концов отказался от использования встроенной функции Autocomplete и реализовал свою собственную работу в гораздо более короткие сроки, чем я потратил на нее изначально. Кажется, что в неуправляемом коде есть некоторые проблемы, которые реализуют элемент управления ComboBox. В частности, у меня возникли проблемы с запуском обработчика событий TextChanged, когда это необходимо. Я решил использовать только обработчики OnKeyDown/Press/Up в своей пользовательской реализации, и это казалось более надежным.

Ответ 11

Лучшим решением для этого является использование обработчиков событий combobox. Используя textUpdate KeyDown DropDown и ChangeCommit, вы можете имитировать autocompletemode, и вы можете настроить, что искать и что отображаться в раскрывающемся списке.

Я нашел этот полезным, но он закодирован в Visual С++, и это toolstripcombobox, но концепция идентична. В любом случае существует огромное сходство С# и С++ в .net, и это не должно быть проблемой при понимании решения.

Индивидуальный автозапуск ToolStripCombobox в Visual С++

Ответ 12

Это очень старая проблема, которую я знаю, но она существует и сегодня. Мое обходное решение состояло в том, чтобы установить для режима Autocomplete и исходных свойств значение "none" и вручную обновить элементы в событии 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;
    }

Ответ 13

используйте этот код

private void dataGridView1_EditingControlShowing(object sender,DataGridViewEditingControlShowingEventArgs e)
    {

        if (e.Control is DataGridViewComboBoxEditingControl)
        {
            ((ComboBox)e.Control).DropDownStyle = ComboBoxStyle.DropDown;
            ((ComboBox)e.Control).AutoCompleteSource = AutoCompleteSource.ListItems;
            ((ComboBox)e.Control).AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest;
        }

}