Как асинхронно получать данные автозаполнения для TextBox? - PullRequest
8 голосов
/ 13 января 2009

Наше WinForms приложение выполняет отложенную загрузку данных для автоматического заполнения текстового поля. Псевдокод для этого выглядит следующим образом:

  1. Пользовательские типы в TextBox
  2. При вводе паузы определите, нужно ли нам извлекать данные автозаполнения
  3. В рабочем потоке связаться с сервером и получить данные
  4. Вызов обратно в поток пользовательского интерфейса
  5. Набор textBox.AutoCompleteCustomSource = fetchedAutoCompleteStringCollection;
  6. Заставить текстовое поле отображаться в раскрывающемся списке автозаполнения.

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

     // This is a hack, but the only way that I have found to get the autocomplete
     // to drop down once the data is returned.
     textBox.SelectionStart = textBox.Text.Length;
     textBox.SelectionLength = 0;
     SendKeys.Send( " {BACKSPACE}" );

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

РЕДАКТИРОВАТЬ: Вызов Win32, чтобы вызвать раскрытие Auto Complete будет приемлемым. Я не против PInvoking, если я должен.

Ответы [ 2 ]

5 голосов
/ 01 декабря 2011

Я написал асинхронный класс автозаполнения для TextBox, используя только управляемый код. Надеюсь, это поможет.

using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;

namespace TextboxAutocomplete
{
    public abstract class AutoCompleteSource
    {
        private TextBox mTextBox;
        private AutoCompleteMode mAutoCompleteMode;

        public AutoCompleteSource(TextBox textbox) :
            this(textbox, AutoCompleteMode.Suggest) { }

        public AutoCompleteSource(TextBox textbox, AutoCompleteMode mode)
        {
            if (textbox == null)
                throw new ArgumentNullException("textbox");

            if (textbox.IsDisposed)
                throw new ArgumentException("textbox");

            mTextBox = textbox;
            mAutoCompleteMode = mode;

            mTextBox.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.None;

            BackgroundWorker autoCompleteLoader = new BackgroundWorker();
            autoCompleteLoader.DoWork += new DoWorkEventHandler(autoCompleteLoader_DoWork);
            autoCompleteLoader.RunWorkerCompleted += new RunWorkerCompletedEventHandler(autoCompleteLoader_RunWorkerCompleted);
            autoCompleteLoader.RunWorkerAsync();
        }

        void autoCompleteLoader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            AutoCompleteStringCollection collection = e.Result as AutoCompleteStringCollection;
            if (collection == null) return;

            if (mTextBox.InvokeRequired)
            {
                mTextBox.Invoke(new SetAutocompleteSource(DoSetAutoCompleteSource), new object[] { collection });
            }
            else
            {
                DoSetAutoCompleteSource(collection);
            }
        }

        protected void DoSetAutoCompleteSource(AutoCompleteStringCollection collection)
        {
            if (mTextBox.IsDisposed) return;

            mTextBox.AutoCompleteMode = mAutoCompleteMode;
            mTextBox.AutoCompleteSource = System.Windows.Forms.AutoCompleteSource.CustomSource;
            mTextBox.AutoCompleteCustomSource = collection;
        }

        void autoCompleteLoader_DoWork(object sender, DoWorkEventArgs e)
        {
            List<string> autoCompleteItems = GetAutocompleteItems();
            if (autoCompleteItems == null) return;
            AutoCompleteStringCollection collection = new AutoCompleteStringCollection();
            collection.AddRange(GetAutocompleteItems().ToArray());
            e.Result = collection;
        }

        protected abstract List<string> GetAutocompleteItems();
    }

    internal delegate void SetAutocompleteSource(AutoCompleteStringCollection collection);
}

Пример реализации:

using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Text;

namespace TextboxAutocomplete
{
    class MockAutoCompleteSource : AutoCompleteSource
    {
        public MockAutoCompleteSource(TextBox textbox)
            : base(textbox)
        {

        }

        protected override List<string> GetAutocompleteItems()
        {
            List<string> result = new List<string>();
            for (int i = 0; i < 2500; i++)
            {
                result.Add(Guid.NewGuid().ToString());
            }

            return result;
        }
    }
}

Как это использовать:

 ...
 TextBox myTextbox = new TextBox();
 MockAutoCompleteSource autoComplete =
      new MockAutoCompleteSource(myTextbox);
 ...
2 голосов
/ 14 января 2009

Обычно вы используете COM-взаимодействие и получаете доступ к интерфейсу IAutoComplete, IAutoComplete2 или IAutoCompleteDropDown. К сожалению, ни у одного из них нет методов, позволяющих вызвать автозаполнение.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...