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

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

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

我没有任何问题设置文本的价值,它在编辑的开始。

问题是移除元素的焦点。我不想把焦点转移到任何其他组件上,我只想让 TextBox失去焦点。我必须有一个看不见的元素设置焦点,使我的 TextBox可以失去焦点?

91598 次浏览

AFAIK, it is not possible to completely remove the focus. Something in your Window will always have the focus.

The code I have been using :

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

You can set the focus to a focusable ancestor. This code will work even if the textbox is inside a template with no focusable ancestors inside that same template:

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


ancestor = VisualTreeHelper.GetParent(ancestor);
}

in .NET Framework 4 just Keyboard.ClearFocus();

A bit late to the party, but it was helpful to me so here it goes.

Since .Net 3.0, FrameworkElement has a MoveFocus function which did the trick for me.

In Windows Phone Development, I just did Focus() or this.Focus() in the PhoneApplicationPage and it worked like a charm.

For me, it's quite tricky, especially when using with LostFocus binding. However, my workaround is to add an empty label and focus on it.

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

...

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

My answer does not adress the above question directly, however, I feel that the wording of it has caused it to become "The Question" about programmatically getting rid of focus. A common scenario where this is needed is for the user to be able to clear focus upon left-clicking the background of a root control, like window.

So, to achieve this, you can create an Attached Behavior that will switch focus to a dynamically created control (in my case, an empty label). It is preferrable to use this behavior on the highest-level elements like windows, as it iterates through it's children to find a panel it can add a dummy label to.

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();
}
}

Since none of the above answers worked for me and the accepted answer does work only for a keyboard focus, I came to the following approach:

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

Kills both, logical as well as the keyboard focus.

Using LPL's answer worked for me, but it would also make me unable to select any options in dropdown menues. To combat this, I added a check to see if the focused element was a textbox.

Doing the same check for when pressing enter, my final code looked like this:

    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();
}
}

With this, I didn't need to add a focuslost to every textbox, and it can easily extend to other elements without breaking compatability with other parts of the program.