C#窗体中的下拉框动态自动完成

39
我的问题类似于这个: 如何在C#的ComboBox或TextBox中动态更改自动完成条目? 但我仍然找不到解决方案。
问题简述:
我有一个 ComboBox 和大量记录要显示在其中。当用户开始输入时,我想加载以输入文本开头的记录,并为用户提供自动完成选项。 如上所述,在 сomboBox_TextChanged 中无法加载它们,因为我始终覆盖先前的结果并从未看到它们。 我能否仅使用 ComboBox 实现此功能?(而不使用 TextBox 或 ListBox)
我使用以下设置:
сomboBox.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
сomboBox.AutoCompleteSource = AutoCompleteSource.CustomSource;

7
嗯...它在WinForm上。 - algreat
抱歉,我太专注于Web了,但概念是相同的。有一个监听按键的事件并调用一个方法,该方法接受字符串并返回结果集。 - Brian
1
问题在于没有必要的事件。我需要像comboBox_TextChanging这样的东西。我尝试使用comboBox_TextUpdate和comboBox_KeyDown,但两者都有不同的问题。 - algreat
11个回答

-2

第二次尝试。我的下面的答案没有完全达到期望的结果,但它可能仍然对某人有用。ComboBox的自动选择功能给我带来了很大的痛苦。这个方法使用一个放置在ComboBox上方的TextBox,允许我忽略ComboBox本身中出现的任何内容,并只响应选择更改事件。

  1. 创建表单
  2. 添加ComboBox
    • 设置所需的大小和位置
    • 将DropDownStyle设置为DropDown
    • 将TabStop设置为false
    • 将DisplayMember设置为Value(我正在使用KeyValuePair列表)
    • 将ValueMember设置为Key
  3. 添加面板
    • 设置与ComboBox相同的大小
    • 用面板覆盖ComboBox(这解释了标准ComboBox比标准TextBox更高的原因)
  4. 添加文本框
    • 将文本框放置在面板的上方
    • 将文本框的底部与面板/ ComboBox的底部对齐

代码后台

public partial class TestForm : Form
{
    // Custom class for managing calls to an external address finder service
    private readonly AddressFinder _addressFinder;

    // Events for handling async calls to address finder service
    private readonly AddressSuggestionsUpdatedEventHandler _addressSuggestionsUpdated;
    private delegate void AddressSuggestionsUpdatedEventHandler(object sender, AddressSuggestionsUpdatedEventArgs e);

    public TestForm()
    {
        InitializeComponent();

        _addressFinder = new AddressFinder(new AddressFinderConfigurationProvider());
        _addressSuggestionsUpdated += AddressSuggestions_Updated;
    }

    private void textBox1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
    {
        if (e.KeyCode == Keys.Tab)
        {
            comboBox1_SelectionChangeCommitted(sender, e);
            comboBox1.DroppedDown = false;
        }
    }

    private void textBox1_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Up)
        {
            if (comboBox1.Items.Count > 0)
            {
                if (comboBox1.SelectedIndex > 0)
                {
                    comboBox1.SelectedIndex--;
                }
            }

            e.Handled = true;
        }
        else if (e.KeyCode == Keys.Down)
        {
            if (comboBox1.Items.Count > 0)
            {
                if (comboBox1.SelectedIndex < comboBox1.Items.Count - 1)
                {
                    comboBox1.SelectedIndex++;
                }
            }

            e.Handled = true;
        }
        else if (e.KeyCode == Keys.Enter)
        {
            comboBox1_SelectionChangeCommitted(sender, e);
            comboBox1.DroppedDown = false;

            textBox1.SelectionStart = textBox1.TextLength;

            e.Handled = true;
        }
    }

    private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
    {
        if (e.KeyChar == '\r')  // Enter key
        {
            e.Handled = true;
            return;
        }

        if (char.IsControl(e.KeyChar) && e.KeyChar != '\b') // Backspace key
        {
            return;
        }

        if (textBox1.Text.Length > 1)
        {
            Task.Run(() => GetAddressSuggestions(textBox1.Text));
        }
    }

    private void comboBox1_SelectionChangeCommitted(object sender, EventArgs e)
    {
        if (comboBox1.Items.Count > 0 &&
            comboBox1.SelectedItem.IsNotNull() &&
            comboBox1.SelectedItem is KeyValuePair<string, string>)
        {
            var selectedItem = (KeyValuePair<string, string>)comboBox1.SelectedItem;

            textBox1.Text = selectedItem.Value;

            // Do Work with selectedItem
        }
    }

    private async Task GetAddressSuggestions(string searchString)
    {
        var addressSuggestions = await _addressFinder.CompleteAsync(searchString).ConfigureAwait(false);

        if (_addressSuggestionsUpdated.IsNotNull())
        {
            _addressSuggestionsUpdated.Invoke(this, new AddressSuggestionsUpdatedEventArgs(addressSuggestions));
        }
    }

    private void AddressSuggestions_Updated(object sender, AddressSuggestionsUpdatedEventArgs eventArgs)
    {
        try
        {
            ThreadingHelper.BeginUpdate(comboBox1);

            ThreadingHelper.ClearItems(comboBox1);

            if (eventArgs.AddressSuggestions.Count > 0)
            {
                foreach (var addressSuggestion in eventArgs.AddressSuggestions)
                {
                    var item = new KeyValuePair<string, string>(addressSuggestion.Key, addressSuggestion.Value.ToUpper());
                    ThreadingHelper.AddItem(comboBox1, item);
                }

                ThreadingHelper.SetDroppedDown(comboBox1, true);
                ThreadingHelper.SetVisible(comboBox1, true);
            }
            else
            {
                ThreadingHelper.SetDroppedDown(comboBox1, false);
            }
        }
        finally
        {
            ThreadingHelper.EndUpdate(comboBox1);
        }
    }

    private class AddressSuggestionsUpdatedEventArgs : EventArgs
    {
        public IList<KeyValuePair<string, string>> AddressSuggestions { get; }

        public AddressSuggestionsUpdatedEventArgs(IList<KeyValuePair<string, string>> addressSuggestions)
        {
            AddressSuggestions = addressSuggestions;
        }
    }
}

您可能会遇到设置ComboBox的DroppedDown属性时出现问题。我最终只是将其包装在try块中,并使用空catch块。这不是一个很好的解决方案,但它可以工作。

请查看下面我的其他答案,了解有关ThreadingHelpers的信息。

祝愉快。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接