【问题标题】:WPF: How to programmatically remove focus from a TextBoxWPF:如何以编程方式从 TextBox 中移除焦点
【发布时间】:2011-02-24 06:18:52
【问题描述】:

我想向我的 WPF TextBox 添加一个简单的(至少我认为是的)行为。

当用户按下 Escape 时,我希望他正在编辑的 TextBox 拥有用户开始编辑时的文本,并且我想从 TextBox 移除焦点。

将文本设置为编辑开始时的值没有任何问题。

问题是移除元素的焦点。我不想将焦点移动到任何其他组件,我只想让 TextBox 失去焦点。我是否必须有一个不可见的元素来设置焦点,这样我的TextBox 才能失去焦点?

【问题讨论】:

    标签: wpf textbox focus


    【解决方案1】:

    AFAIK,不可能完全移除焦点。 Window 中的某些内容始终是焦点。

    【讨论】:

      【解决方案2】:

      我一直在使用的代码:

      // Move to a parent that can take focus
      FrameworkElement parent = (FrameworkElement)textBox.Parent;
      while (parent != null && parent is IInputElement && !((IInputElement)parent).Focusable)
      {
          parent = (FrameworkElement)parent.Parent;
      }
      
      DependencyObject scope = FocusManager.GetFocusScope(textBox);
      FocusManager.SetFocusedElement(scope, parent as IInputElement);
      

      【讨论】:

      • 这段代码很棒,Keyboard.ClearFocus() 有一些意想不到的副作用
      • 为什么条件 !((IInputElement)parent).Focusable 有 "!"在前?如果父母是可聚焦的,这个条件不应该是真的吗?
      • Mert - 不确定,但只是浏览这篇文章,看起来继续循环直到条件为真才是重点。这样第一个可聚焦的项目就会终止循环。
      • @patrick,哪些 意外副作用?你能举出相关的例子吗?
      • 这是一个很好的解决方案。我也遇到了 Keyboard.ClearFocus() 的问题。在模式窗口内的 TextBox 上运行 ClearFocus() 时,会导致 TextBox 和 Window 失去焦点。这意味着 KeyDown 事件不再进入窗口。相反,通过更改它使 Focus 更改为父级(可能是 Window),未来的 KeyDown 事件不会丢失。在我的实际示例中,我让窗口寻找“Key.Escape”并调用 Close()。如果您在任何地方运行 ClearFocus(),这将停止工作。
      【解决方案3】:

      您可以将焦点设置为可聚焦的祖先。即使文本框位于同一模板内没有可聚焦祖先的模板内,此代码也将起作用:

      DependencyObject ancestor = textbox.Parent;
      while (ancestor != null)
      {
          var element = ancestor as UIElement;
          if (element != null && element.Focusable)
          {
              element.Focus();
              break;
          }
      
          ancestor = VisualTreeHelper.GetParent(ancestor);
      }
      

      【讨论】:

        【解决方案4】:

        在 .NET Framework 4 中只是 Keyboard.ClearFocus();

        【讨论】:

        • 这正是我今晚要找的!
        • 这并不总是清除焦点:我有一个问题,即在单击某处后从代码隐藏运行 Keyboard.ClearFocus() 时,ListBox 内的 AutoCompleteTextBox 不会失去焦点。
        • ClearFocus 导致GotFocus 不会为最近聚焦的控件触发,但它仍会为其他控件触发。例如,对于我的自定义屏幕键盘来说,这是一个大问题。它确实会导致插入符号消失,这可能就是“键盘焦点”所需要的全部内容。也许我对“鼠标焦点”之类的东西更感兴趣。
        • 谢谢 Grault,我遇到了同样的问题。我想出的最好方法是使用other.Focus() 将焦点转移到其他控件。
        • @Grault 这只会清除键盘焦点,而不是逻辑焦点(这是触发GotFocus 事件的原因)。在你的程序中总是有一些具有逻辑焦点的东西。在清除键盘焦点之前,使用LostKeyboardFocus 事件或将焦点转移到另一个元素(逻辑焦点随之转移)。
        【解决方案5】:

        聚会有点晚了,但对我有帮助,所以就这样吧。

        从 .Net 3.0 开始,FrameworkElement 有一个 MoveFocus 函数,这对我有用。

        【讨论】:

        【解决方案6】:

        在 Windows Phone 开发中,我刚刚在 PhoneApplicationPage 中执行了Focus()this.Focus(),它就像一个魅力。

        【讨论】:

          【解决方案7】:

          对我来说,这很棘手,尤其是在使用 LostFocus 绑定时。 但是,我的解决方法是添加一个空标签并专注于它。

          <Label Name="ResetFocusArea" Focusable="True" FocusVisualStyle="{x:Null}" />
          

          ...

          OnKeyDown(object sender, RoutedEventArgs e)
          {
            //if is Esc
            ResetFocusArea.Focus();
          }
          

          【讨论】:

            【解决方案8】:

            我的回答并没有直接解决上述问题,但是,我觉得它的措辞导致它成为关于以编程方式摆脱焦点的“问题”。需要这样做的常见场景是用户能够在左键单击根控件(如窗口)的背景时清除焦点。

            因此,要实现这一点,您可以创建一个附加行为,它将焦点切换到动态创建的控件(在我的例子中是一个空标签)。最好在最高级别的元素(如窗口)上使用此行为,因为它会遍历其子元素以找到可以添加虚拟标签的面板。

            public class LoseFocusOnLeftClick : Behavior<FrameworkElement>
            {
                private readonly MouseBinding _leftClick;
                private readonly Label _emptyControl = new Label() { Focusable = true, HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Top };
            
                public LoseFocusOnLeftClick()
                {
                    _leftClick = new MouseBinding(new RelayCommand(LoseFocus), new MouseGesture(MouseAction.LeftClick));
                }
            
                protected override void OnAttached()
                {
                    AssociatedObject.InputBindings.Add(_leftClick);
                    AssociatedObject.Loaded += AssociatedObject_Loaded;
                }        
            
                protected override void OnDetaching()
                {
                    AssociatedObject.InputBindings.Remove(_leftClick);
                    AssociatedObject.Loaded -= AssociatedObject_Loaded;
                }
            
                private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
                {
                    AssociatedObject.Loaded -= AssociatedObject_Loaded;
            
                    AttachEmptyControl();
                }
            
                private void AttachEmptyControl()
                {            
                    DependencyObject currentElement = AssociatedObject;
                    while (!(currentElement is Panel))
                    {
                        currentElement = VisualTreeHelper.GetChild(currentElement, 0);
                    }
            
                    ((Panel)currentElement).Children.Add(_emptyControl);
                }
            
                private void LoseFocus()
                {            
                    _emptyControl.Focus();
                }
            }
            

            【讨论】:

              【解决方案9】:

              由于上述答案均不适合我,而接受的答案仅适用于键盘焦点,因此我采用了以下方法:

              // Kill logical focus
              FocusManager.SetFocusedElement(FocusManager.GetFocusScope(textBox), null);
              // Kill keyboard focus
              Keyboard.ClearFocus();
              

              同时杀死逻辑和键盘焦点。

              【讨论】:

              • 非常感谢,我已经尝试了其他所有方法将焦点转移到其他地方,你的方法很有效。
              【解决方案10】:

              使用 LPL 的答案对我有用,但它也会让我无法在下拉菜单中选择任何选项。为了解决这个问题,我添加了一个检查以查看焦点元素是否是文本框。

              按回车时做同样的检查,我的最终代码如下所示:

                  public Menu()
                  {
                      InitializeComponent();
                      this.PreviewMouseDown += PreviewMouseDownEventHandler;
                      this.KeyDown += WindowKeyDownHandler;
                  }
                  void ClearFocus()
                  {
                      UIElement elementWithFocus = Keyboard.FocusedElement as UIElement;
                      if (elementWithFocus is System.Windows.Controls.TextBox tb)
                      {
                          if (Keyboard.FocusedElement != null)
                          {
                              Keyboard.FocusedElement.RaiseEvent(new RoutedEventArgs(UIElement.LostFocusEvent));
                              Keyboard.ClearFocus();
                          }
                      }
                  }
              
                  private void PreviewMouseDownEventHandler(object sender, MouseButtonEventArgs e)
                  {
                      ClearFocus();
                  }
                  private void WindowKeyDownHandler(object sender, System.Windows.Input.KeyEventArgs e)
                  {
                      if (e.Key == Key.Enter)
                      {
                          ClearFocus();
                      }
                  }
              

              有了这个,我不需要为每个文本框添加一个焦点丢失,它可以很容易地扩展到其他元素,而不会破坏与程序其他部分的兼容性。

              【讨论】:

                猜你喜欢
                • 2011-12-18
                • 2013-10-09
                • 2010-11-11
                • 1970-01-01
                • 2014-08-05
                • 2015-12-30
                • 1970-01-01
                • 1970-01-01
                • 2019-12-07
                相关资源
                最近更新 更多