WPF 的自动完成组合框 [英] Autocomplete combobox for WPF

查看:17
本文介绍了WPF 的自动完成组合框的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要一个用于 WPF C# 的自动完成组合框.我尝试了几种方法,但没有任何效果.例如我试过一个组合框:

I need an autocomplete combobox for WPF C#. I've tried several approaches but nothing works. For example I've tried a combobox:

<ComboBox  Width="200"
      IsEditable="True"
      ItemsSource="{Binding Names}"
      IsTextSearchEnabled="True"
      HorizontalAlignment="Left"/>

Names 是一个字符串列表:Peter John、John、John Doe、Cathy、Howard、John Richards 等等

Names is a List of Strings: Peter John, John, John Doe, Cathy, Howard, John Richards and so on

如果您输入名称,例如约翰组合框应该展开,我应该看到

If you type in a name e.g. John the combobox should expand and I should see

  • 约翰
  • 约翰·多伊
  • 约翰·理查兹
  • 彼得约翰

但这行不通.我该怎么做?

But that doesn't work. How can I do that?

推荐答案

经过一番折腾,我终于找到了一个完整的、有效的解决方案.(或者看起来是这样.)

After a lot of fiddling, I have managed to arrive at a complete, working solution. (Or so it seems.)

你需要像这样修改你的组合框:

You need to modify your ComboBox like so:

<ComboBox
    ...
    IsTextSearchEnabled="False"
    ...
    PreviewTextInput="PreviewTextInput_EnhanceComboSearch"
    PreviewKeyUp="PreviewKeyUp_EnhanceComboSearch"
    DataObject.Pasting="Pasting_EnhanceComboSearch" />

即.禁用默认文本搜索,并添加处理用户添加、删除和粘贴文本的事件处理程序.

ie. to disable default text search, and add events handlers that will take care of user adding, deleting and pasting text.

为了让 PreviewTextInput_EnhanceComboSearchPasting_EnhanceComboSearch 正常工作,您需要访问 ComboBox 的插入符号.不幸的是,要做到这一点,你需要遍历,呃,可视化树(给马特·汉密尔顿的提示).您可以在扩展方法中执行此操作,但我在 Page 类中使用了静态方法:

In order for PreviewTextInput_EnhanceComboSearch and Pasting_EnhanceComboSearch to work at all, you will need to access your ComboBox's caret. Unfortunately, to do this, you need to traverse, er, visual tree (hat tip to Matt Hamilton). You can do that in an extension method, but I used a static one in my Page class:

public static T GetChildOfType<T>(DependencyObject depObj) where T : DependencyObject
{
    if (depObj == null) return null;

    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
    {
        var child = VisualTreeHelper.GetChild(depObj, i);

        var result = (child as T) ?? GetChildOfType<T>(child);
        if (result != null) return result;
    }
    return null;
}

步骤 3. 实现事件处理程序

请注意我用过

s => s.IndexOf(e.Text, StringComparison.InvariantCultureIgnoreCase) != -1 

等价于不区分大小写的 s =>s.Contains(e.Text) 检查.请记住更改该部分以满足您的需要.

which is equivalent to case-insensitive s => s.Contains(e.Text) check. Remember to change that part to suit your needs.

当运行 PreviewTextInput 处理程序时,ComboBox 内的 .Text 属性包含修改前的文本.因此,我们需要使用GetChildOfType 方法获取ComboBox 的内部TextBox 来获取它的插入符,这样我们就知道输入的字符到底插入到哪里了.

When a PreviewTextInput handler is run, the .Text property inside the ComboBox contains the text from before it was modified. Therefore, we need to get ComboBox's internal TextBox using GetChildOfType method in order to obtain its caret, so we know where exactly was the typed character inserted.

private void PreviewTextInput_EnhanceComboSearch(object sender, TextCompositionEventArgs e)
{
    ComboBox cmb = (ComboBox)sender;

    cmb.IsDropDownOpen = true;

    if (!string.IsNullOrEmpty(cmb.Text))
    {
        string fullText = cmb.Text.Insert(GetChildOfType<TextBox>(cmb).CaretIndex, e.Text);
        cmb.ItemsSource = Names.Where(s => s.IndexOf(fullText, StringComparison.InvariantCultureIgnoreCase) != -1).ToList();
    }
    else if (!string.IsNullOrEmpty(e.Text))
    {
        cmb.ItemsSource = Names.Where(s => s.IndexOf(e.Text, StringComparison.InvariantCultureIgnoreCase) != -1).ToList();
    }
    else
    {
        cmb.ItemsSource = Names;
    }
}

步骤 3.b 在用户粘贴到 ComboBox 时触发搜索

DataObject.Pasting 处理程序的行为方式与 PreviewTextInput 处理程序类似,因此我们再次需要插入符号.

Step 3.b Trigger search on user pasting into ComboBox

DataObject.Pasting handler behaves in a similar fashion to PreviewTextInput hanlder, so we need the caret again.

private void Pasting_EnhanceComboSearch(object sender, DataObjectPastingEventArgs e)
{
    ComboBox cmb = (ComboBox)sender;

    cmb.IsDropDownOpen = true;

    string pastedText = (string)e.DataObject.GetData(typeof(string));
    string fullText = cmb.Text.Insert(GetChildOfType<TextBox>(cmb).CaretIndex, pastedText);

    if (!string.IsNullOrEmpty(fullText))
    {
        cmb.ItemsSource = Names.Where(s => s.IndexOf(fullText, StringComparison.InvariantCultureIgnoreCase) != -1).ToList();
    }
    else
    {
        cmb.ItemsSource = Names;
    }
}

步骤 3.c 在用户删除 ComboBox 内的文本时触发搜索(并且还按空格,因为 WPF)

这将在用户按下 Delete 或 Backspace 时触发.

Step 3.c Trigger search on user deleting text inside ComboBox (and also pressing Space, because WPF)

This will trigger when the user depresses either Delete or Backspace.

还有空格,因为空格被 PreviewTextInput 忽略,所以在示例中很难从John Doe"和John Richards"中过滤掉John".

And also Space, because Space is ignored by PreviewTextInput, so it would be difficult to filter out "John" from "John Doe" and "John Richards" in the example.

private void PreviewKeyUp_EnhanceComboSearch(object sender, KeyEventArgs e)
{
    if (e.Key == Key.Back || e.Key == Key.Delete)
    {
        ComboBox cmb = (ComboBox)sender;

        cmb.IsDropDownOpen = true;

        if (!string.IsNullOrEmpty(cmb.Text))
        {
            cmb.ItemsSource = Names.Where(s => s.IndexOf(cmb.Text, StringComparison.InvariantCultureIgnoreCase) != -1).ToList();
        }
        else
        {
            cmb.ItemsSource = Names;
        }
    }
}

<小时>

...这应该足够了.


...and that should probably be enough.

这篇关于WPF 的自动完成组合框的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