WPF:ComboBox的下拉菜单突出显示文本

15

当我在组合框中输入时,自动打开并启用下拉列表

searchComboBox.IsDropDownOpen = true;

这里的问题是 - 文本被高亮显示,下一个按键会覆盖先前的文本。

我如何在 ComboBox 下拉框打开时禁用文本高亮显示?


你是指哪一个“下拉框组合框”? - Trainee4Life
3
你是否将comboBox的IsTextSearchEnabled属性设置为false了?这会禁用文本选择功能。希望这能帮到你。 - Asim Sajjad
7个回答

9

我曾经遇到了这个问题,和一些新手一样,在WPF方面挣扎了很久才理解Einar Guðsteinsson提供的解决方案。因此,为了支持他的答案,我在这里贴出了实现这个功能的步骤(或者更准确地说,是我是如何实现的)。

首先创建一个自定义的ComboBox类,该类继承自ComboBox类。请参见下面的代码以获取完整的实现方式。您可以更改OnDropSelectionChanged中的代码以满足您的个人要求。

namespace MyCustomComboBoxApp { using System.Windows.Controls;

public class MyCustomComboBox : ComboBox
{
    private int caretPosition;

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        var element = GetTemplateChild("PART_EditableTextBox");
        if (element != null)
        {
            var textBox = (TextBox)element;
            textBox.SelectionChanged += OnDropSelectionChanged;
        }
    }

    private void OnDropSelectionChanged(object sender, System.Windows.RoutedEventArgs e)
    {
        TextBox txt = (TextBox)sender;
        
        if (base.IsDropDownOpen && txt.SelectionLength > 0)
        {
            caretPosition = txt.SelectionLength; // caretPosition must be set to TextBox's SelectionLength
            txt.CaretIndex = caretPosition;
        }
        if (txt.SelectionLength == 0 && txt.CaretIndex != 0)
        {
            caretPosition = txt.CaretIndex;
        }
    }

}

确保这个自定义组合类存在于同一个项目中。然后您可以使用下面的代码来在您的用户界面中引用此组合。

<Window x:Class="MyCustomComboBoxApp.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:cc="clr-namespace:MyCustomComboBoxApp"
    Title="MainWindow" Height="350" Width="525" FocusManager.FocusedElement="{Binding ElementName=cb}">
<Grid>
    <StackPanel Orientation="Vertical">
        <cc:MyCustomComboBox x:Name="cb" IsEditable="True" Height="20" Margin="10" IsTextSearchEnabled="False" KeyUp="cb_KeyUp">
            <ComboBoxItem>Toyota</ComboBoxItem>
            <ComboBoxItem>Honda</ComboBoxItem>
            <ComboBoxItem>Suzuki</ComboBoxItem>
            <ComboBoxItem>Vauxhall</ComboBoxItem>
        </cc:MyCustomComboBox>
    </StackPanel>
</Grid>
</Window>

就是这样了!有任何问题,请问我!我会尽力帮助。

感谢Einar Guðsteinsson提供的解决方案!


在我的改进方面:必须将caretPosition设置为TextBox的SelectionLength。 - PeterPazmandi

8

迟做总比不做好,如果其他人遇到了这个问题,他们可能会用到这个解决方法。

如果你重写combobox,就有办法解决这个问题。首先获取在模板中使用的文本框的句柄,并注册selectionchanged事件。

public override void OnApplyTemplate()
{
  base.OnApplyTemplate();

  var element = GetTemplateChild("PART_EditableTextBox");
  if (element != null)
  {
    var textBox = (TextBox)element;
    textBox.SelectionChanged += OnDropSelectionChanged;
  }
}

private void OnDropSelectionChanged(object sender, RoutedEventArgs e)
{
    // Your code
}

然后在事件处理程序中,您可以根据自己的需要重新设置选择。在我的情况下,我在代码中调用了IsDropDownOpen。在此保存了所选内容,然后在事件处理程序中重新设置回去。这样做并不美观,但确实起到了作用。


请问您能否写出完整的代码?我遇到了完全相同的问题,但是我对此还很陌生,请详细解释一下。我的元素不是文本框,而是组合框。 - flexxxit
请问您能否在textbox_SelectionChanged中发布一个示例代码?就像这样吗? TextBox tb = (TextBox)e.Source; if (tb != null) { tb.SelectionStart = 0; } - Jayson Ragasa

4

根据clsturgeon的回答,我通过在DropDownOpened事件发生时设置选择来解决了这个问题:

private void ItemCBox_DropDownOpened(object sender, EventArgs e)
{
    TextBox textBox = (TextBox)((ComboBox)sender).Template.FindName("PART_EditableTextBox", (ComboBox)sender);
    textBox.SelectionStart = ((ComboBox)sender).Text.Length;
    textBox.SelectionLength = 0;
}

1
它有效了,这是我找到的最短的解决方案,谢谢:D - Kreshnik
1
如果插入符号不在文本末尾,这个方法就无法工作。 - user487779

3

我认为安德鲁·N提供的解决方案中缺少了一些东西,当我尝试它时,文本框的Selection Changed事件会把插入符号放在错误的位置。因此,我做了这个更改来解决这个问题。

