【问题标题】:ComboBox Filtering with IsTextSearchable Off Clears Text使用 IsTextSearchable Off 进行组合框过滤清除文本
【发布时间】:2018-05-12 11:54:48
【问题描述】:

我在 WPF 表单中使用了一个 ComboBox,它似乎表现出奇怪的行为,在选择下拉项后立即从 ComboBox 中清除文本,然后在 ComboBox 中键入文本。

首先了解一些背景信息,我的 ComboBox 包含一个 ComboBoxItem 列表,其中 Content 属性设置为各种字符串值(在我的示例应用程序中,我使用了水果名称)。 ComboBox 本身的所有属性都设置为默认值,除了 IsEditable 属性(设置为 true)和 IsTextSearchEnabled 属性(设置为 false)。

<ComboBox x:Name="comboBox" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120" Margin="121,100,0,0" IsEditable="True" KeyUp="comboBox_KeyUp" IsTextSearchEnabled="False">
    <ComboBoxItem Content="apple"/>
    <ComboBoxItem Content="banana"/>
    <ComboBoxItem Content="grape"/>
    <ComboBoxItem Content="lemon"/>
    <ComboBoxItem Content="strawberry"/>
</ComboBox>

我还使用了在组合框的 KeyUp 事件期间触发的基本过滤器:

private void comboBox_KeyUp(object sender, KeyEventArgs e)
{
    var combobox = (ComboBox)sender;
    combobox.IsDropDownOpen = true;

    CollectionView itemsViewOriginal = (CollectionView)CollectionViewSource.GetDefaultView(combobox.Items);
    itemsViewOriginal.Filter = ((o) =>
    {
        if (String.IsNullOrEmpty(combobox.Text))
        {
            return true;
        }
        else
        {
            if (((ComboBoxItem)o).Content.ToString().StartsWith(combobox.Text, true, null))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    });

    itemsViewOriginal.Refresh();
}

当我从下拉列表中选择一个项目,单击组合框以删除突出显示并将焦点放在单词的末尾,然后键入时,会出现此问题。只要我输入一个字母,文本就会立即被删除,我输入的任何新文本都会重新启动过滤器。我希望新文本简单地附加到组合框中的现有文本,但这里不是这种情况。

有趣的是,我可以通过两种方式解决这个问题:

a) 重新启用“IsTextSearchEnabled”。但是,出于各种原因,我不希望启用此属性,主要是行为与客户端期望控件的行为方式不一致。

b) 修改过滤器以使用静态值。

如果我将过滤器修改为:

if (((ComboBoxItem)o).Content.ToString().StartsWith(combobox.Text, true, null))

到:

if (((ComboBoxItem)o).Content.ToString().StartsWith("ba", true, null))

然后清除文本行为不再发生。但是,这显然对我没有帮助,因为我想使用 ComboBox 的文本作为列表的过滤器。

在这种情况下,我可以做些什么来防止文本从控件中删除,同时仍保持过滤和一般控件行为?

【问题讨论】:

  • 过滤Items 集合是自找麻烦。 ComboBox 尝试将所选项目与Text 属性同步。当您过滤Items 使其实际上为空时,组合框显然会认为,“哦,没有可能的选择,所以我当前的选择不可能有效。我会继续清除它以及我的文本。 " ComboBox 可能不是用于这种情况的最佳控件。我建议你寻找一个可以使用自动完成的第三方文本控件。
  • 我能问一下你为什么不使用任何绑定吗? WPF 最常与the MVVM pattern 一起使用,它也可以解决很多类似的问题

标签: c# wpf filter combobox


【解决方案1】:

我真的不明白为什么你不能使用内置的搜索功能。对于发布的示例IsTextSearchEnabled="True"StaysOpenOnEdit="True" 应该得到所需的结果。但我猜你有你的理由,可能不想分享你的确切代码。

当您第一次聚焦 ComboBox 并开始输入时,没有选中任何内容,您只需过滤项目列表。因此,在组合框失去焦点或实际提交他选择的值之前,一切都运行良好。
当您选择一个项目时,ComboBox 使用SelectedItem 属性并将其绑定到选定的 ComboBoxItem。如果文本随后被更改并且不再与当前选定项目的匹配,则组合框将删除选定项目,实际上将其设置为空,从而清除文本。这可以通过将SelectedValue 声明为组合框的实际文本属性来避免。要让下拉列表仍然正确影响文本,您还必须定义 SelectedValuePath 属性。

