WPF: come rimuovere lo stato attivo a livello di codice da un controllo TextBox


95

Voglio aggiungere un comportamento semplice (almeno pensavo che fosse) al mio WPF TextBox.

Quando l'utente preme Esc, voglio che il testo TextBoxche sta modificando abbia il testo che aveva quando l'utente ha iniziato a modificare, E voglio rimuovere il focus dal file TextBox.

Non ho alcun problema a impostare il testo per il valore che aveva all'inizio della modifica.

Il problema è rimuovere il focus dell'elemento. Non voglio spostare il focus su nessun altro componente, voglio solo che lo TextBoxperda. Dovrò avere un elemento invisibile per impostare il focus in modo che il mio TextBoxpossa perdere il focus?

Risposte:


152

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


1
Questo era esattamente quello che stavo cercando questa sera!
Josh

9
Questo non sempre chiarisce il focus: ho un problema in cui un AutoCompleteTextBox all'interno di un ListBox non perde il focus quando eseguo Keyboard.ClearFocus()dal code-behind dopo un clic da qualche parte.
ANeves

3
ClearFocusfa sì GotFocusche non si attivi per il controllo focalizzato di recente mentre si attiva ancora per altri controlli. Questo è un grosso problema per la mia tastiera su schermo personalizzata, ad esempio. Causa la scomparsa del cursore, che è probabilmente tutto ciò che comporta il "focus della tastiera". Forse sono più interessato a qualcosa come "messa a fuoco del mouse".
Grault

2
Grazie Grault, ho lo stesso problema. Il meglio che mi è venuto in mente è spostare l'attenzione su un altro controllo con other.Focus().
Tor Klingberg

7
@Grault Questo cancella solo il focus della tastiera, non il focus logico (che è ciò che si attiva per l' GotFocusevento). C'è sempre qualcosa con focalizzazione logica nel tuo programma. Utilizza l' LostKeyboardFocusevento o sposta lo stato attivo su un altro elemento (che sposta lo stato attivo logico insieme ad esso) prima di cancellare lo stato attivo della tastiera.
Chirimorin

54

Il codice che sto 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);

2
Questo codice è fantastico, Keyboard.ClearFocus () ha alcuni effetti collaterali indesiderati
patrick

Perché la condizione! ((IInputElement) parent) .Focusable ha "!" di fronte? Questa condizione non dovrebbe essere vera se il genitore è focalizzabile?
Mert Akcakaya

Mert: non sono sicuro, ma il punto è solo sfogliare questo post, continuare a scorrere fino a quando quella condizione è vera. In questo modo il primo elemento attivabile termina il ciclo.
jpierson

4
@ patrick, quali effetti collaterali indesiderati? Potreste fornire esempi pertinenti?
ANeves

1
Questa è un'ottima soluzione. Ho anche avuto problemi con Keyboard.ClearFocus (). Quando si esegue ClearFocus () su un TextBox all'interno di una finestra modale, fa perdere lo stato attivo sia al TextBox che alla finestra. Significato Gli eventi KeyDown non vengono più visualizzati nella finestra. Modificandolo invece in modo che il Focus cambi in un genitore (che potrebbe essere la Finestra), i futuri eventi KeyDown non vengono persi. Nel mio esempio pratico, ho la finestra che cerca "Key.Escape" e chiama Close (). Questo smette di funzionare se esegui ClearFocus () ovunque.
Denis P

19

Un po 'tardi per la festa, ma mi è stato d'aiuto, quindi eccolo qui.

Dal momento che .Net 3.0, FrameworkElementha una funzione MoveFocus che ha fatto il trucco per me.



"Assicurati di controllare il valore di ritorno di questo metodo. Potrebbe essere restituito un valore di ritorno false se l'attraversamento viene eseguito in un punto di tabulazione definito dalla composizione di un controllo e la richiesta di attraversamento non ha richiesto il ritorno a capo." - msdn.microsoft.com/en-us/library/...
aderesh

13

Poiché nessuna delle risposte precedenti ha funzionato per me e la risposta accettata funziona solo per un focus sulla tastiera, sono arrivato al seguente approccio:

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

Uccide entrambi, logico e il focus della tastiera.


9

Puoi impostare il focus su un antenato focalizzabile. Questo codice funzionerà anche se la casella di testo si trova all'interno di un modello senza antenati attivabili all'interno dello stesso modello:

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, non è possibile rimuovere completamente il focus. Qualcosa nella tua finestra avrà sempre il focus.


2

In Windows Phone Development, l'ho appena fatto Focus()o this.Focus()nella PhoneApplicationPage e ha funzionato a meraviglia .


1

Per me è piuttosto complicato, soprattutto quando si utilizza con l'associazione LostFocus. Tuttavia, la mia soluzione alternativa è aggiungere un'etichetta vuota e concentrarmi su di essa.

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

...

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

0

La mia risposta non affronta direttamente la domanda di cui sopra, tuttavia, ritengo che la sua formulazione abbia fatto sì che diventasse "La Domanda" sull'eliminazione programmatica della concentrazione. Uno scenario comune in cui ciò è necessario è che l'utente sia in grado di evidenziare chiaramente il clic con il pulsante sinistro del mouse sullo sfondo di un controllo root, come la finestra.

Quindi, per ottenere ciò, puoi creare un comportamento allegato che sposterà lo stato attivo su un controllo creato dinamicamente (nel mio caso, un'etichetta vuota). È preferibile utilizzare questo comportamento sugli elementi di livello più alto come le finestre, poiché itera attraverso i suoi figli per trovare un pannello a cui può aggiungere un'etichetta fittizia.

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();
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.