【问题标题】:Combobox select item only by mouse or Enter key组合框仅通过鼠标或 Enter 键选择项目
【发布时间】:2011-12-05 06:41:03
【问题描述】:

我有一个 WPF ComboBox
我需要更改弹出列表中的默认行为。

现在,按updownSelectedItem 会自动更改。
我只需按Enter 键或鼠标单击即可更改SelectedItem

怎么做?

我已经继承了ComboBox:

protected override void OnPreviewKeyDown(System.Windows.Input.KeyEventArgs e)
{
    Debug.Write("Pressed " + e.Key+ " ");
    if (e.Key == System.Windows.Input.Key.Up || e.Key == System.Windows.Input.Key.Down)
    {
        // ???
        e.Handled = true;
        return;
    }
    base.OnPreviewKeyDown(e);
}

此代码不起作用 - 不显示弹出窗口并且用户无法选择项目。 我应该写什么,在哪里写? :)

谢谢。

UPD1:

我需要与ComboBox's 弹出窗口相同的功能,并且用户可以通过鼠标选择项目。
每个项目都可以用鼠标悬停,但不能被选中。选择成为仅通过按下鼠标按钮。我需要同样的。 “向上”和“向下”仅突出显示弹出窗口中的项目,但 SelectedItem 将仅通过按 Enter 或单击鼠标来更改。

UPD2: 如果我通过鼠标按下按钮,在 ComboCox 中打开 Popup,我可以通过鼠标突出显示 Popup 中的项目,但 SelectedItem 只有在我单击项目时才会更改。

我需要通过键盘实现相同的功能。如果我开始在 ComboBox 中输入一些内容,则会打开弹出窗口。我必须通过键盘UpDown 突出显示项目。 ComboBox 中的 TextBox 在突出显示期间不得更改,并且 SelectedItem 只有在我按下 Enter(或鼠标单击)时才能更改

UPD3: 演示解决方案链接:download

