【问题标题】:How to Asynchronously fetch AutoComplete data for a TextBox?如何异步获取文本框的自动完成数据?
【发布时间】:2009-01-13 18:56:14
【问题描述】:

我们的 WinForms 应用程序会延迟加载数据以自动完成文本框。其伪代码如下;

  1. TextBox 中的用户类型
  2. 在输入 pause 时,确定我们是否需要获取自动完成数据
  3. 在工作线程中,联系服务器并获取数据
  4. 调用回 UI 线程
  5. 设置textBox.AutoCompleteCustomSource = fetchedAutoCompleteStringCollection;
  6. 强制文本框下拉它的自动完成下拉菜单。

我目前在使用 #6 时遇到问题。作为一个 hack,我执行以下操作来模拟一个有效的按键,但它并非在所有情况下都有效。

     // 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 调用导致自动完成下拉是可以接受的。如果需要,我不介意 PInvoking。

【问题讨论】:

    标签: .net winforms autocomplete


    【解决方案1】:

    我只使用托管代码为 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】:

    通常,您将使用 COM 互操作并访问 IAutoCompleteIAutoComplete2IAutoCompleteDropDown 接口。不幸的是,这些都没有强制自动完成下拉的方法。

    您可能希望使用 Spy++ 并查看在自动完成显示时发送到控件的 Windows 消息。您可能会找到将激活它的命令消息。当然,这是一个实现细节,但它可能是唯一的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-09-02
      • 1970-01-01
      • 2019-02-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-06
      • 1970-01-01
      相关资源
      最近更新 更多