WPF: Cómo quitar el foco mediante programación de un TextBox

96

Quiero agregar un comportamiento simple (al menos pensé que lo era) a mi WPF TextBox.

Cuando el usuario presiona Escape, quiero que el TextBoxque está editando tenga el texto que tenía cuando el usuario comenzó a editar, Y quiero quitar el foco del TextBox.

No tengo ningún problema para configurar el texto por el valor que tenía al comienzo de la edición.

El problema es quitar el foco del elemento. No quiero mover el enfoque a ningún otro componente, solo quiero TextBoxperder el enfoque. ¿Tendré que tener un elemento invisible para establecer el enfoque para que TextBoxpueda perder el enfoque?

jpsstavares
fuente

Respuestas:

152

en .NET Framework 4 solo Keyboard.ClearFocus();

LPL
fuente
1
¡Esto era exactamente lo que estaba buscando esta noche!
Josh
9
Esto no siempre aclara el enfoque: tengo un problema en el que un AutoCompleteTextBox dentro de un ListBox no pierde el foco cuando ejecuto Keyboard.ClearFocus()desde el código subyacente después de hacer clic en alguna parte.
ANeves piensa que SE es malvado
3
ClearFocushace GotFocusque no se dispare para el control enfocado recientemente mientras aún dispara para otros controles. Ese es un gran problema para mi teclado en pantalla personalizado, por ejemplo. Hace que el símbolo de intercalación desaparezca, que es probablemente todo lo que implica el "enfoque del teclado". Quizás estoy más interesado en algo como "enfoque del mouse".
Grault
2
Gracias Grault, tengo el mismo problema. Lo mejor que se me ocurrió es mover el enfoque a algún otro control other.Focus().
Tor Klingberg
7
@Grault Esto solo borra el foco del teclado, no el foco lógico (que es lo que dispara el GotFocusevento). Siempre hay algo con un enfoque lógico en su programa. Utilice el LostKeyboardFocusevento o cambie el enfoque a otro elemento (que cambia el enfoque lógico junto con él) antes de borrar el enfoque del teclado.
Chirimorin
54

El código que he estado usando:

// 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);
decasteljau
fuente
2
Este código es genial, Keyboard.ClearFocus () tiene algunos efectos secundarios no deseados
patrick
¿Por qué la condición? ((IInputElement) parent) .Focusable tiene "!" ¿Al frente? ¿No debería ser cierta esta condición si el padre puede enfocarse?
Mert Akcakaya
Mert: no estoy seguro, pero al navegar a través de esta publicación, parece que continuar con el bucle hasta que esa condición sea verdadera es el punto. De esa forma, el primer elemento enfocable termina el ciclo.
jpierson
4
@patrick, ¿ qué efectos secundarios no deseados? ¿Podría dar ejemplos relevantes?
ANeves piensa que SE es malvado
1
Esta es una gran solucion. También tuve problemas con Keyboard.ClearFocus (). Cuando se ejecuta ClearFocus () en un TextBox dentro de una ventana modal, hace que tanto el TextBox como la ventana pierdan el foco. Lo que significa que los eventos KeyDown ya no van a la ventana. En cambio, al cambiarlo para que el enfoque cambie a un padre (que puede ser la ventana), los eventos futuros de KeyDown no se pierden. En mi ejemplo práctico, tengo la ventana buscando "Key.Escape" y llamando a Close (). Esto deja de funcionar si ejecuta ClearFocus () en cualquier lugar.
Denis P
19

Un poco tarde para la fiesta, pero me ayudó, así que aquí va.

Desde .Net 3.0, FrameworkElementtiene una función MoveFocus que me sirvió.

SuperOli
fuente
Para obtener instrucciones -> msdn.microsoft.com/en-us/library/…
Carter Medlin
"Asegúrese de comprobar el valor de retorno de este método. Es posible que se devuelva un valor de retorno falso si el recorrido se encuentra en una tabulación definida por la composición de un control y la solicitud de recorrido no solicitó ajustarse". - msdn.microsoft.com/en-us/library/…
aderesh
13

Dado que ninguna de las respuestas anteriores funcionó para mí y la respuesta aceptada funciona solo para un enfoque de teclado, llegué al siguiente enfoque:

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

Mata tanto el enfoque lógico como el del teclado.

Ciclón
fuente
9

Puede establecer el enfoque en un antepasado enfocable. Este código funcionará incluso si el cuadro de texto está dentro de una plantilla sin antepasados ​​enfocables dentro de esa misma plantilla:

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

    ancestor = VisualTreeHelper.GetParent(ancestor);
}
Julián Domínguez
fuente
6

AFAIK, no es posible eliminar completamente el enfoque. Algo en su ventana siempre tendrá el foco.

bitbonk
fuente
2

En Windows Phone Development, acabo de hacerlo Focus()o this.Focus()en PhoneApplicationPage y funcionó a las mil maravillas .

Bruno Lemos
fuente
1

Para mí, es bastante complicado, especialmente cuando se usa con el enlace LostFocus. Sin embargo, mi solución es agregar una etiqueta vacía y concentrarme en ella.

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

...

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

Mi respuesta no aborda directamente la pregunta anterior, sin embargo, creo que su redacción ha hecho que se convierta en "La pregunta" sobre cómo deshacerse del enfoque mediante programación. Un escenario común donde esto es necesario es que el usuario pueda despejar el enfoque al hacer clic con el botón izquierdo en el fondo de un control raíz, como una ventana.

Entonces, para lograr esto, puede crear un comportamiento adjunto que cambiará el enfoque a un control creado dinámicamente (en mi caso, una etiqueta vacía). Es preferible usar este comportamiento en los elementos de más alto nivel como ventanas, ya que recorre sus elementos secundarios para encontrar un panel al que pueda agregar una etiqueta ficticia.

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();
    }
}
Triple Acreción
fuente