【问题讨论】:

    标签: wpf combobox


    【解决方案1】:

    安装一个 nuget 包System.Windows.Interactivity.WPF,创建一个如下所示的类(注意:左/右箭头也可以更改闭合焦点组合框中的选择):

    public class ComboBoxCustomBehaviour: Behavior<ComboBox>
    {
        private readonly ISet<Key> _dropdownBlockedKeys = new HashSet<Key>{Key.Up, Key.Down, Key.Left, Key.Right};
    
        protected override void OnAttached()
        {
            AssociatedObject.PreviewKeyDown += AssociatedObject_KeyDown;
        }
    
        protected override void OnDetaching()
        {
            AssociatedObject.PreviewKeyDown -= AssociatedObject_KeyDown;
        }
    
        private void AssociatedObject_KeyDown(object sender, KeyEventArgs e)
        {
            if (_dropdownBlockedKeys.Contains(e.Key))
                e.Handled = true;
            // Use following line, when you need to stop selection only on closed ComboBox
            // e.Handled = !((ComboBox)sender).IsDropDownOpen;
        }
    }
    

    将此行为类添加到 xaml:

    <ComboBox>
        <ComboBoxItem>Item 1</ComboBoxItem>
        <ComboBoxItem>Item 2</ComboBoxItem>
        <ComboBoxItem>Item 3</ComboBoxItem>
        <i:Interaction.Behaviors>
            <local:ComboBoxCustomBehaviour />
        </i:Interaction.Behaviors>
    </ComboBox>
    

    【讨论】:

      【解决方案2】:

      最好使用 ComboBox。OnDropDownClosed(EventArgs) 方法

      https://docs.microsoft.com/de-de/dotnet/api/system.windows.forms.combobox.ondropdownclosed?view=netframework-4.7.2#System_Windows_Forms_ComboBox_OnDropDownClosed_System_EventArgs_

      PS:对于 Infracistics 控件 (UltraComboEditor),它是 OnAfterCloseUp 事件。

      【讨论】:

        【解决方案3】:

        您的代码似乎工作正常,只需在取消按键事件之前添加一个检查以查看 DropDown 是否打开

        protected override void OnPreviewKeyDown(KeyEventArgs e)
        {
            Debug.Write("Pressed " + e.Key + " ");
            if (!base.IsDropDownOpen && (e.Key == Key.Up || e.Key == Key.Down))
            {
                e.Handled = true;
                return;
            }
            base.OnPreviewKeyDown(e);
        }
        

        【讨论】:

        • @Lari 请参阅此问题:stackoverflow.com/q/7145876/302677 建议更改 ComboBox 选择项目的行为,以便 SelectedItem 只有在 ComboBox 的 DropDown 关闭时才会更改。如果您想在 DropDown 关闭并按下箭头键时保持选择行为,那么您还可以处理 Key 事件以更新 SelectedItem
        【解决方案4】:

        这是对我有用的解决方案 -

        public class CustomComboBox : ComboBox
        {
            private int _currentItemIndex;
            private string _rowColor = "#E7E7E7";
            private string _selectedRowColor = "#FFFFC6";        
        
            protected override void OnDropDownOpened(EventArgs e)
            {
                _currentItemIndex = base.SelectedIndex;
                base.OnDropDownOpened(e);
            }
        
            protected override void OnPreviewKeyDown(KeyEventArgs e)
            {
                if (base.IsDropDownOpen)
                {
                    if (e.Key == Key.Up || e.Key == Key.Down)
                    {
                        ComboBoxItem currentItem;
                        var colorConverter = new BrushConverter();
        
                        if (_currentItemIndex > -1 && _currentItemIndex != base.SelectedIndex)
                        {
                            currentItem = (ComboBoxItem)base.ItemContainerGenerator.ContainerFromIndex(_currentItemIndex);
                            currentItem.Background = (Brush)colorConverter.ConvertFromString(_rowColor);
                        }
        
                        if (e.Key == Key.Up)
                        {
                            _currentItemIndex -= 1;
                            if (_currentItemIndex < 0)
                            {
                                _currentItemIndex = 0;
                            }
                        }
                        else if (e.Key == Key.Down)
                        {
                            _currentItemIndex += 1;
                            if (_currentItemIndex > base.Items.Count - 1)
                            {
                                _currentItemIndex = base.Items.Count - 1;
                            }
                        }
        
                        currentItem = (ComboBoxItem)base.ItemContainerGenerator.ContainerFromIndex(_currentItemIndex);
                        currentItem.Background = (Brush)colorConverter.ConvertFromString(_selectedRowColor);
                        currentItem.BringIntoView();
        
                        e.Handled = true;
                        return;
                    }
                    else if (e.Key == Key.Enter)
                    {
                        base.SelectedItem = base.Items[_currentItemIndex];
                    }
                }
                base.OnPreviewKeyDown(e);
            }
        
            protected override void OnDropDownClosed(EventArgs e)
            {
                if (_currentItemIndex > -1 && base.Items[_currentItemIndex] != base.SelectedItem)
                {
                    var colorConverter = new BrushConverter();
                    ComboBoxItem currentItem = (ComboBoxItem)base.ItemContainerGenerator.ContainerFromIndex(_currentItemIndex);
                    currentItem.Background = (Brush)colorConverter.ConvertFromString(_rowColor);
                }
                base.OnDropDownClosed(e);
            }
        }
        

        【讨论】:

          【解决方案5】:

          在我看来,您应该创建属性 - IsKeyNavigation(如 IsMouseOver)

          protected override void OnPreviewKeyDown(System.Windows.Input.KeyEventArgs e)
          {
              Debug.Write("Pressed " + e.Key+ " ");
              if (e.Key == System.Windows.Input.Key.Up || e.Key == System.Windows.Input.Key.Down)
              {
                  VisualStateManager.GoToState(this, "KeyNavigation", true);
                  e.Handled = true;
                  return;
              }
              base.OnPreviewKeyDown(e);
          }
          

          如果 Key.Up 或 Key.Down 被按下,您应该定义导航元素并更改 VisualState。

          【讨论】:

            【解决方案6】:

            您应该在组合框中的所有ComboBoxItems 上处理此事件。

               <ComboBox.Resources>
                    <Style TargetType="{x:Type ComboBoxItem}">
                       <EventSetter Event="PreviewKeyDown" Handler="OnPreviewKeyDown" />
                    </Style> 
              </ComboBox.Resources>
            

            编辑:

            在后面的代码中,您可以在 InitializeComponent() 之后在 MyComboBox 的构造函数中添加以下代码...

              var comboBoxItemstyle = new Style(typeof (ComboBoxItem));  
              comboBoxItemstyle.Setters.Add(
                    new EventSetter(PreviewKeyDownEvent,
                            new KeyEventHandler(OnPreviewKeyDown)));
              this.Resources.Add(typeof (ComboBoxItem), comboBoxItemstyle);
            

            希望这会有所帮助。

            【讨论】:

            • 我必须从子类 MyComboBox 中进行,而不是在 XAML 中。有可能吗?
            • 我为您提供了 XAML,您的处理程序可以在您的 xaml.cs 文件中,或者如果您使用 MVVm,则通过附加行为执行此操作。
            • 我没有 XAML。我只有MyComboBox : ComboBox 类,所以我不需要附加行为,因为我已经inside ComboBox :)
            • 但是您可以使用this.resources.Add() 以编程方式将ComboboxItem 样式添加到您的MyComboBox.Resources 中@ 不能吗?
            • 您能提供一个代码示例吗?我不明白怎么做。您是否愿意在某处使用代码隐藏编写 XAML 代码,然后将其作为资源添加到 MyComboBox 中?我不认为这是一个好主意。我敢肯定,还有其他解决方案。
            猜你喜欢
            • 2012-04-28
            • 1970-01-01
            • 1970-01-01
            • 2015-11-06
            • 1970-01-01
            • 1970-01-01
            • 2011-01-13
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多