<ComboBox x:Name="comboBox" HorizontalAlignment="Left" SelectedValuePath="Content" SelectedValue="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Text}" VerticalAlignment="Top" Width="120" Margin="121,100,0,0"  IsEditable="True" KeyUp="comboBox_KeyUp" IsTextSearchEnabled="False">
    <ComboBoxItem Content="apple"/>
    <ComboBoxItem Content="banana"/>
    <ComboBoxItem Content="grape"/>
    <ComboBoxItem Content="lemon"/>
    <ComboBoxItem Content="strawberry"/>
</ComboBox>

不幸的是,仅此一项似乎还不够。这似乎解决了您提到的确切问题,因此当在当前选定值的末尾添加更多文本时,您将获得所需的结果。但是,当 ComboBox 被退格键清除时,对于具有单个匹配项的第一个字母,ComboBox 似乎仍然认为它已经完成了选择并选择了整个文本,下一次按键当然会删除您之前输入的内容。
但是,我可以通过将光标放在最后一个索引处来解决这个问题,这会撤消选择,现在应该完全按照预期工作。

private void comboBox_KeyUp(object sender, KeyEventArgs e)
{
    var combobox = (ComboBox)sender;
    var ctb = combobox.Template.FindName("PART_EditableTextBox", combobox) as TextBox;
    if (ctb == null) return;
    var caretPos = ctb.CaretIndex;
    combobox.IsDropDownOpen = true;

    CollectionView itemsViewOriginal = (CollectionView)CollectionViewSource.GetDefaultView(combobox.Items);
    itemsViewOriginal.Filter = ((o) =>
    {
        if (String.IsNullOrEmpty(combobox.Text))
        {
            return true;
        }
        else
        {
            if (((ComboBoxItem)o).Content.ToString().StartsWith(combobox.Text, true, null))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    });

    itemsViewOriginal.Refresh();
    ctb.CaretIndex = caretPos;
}

希望这可以帮助您解决当前的问题,我想补充一点,对于此类问题有更好的解决方案。它确实涉及实际的模型绑定,如果您还没有,那么您绝对应该研究一下。

这是一个用于 WPF 的自定义过滤组合框的示例,它比您当前制作的具有更多的可重用性。
Building a Filtered ComboBox for WPF


更新:我忘了检查修饰键,如 CtrlShiftAlt

private void comboBox_KeyUp(object sender, KeyEventArgs e)
{
    var combobox = (ComboBox)sender;
    var ctb = combobox.Template.FindName("PART_EditableTextBox", combobox) as TextBox;
    if (ctb == null) return;
    if (Keyboard.Modifiers.HasFlag(ModifierKeys.Shift) || Keyboard.Modifiers.HasFlag(ModifierKeys.Control) || Keyboard.Modifiers.HasFlag(ModifierKeys.Alt))
    return;
    var caretPos = ctb.CaretIndex;
    combobox.IsDropDownOpen = true;

    CollectionView itemsViewOriginal = (CollectionView)CollectionViewSource.GetDefaultView(combobox.Items);
    itemsViewOriginal.Filter = ((o) =>
    {
        if (String.IsNullOrEmpty(combobox.Text))
        {
            return true;
        }
        else
        {
            if (((ComboBoxItem)o).Content.ToString().StartsWith(combobox.Text, true, null))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    });

    itemsViewOriginal.Refresh();
    ctb.CaretIndex = caretPos;
}

【讨论】:

  • 这是一个很好的答案,它确实解决了问题。但是,导致光标被修改的行为使我无法使用“Ctrl + A”等快捷方式选择文本并立即取消选择文本。正如一些人所问的那样,我使用过滤器的原因是,我希望无效的下拉项目在用户输入框中时消失,并且应用过滤器似乎是最好的方法。
  • 我没有想到这一点,很好。我现在没有时间对此进行测试,但您可以检查修饰键并以这种方式转义事件。也许您最好使用KeyDownEvent
  • 我必须将修饰符条件更改为以下,并检查修饰键是否是被按下的键:if (ctb == null || (Keyboard.Modifiers.HasFlag(ModifierKeys.Shift) || Keyboard.Modifiers.HasFlag(ModifierKeys.Control) || Keyboard.Modifiers.HasFlag(ModifierKeys.Alt)) || (e.Key == Key.LeftShift || e.Key == Key.RightShift || e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl || e.Key == Key.LeftAlt || e.Key == Key.RightAlt)) { return; } 但这样做之后,看起来它就像我需要的那样工作!
猜你喜欢
  • 2016-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多