namespace MyCustomComboBoxApp { using System.Windows.Controls;

public class MyCustomComboBox : ComboBox
{
    private int caretPosition;

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        var element = GetTemplateChild("PART_EditableTextBox");
        if (element != null)
        {
            var textBox = (TextBox)element;
            textBox.SelectionChanged += OnDropSelectionChanged;
        }
    }

    private void OnDropSelectionChanged(object sender, System.Windows.RoutedEventArgs e)
    {
        TextBox txt = (TextBox)sender;

        if (base.IsDropDownOpen && txt.SelectionLength > 0)
        {
            caretPosition = txt.SelectionLength; // caretPosition must be set to TextBox's SelectionLength
            txt.CaretIndex = caretPosition;
        }
        if (txt.SelectionLength == 0 && txt.CaretIndex != 0)
        {
            caretPosition = txt.CaretIndex;
        }
    }
}

干得好,修复得很棒,Mohammed! - Andrew N
第二个if语句除了设置没被使用的caretPosition变量之外什么也不做。移除它以及该变量,然后将第一个if语句中的赋值合并为一行 - txt.CaretIndex = txt.SelectionLength。 - user487779

0
当一个组合框获得焦点时,您可以禁用文本高亮(即在GotFocus事件中选择无文本)。 但是,当您下拉组合框时,系统将定位列表中的项目并将其设置为选定项目。 这反过来会自动突出显示文本。 如果我理解您要寻找的行为,我不认为它完全可能。

你是对的吗?即使我像Asim提到的那样将IsTextSearchEnabled属性设置为false,似乎也不可能。 - Panks

0

一种替代方案。防止框架干扰选择。

public class ReflectionPreventSelectAllOnDropDown
{
    private static readonly PropertyInfo edtbPropertyInfo;
    static ReflectionPreventSelectAllOnDropDown()
    {
        edtbPropertyInfo = typeof(ComboBox).GetProperty("EditableTextBoxSite", BindingFlags.NonPublic | BindingFlags.Instance);
    }
    public void DropDown(ComboBox comboBox)
    {
        if (!comboBox.IsDropDownOpen)
        {
            var edtb = edtbPropertyInfo.GetValue(comboBox);
            edtbPropertyInfo.SetValue(comboBox, null);
            comboBox.IsDropDownOpen = true;
            edtbPropertyInfo.SetValue(comboBox, edtb);
        }
    }
    
}

0

我使用了Jun Xie的修改答案来解决这个问题。假设您正在使用keyUp事件进行组合框搜索,我在我的自定义用例中发现了一个边缘情况,它仍然会覆盖文本:

  • 第一次在组合框中输入。文本没问题。
  • 使用上下箭头键选择列表中的项目,但不“提交”更改(例如按Enter并关闭下拉选择)。请注意此时文本已突出显示,如clsturgeon所指出的那样。
  • 再次尝试在文本框中输入。在这种情况下,文本将被覆盖,因为下拉列表仍然处于打开状态,因此事件从未触发以清除突出显示。

解决方案是检查是否选择了项目。以下是可行的代码: XAML:

<ComboBox x:Name="SearchBox" IsEditable="True" KeyUp="SearchBox_KeyUp" 
          PreviewMouseDown="SearchBox_PreviewMouseDown" IsTextSearchEnabled="False"
          DropDownOpened="SearchBox_DropDownOpened">
</ComboBox>

代码:

private void SearchBox_KeyUp(object sender, KeyEventArgs e)
{
    SearchBox.IsDropDownOpen = true;
    if (e.Key == Key.Down || e.Key == Key.Up)
    {
        e.Handled = true;

        //if trying to navigate but there's noting selected, then select one
        if(SearchBox.Items.Count > 0 && SearchBox.SelectedIndex == -1)
        {
            SearchBox.SelectedIndex = 0;
        }
    }
    else if (e.Key == Key.Enter)
    {
        //commit to selection
    }
    else if (string.IsNullOrWhiteSpace(SearchBox.Text))
    {
        SearchBox.Items.Clear();
        SearchBox.IsDropDownOpen = false;
        SearchBox.SelectedIndex = -1;
    }
    else if (SearchBox.Text.Length > 1)
    {
        //if something is currently selected, then changing the selected index later will loose
        //focus on textbox part of combobox and cause the text to
        //highlight in the middle of typing. this will "eat" the first letter or two of the user's search
        if(SearchBox.SelectedIndex != -1)
        {
            TextBox textBox = (TextBox)((ComboBox)sender).Template.FindName("PART_EditableTextBox", (ComboBox)sender);
            //backup what the user was typing
            string temp = SearchBox.Text;
            //set the selected index to nothing. sets focus to dropdown
            SearchBox.SelectedIndex = -1;
            //restore the text. sets focus and highlights the combobox text
            SearchBox.Text = temp;
            //set the selection to the end (remove selection)
            textBox.SelectionStart = ((ComboBox)sender).Text.Length;
            textBox.SelectionLength = 0;
        }

        //your search logic
    }
}

private void SearchBox_DropDownOpened(object sender, EventArgs e)
{
    TextBox textBox = (TextBox)((ComboBox)sender).Template.FindName("PART_EditableTextBox", (ComboBox)sender);
    textBox.SelectionStart = ((ComboBox)sender).Text.Length;
    textBox.SelectionLength = 0;
}

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