繁体   English   中英

自动完成 combobox 为 WPF

[英]Autocomplete combobox for WPF

我需要 WPF C# 的自动完成 combobox。我尝试了几种方法,但没有任何效果。 例如我试过 combobox:

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

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

如果你输入一个名字,例如 John,combobox 应该会展开,我应该会看到

  • 约翰
  • 无名氏
  • 约翰·理查兹
  • 彼得约翰

但这是行不通的。 我怎样才能做到这一点?

经过大量的摆弄,我设法找到了一个完整的、有效的解决方案。 (或者看起来是这样。)

步骤 1. 修改 XAML 标记

您需要像这样修改 ComboBox:

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

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

步骤 2. 添加一个辅助函数,它将获取 ComboBox 的内部 TextBox(因为 WPF)

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

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)检查。 请记住更改该部分以满足您的需要。

步骤 3.a 在用户在 ComboBox 内输入时触发搜索

PreviewTextInput处理程序运行时,ComboBox 内的.Text属性包含修改之前的文本。 因此,我们需要使用GetChildOfType方法获取 ComboBox 的内部 TextBox 以获取其插入符,以便我们知道键入的字符究竟插入到哪里。

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处理程序类似,因此我们再次需要插入符号。

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 时触发。

还有 Space,因为 Space 被PreviewTextInput忽略,因此在PreviewTextInput中很难从“John Doe”和“John Richards”中过滤掉“John”。

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

......这应该足够了。

使用 PreviewTextInput 事件进行过滤并显示如下下拉列表:

private void ComboBox_TextInput_1(object sender, TextCompositionEventArgs e)
    {           
        cmbperson.IsDropDownOpen = true;
        cmbperson.ItemsSource = DataBase.Persons.Where(p => p.Name.Contains(e.Text)).ToList();
    }

我建议您使用为自动完成制作的控件而不是组合框。 许多公司提供这样的控件,这个是免费的并且被认为是好的。

我为WPF创建了一个自动完成功能,可以帮助您。 按照下面的链接到 github: https : //github.com/rhpontes/AutocompleteWpf

我希望它能帮助你。

这是对我有用的实现:

<ComboBox
    Name="ItemsControl"
    IsEditable="True"
    KeyUp="OnItemsControlKeyUp"

我检查自上次应用过滤器后文本是否已更改(以避免在按下非字母数字键时进行过滤)。

private string _textBeforeFilter;

private void OnItemsControlKeyUp(object sender, KeyEventArgs e)
{
    var arrowKey = e.Key >= Key.Left && e.Key <= Key.Down;

    // if arrow key (navigating) or the text hasn't changed, then a we don't need to filter
    if (arrowKey || ItemsControl.Text.EqualsIgnoreCase(_textBeforeFilter)) return;

    _textBeforeFilter = ItemsControl.Text;

    var textIsEmpty = string.IsNullOrWhiteSpace(ItemsControl.Text);

    var itemsViewOriginal = (CollectionView) CollectionViewSource.GetDefaultView(ItemsControl.ItemsSource);
    // if the text is empty, then we show everything, otherwise filter based on the text 
    itemsViewOriginal.Filter = o => textIsEmpty || ((string) o).ContainsIgnoreCase(ItemsControl.Text);
}

注意: EqualsIgnoreCaseContainsIgnoreCase是扩展方法:

public static bool EqualsIgnoreCase(this string source, string value)
{
    return source.Equals(value, StringComparison.OrdinalIgnoreCase);
}

public static bool ContainsIgnoreCase(this string source, string value)
{
    return source.Contains(value, StringComparison.OrdinalIgnoreCase);
}

在 XAML 中,您应该设置IsEditable=True并为 PreviewKeyDown 事件添加处理程序:

private void ComboBox_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        var cmb = (ComboBox)sender;
        cmb.IsDropDownOpen = true;
        var textbox = cmb.Template.FindName("PART_EditableTextBox", cmb) as TextBox;
        cmb.ItemsSource = CurrentStorage.Organisations.Where(p => string.IsNullOrEmpty(cmb.Text) || p.Name.ToLower().Contains(textbox.Text.ToLower())).ToList();
    }

使用 ComboBox.Items.Filter 显示适合文本框中写入的文本的项目。 这是一个例子:

            If cmb.Text = "" Then
                cmb.Items.Filter = Nothing
            Else
                Dim T = cmb.Text
                cmb.Items.Filter = Nothing
                Dim I = cmb.Items.IndexOf(T)
                cmb.Items.Filter = Function(x As String)
                                       If x.StartsWith(T) Then Return True
                                       If x.Contains(" " & T) Then Return True
                                       Return False
                                   End Function

                If I = -1 Then
                    cmb.SelectedIndex = -1
                    cmb.Text = T
                    Dim txt As TextBox = cmb.Template.FindName("PART_EditableTextBox", cmb)
                    txt.SelectionStart = T.Length
                Else
                    cmb.SelectedIndex = 0
                End If

            End If

由于先前答案中提到的自动完成控件的链接不再处于活动状态,并且以我的拙见,使用预定义控件比重新发明自己的轮子更可行,这里是用于良好控件的链接 你安装它如下

Install-Package AutoCompleteTextBox

这是有关如何在代码中使用它的演示

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM