WPF: como remover programaticamente o foco de um TextBox

95

Eu quero adicionar um comportamento simples (pelo menos eu pensei que era) ao meu WPF TextBox.

Quando o usuário pressiona Escape, quero que o que TextBoxele está editando tenha o texto que tinha quando o usuário começou a editar, E quero remover o foco do TextBox.

Não tenho nenhum problema em configurar o texto para o valor que tinha no início da edição.

O problema é remover o foco do elemento. Não quero mover o foco para nenhum outro componente, só quero TextBoxperder o foco. Terei que ter um elemento invisível para definir o foco e TextBoxperder o foco?

jpsstavares
fonte

Respostas:

152

no .NET Framework 4 apenas Keyboard.ClearFocus();

LPL
fonte
1
Isso era exatamente o que eu estava procurando esta noite!
Josh
9
Isso nem sempre limpa o foco: tenho um problema em que um AutoCompleteTextBox dentro de um ListBox não perde o foco quando executo a Keyboard.ClearFocus()partir do code-behind após um clique em algum lugar.
ANeves
3
ClearFocusfaz GotFocuscom que não dispare para o controle recentemente focado enquanto ainda dispara para outros controles. Esse é um grande problema para meu teclado na tela personalizado, por exemplo. Isso faz com que o cursor desapareça, o que provavelmente é tudo o que o "foco do teclado" envolve. Talvez eu esteja mais interessado em algo como "foco do mouse".
Grault
2
Obrigado Grault, estou com o mesmo problema. O melhor que descobri é mover o foco para algum outro controle other.Focus().
Tor Klingberg de
7
@Grault Isso só limpa o foco do teclado, não o foco lógico (que é o que dispara para o GotFocusevento). Sempre há algo com foco lógico em seu programa. Use o LostKeyboardFocusevento ou mude o foco para outro elemento (que muda o foco lógico junto com ele) antes de limpar o foco do teclado.
Chirimorin de
54

O código que tenho usado:

// 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
fonte
2
Este código é ótimo, Keyboard.ClearFocus () tem alguns efeitos colaterais indesejados
patrick
Por que a condição! ((IInputElement) pai) .Focusable tem "!" em frente? Essa condição não deveria ser verdadeira se o pai fosse focalizável?
Mert Akcakaya
Mert - não tenho certeza, mas apenas navegando por este post, parece que continuar o loop até que a condição seja verdadeira é o ponto. Dessa forma, o primeiro item focalizável termina o loop.
jpierson de
4
@patrick, que efeitos colaterais indesejados? Você poderia dar exemplos relevantes?
ANeves
1
Esta é uma otima soluçao. Eu também tive problemas com Keyboard.ClearFocus (). Ao executar ClearFocus () em um TextBox dentro de uma janela modal, faz com que o TextBox e a janela percam o foco. Significando que os eventos KeyDown não vão mais para a janela. Ao mudá-lo para que o Foco mude para um pai (que pode ser a janela), os eventos futuros de KeyDown não são perdidos. Em meu exemplo prático, tenho a janela procurando por "Key.Escape" e chamando Close (). Isso para de funcionar se você executar ClearFocus () em qualquer lugar.
Denis P
19

Um pouco tarde para a festa, mas foi útil para mim, então aqui vai.

Desde .Net 3.0, FrameworkElementtem uma função MoveFocus que fez o truque para mim.

SuperOli
fonte
Para obter instruções -> msdn.microsoft.com/en-us/library/…
Carter Medlin
"Certifique-se de verificar o valor de retorno deste método. Um valor de retorno false pode ser retornado se a travessia for executada em uma parada de tabulação que é definida pela composição de um controle e a solicitação de travessia não solicitou a quebra." - msdn.microsoft.com/en-us/library/…
aderesh
13

Como nenhuma das respostas acima funcionou para mim e a resposta aceita funciona apenas para o foco do teclado, cheguei à seguinte abordagem:

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

Mata tanto o foco lógico quanto o do teclado.

Ciclone
fonte
9

Você pode definir o foco para um ancestral focalizável. Este código funcionará mesmo se a caixa de texto estiver dentro de um modelo sem ancestrais focalizáveis ​​dentro do mesmo modelo:

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

    ancestor = VisualTreeHelper.GetParent(ancestor);
}
Julian Dominguez
fonte
6

AFAIK, não é possível remover completamente o foco. Algo em sua janela sempre terá o foco.

bitbonk
fonte
2

No desenvolvimento do Windows Phone, acabei de fazer Focus()ou this.Focus()na PhoneApplicationPage e funcionou perfeitamente .

Bruno Lemos
fonte
1

Para mim, é bastante complicado, especialmente ao usar com ligação LostFocus. No entanto, minha solução alternativa é adicionar um rótulo vazio e me concentrar nele.

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

...

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

Minha resposta não aborda a questão acima diretamente, no entanto, eu sinto que a formulação dela fez com que ela se tornasse "A Questão" sobre como se livrar programaticamente do foco. Um cenário comum onde isso é necessário é que o usuário consiga limpar o foco clicando com o botão esquerdo do mouse no plano de fundo de um controle raiz, como uma janela.

Então, para conseguir isso, você pode criar um comportamento anexado que mudará o foco para um controle criado dinamicamente (no meu caso, um rótulo vazio). É preferível usar esse comportamento nos elementos de nível mais alto, como janelas, pois ele itera por meio de seus filhos para encontrar um painel ao qual possa adicionar um rótulo fictício.

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