WPF:如何以编程方式从文本框中删除焦点


95

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

当用户按下Escape键时,我希望TextBox他正在编辑,以使其具有用户开始编辑时的文本,并且我想从中移除焦点TextBox

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

问题是要删除该元素的焦点。我不想将焦点移到任何其他组件,我只是想TextBox失去焦点。我是否需要一个不可见的元素来设置焦点,以便我TextBox可以失去焦点?

Answers:


152

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


1
这正是我今晚在寻找的东西!
2012年

9
这并不总是会清除焦点:我有一个问题,当我Keyboard.ClearFocus()单击某个位置后从后台运行代码时,ListBox内的AutoCompleteTextBox不会失去焦点。
阿涅夫斯,2015年

3
ClearFocus导致GotFocus对最近聚焦的控件不触发,而对其他控件仍触发。例如,这对于我的自定义屏幕键盘来说是个大问题。它确实导致插入符号消失,这可能是“键盘焦点”所需要的。也许我对诸如“鼠标焦点”之类的东西更感兴趣。
Grault

2
谢谢Grault,我遇到了同样的问题。我想出的最好的办法就是将焦点转移到其他控件上other.Focus()
Tor Klingberg

7
@Grault这仅清除键盘焦点,而不清除逻辑焦点(这是触发GotFocus事件的原因)。在您的程序中总会有一些逻辑上的焦点。LostKeyboardFocus在清除键盘焦点之前,可以使用事件或将焦点移到另一个元素(逻辑焦点也随之移动)。
Chirimorin

54

我一直在使用的代码:

// 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);

2
这段代码很棒,Keyboard.ClearFocus()有一些意想不到的副作用
patrick 2012年

为什么条件!((IInputElement)parent).Focusable具有“!” 在前?如果父母可以集中精力,这个条件不应该成立吗?
Mert Akcakaya 2012年

Mert-不能确定,但​​是只是浏览这篇文章,就像继续循环直到该条件成立才是重点。这样,第一个可聚焦项将终止循环。
jpierson 2012年

4
@patrick,哪些意外副作用?你能举一些相关的例子吗?
2014年

1
这是一个很好的解决方案。我也遇到了Keyboard.ClearFocus()问题。在模态窗口内的TextBox上运行ClearFocus()时,它将导致TextBox和Window失去焦点。意味着KeyDown事件不再进入窗口。通过改为更改它,以便Focus更改为父项(可能是Window),以后的KeyDown事件不会丢失。在我的实际示例中,我让Window查找“ Key.Escape”并调用Close()。如果您在任何地方运行ClearFocus(),这将停止工作。
丹尼斯·P


13

由于以上答案均不适用于我,并且接受的答案仅对键盘焦点有效,因此我采用以下方法:

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

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


9

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

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

    ancestor = VisualTreeHelper.GetParent(ancestor);
}

6

AFAIK,无法完全移开焦点。窗口中的所有内容始终会成为焦点。



1

对我来说,这非常棘手,特别是在与LostFocus绑定一起使用时。但是,我的解决方法是添加一个空标签并专注于它。

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

...

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

0

我的回答不能直接解决上述问题,但是,我觉得它的措辞已使它成为关于以编程方式摆脱焦点的“问题”。需要此操作的常见方案是用户能够通过左键单击根控件(如窗口)的背景来清除焦点。

因此,要实现此目的,您可以创建一个附加行为,该行为会将焦点切换到动态创建的控件(在我的情况下为空标签)。最好在Windows等最高级别的元素上使用此行为,因为它会遍历其子级以查找可以向其添加虚拟标签的面板。

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();
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.