Cenni preliminari sullo stato attivo
In WPF lo stato attivo è basato su due concetti principali, lo stato attivo della tastiera e lo stato attivo logico. Lo stato attivo della tastiera fa riferimento all'elemento che riceve l'input della tastiera, mentre lo stato attivo logico fa riferimento all'elemento di un ambito che ha ricevuto lo stato attivo. In questi cenni preliminari verranno illustrati questi concetti. Comprendere le differenze esistenti tra questi concetti è fondamentale per la creazione di applicazioni complesse costituite da più aree in cui è possibile ottenere lo stato attivo.
Le principali classi correlate alla gestione dello stato attivo sono Keyboard, FocusManager e le classi degli elementi di base, quali UIElement e ContentElement. Per ulteriori informazioni sugli elementi di base, vedere Cenni preliminari sugli elementi di base.
La classe Keyboard riguarda principalmente lo stato attivo della tastiera, mentre la classe FocusManager riguarda lo stato attivo logico, tuttavia tale distinzione non può essere considerata assoluta. Un elemento con lo stato attivo della tastiera presenta anche lo stato attivo logico, mentre un elemento che ha ottenuto lo stato attivo logico non ha necessariamente lo stato attivo della tastiera. Questo è evidente quando si utilizza la classe Keyboard per impostare l'elemento che ha lo stato attivo della tastiera, in quanto in tal modo viene anche impostato lo stato attivo logico.
Nel presente argomento sono contenute le seguenti sezioni.
- Stato attivo della tastiera
- Stato attivo logico
- Spostamento tramite tastiera
- Spostamento dello stato attivo a livello di codice
- Eventi di stato attivo
- Argomenti correlati
Stato attivo della tastiera
Lo stato attivo della tastiera fa riferimento all'elemento che riceve in un determinato momento l'input della tastiera. Su un desktop un solo elemento può avere lo stato attivo della tastiera. In WPF l'oggetto IsKeyboardFocused dell'elemento che presenta lo stato attivo della tastiera è impostato su true. La proprietà statica FocusedElement nella classe Keyboard ottiene l'elemento che attualmente presenta lo stato attivo della tastiera.
Affinché un elemento ottenga lo stato attivo della tastiera, le proprietà Focusable e IsVisible sugli elementi di base devono essere impostate su true. Per alcune classi, ad esempio la classe di base Panel, l'impostazione predefinita di Focusable è false. È pertanto necessario impostare Focusable su true affinché un elemento di questo tipo possa ottenere lo stato attivo della tastiera.
Lo stato attivo della tastiera può essere ottenuto tramite interazione dell'utente con l'UI, ad esempio utilizzando il tasto TAB per spostarsi su un elemento o facendo clic con il mouse su alcuni elementi. Lo stato attivo della tastiera può anche essere ottenuto a livello di codice utilizzando il metodo Focus sulla classe Keyboard. Il metodo Focus tenta di fornire lo stato attivo della tastiera all'elemento specificato. L'elemento restituito è l'elemento che presenta lo stato attivo della tastiera; potrebbe non essere quello richiesto se l'oggetto stato attivo nuovo o quello precedente blocca la richiesta.
Nell'esempio riportato di seguito viene utilizzato il metodo Focus per impostare lo stato attivo della tastiera su un oggetto Button.
Private Sub OnLoaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
' Sets keyboard focus on the first Button in the sample.
Keyboard.Focus(firstButton)
End Sub
private void OnLoaded(object sender, RoutedEventArgs e)
{
// Sets keyboard focus on the first Button in the sample.
Keyboard.Focus(firstButton);
}
La proprietà IsKeyboardFocused sulle classi degli elementi di base ottiene un valore che indica se l'elemento presenta lo stato attivo della tastiera. La proprietà IsKeyboardFocusWithin sulle classi degli elementi di base ottiene un valore che indica se l'elemento o uno qualsiasi dei relativi elementi figlio visivi presenta lo stato attivo della tastiera.
Quando si imposta lo stato attivo iniziale all'avvio dell'applicazione, l'elemento che riceverà lo stato attivo deve essere incluso nella struttura ad albero visuale della finestra iniziale caricata dall'applicazione e per tale elemento le proprietà Focusable e IsVisible devono essere impostate su true. È consigliabile impostare lo stato attivo iniziale nel gestore eventi Loaded. È inoltre possibile utilizzare un callback di Dispatcher chiamando Invoke o BeginInvoke.
Stato attivo logico
Lo stato attivo logico fa riferimento a FocusManager.FocusedElement in un ambito di stato attivo. Per ambito di stato attivo si intende un elemento che tiene traccia di FocusedElement all'interno del relativo ambito. Quando lo stato attivo della tastiera lascia un ambito di stato attivo, l'elemento con lo stato attivo perde lo stato attivo della tastiera, ma mantiene quello logico. Quando lo stato attivo della tastiera torna nell'ambito di stato attivo, l'elemento con lo stato attivo ottiene nuovamente lo stato attivo della tastiera. In questo modo, lo stato attivo della tastiera può essere passato tra più ambiti, ma l'elemento con lo stato attivo nell'ambito di stato attivo ottiene nuovamente lo stato attivo della tastiera quando lo stato attivo torna nell'ambito di stato attivo.
In un'applicazione possono essere presenti più elementi con stato attivo logico, ma in un determinato ambito può esistere un solo elemento con lo stato attivo.
Un elemento con stato attivo della tastiera presenta lo stato attivo logico per l'ambito a cui appartiene.
Un elemento può essere modificato in ambito di stato attivo in Extensible Application Markup Language (XAML) impostando la proprietà associata FocusManager IsFocusScope su true. Nel codice un elemento può essere convertito in un ambito di stato attivo chiamando SetIsFocusScope.
Nell'esempio riportato di seguito StackPanel viene convertito in un ambito di stato attivo impostando la proprietà associata IsFocusScope.
<StackPanel Name="focusScope1"
FocusManager.IsFocusScope="True"
Height="200" Width="200">
<Button Name="button1" Height="50" Width="50"/>
<Button Name="button2" Height="50" Width="50"/>
</StackPanel>
Dim focuseScope2 As New StackPanel()
FocusManager.SetIsFocusScope(focuseScope2, True)
StackPanel focuseScope2 = new StackPanel();
FocusManager.SetIsFocusScope(focuseScope2, true);
GetFocusScope restituisce l'ambito di stato attivo per l'elemento specificato.
In WPF le classi che sono ambiti di stato attivo per impostazione predefinita sono Window, MenuItem, ToolBar e ContextMenu.
GetFocusedElement ottiene l'elemento con lo stato attivo per l'ambito di stato attivo specificato. SetFocusedElement imposta invece l'elemento con lo stato attivo nell'ambito di stato attivo specificato. SetFocusedElement viene in genere utilizzato per impostare l'elemento con lo stato attivo iniziale.
Nell'esempio riportato di seguito viene illustrato come impostare e ottenere l'elemento con lo stato attivo in un ambito di stato attivo.
' Sets the focused element in focusScope1
' focusScope1 is a StackPanel.
FocusManager.SetFocusedElement(focusScope1, button2)
' Gets the focused element for focusScope 1
Dim focusedElement As IInputElement = FocusManager.GetFocusedElement(focusScope1)
// Sets the focused element in focusScope1
// focusScope1 is a StackPanel.
FocusManager.SetFocusedElement(focusScope1, button2);
// Gets the focused element for focusScope 1
IInputElement focusedElement = FocusManager.GetFocusedElement(focusScope1);
Spostamento tramite tastiera
La classe KeyboardNavigation è responsabile dell'implementazione dello spostamento dello stato attivo della tastiera predefinito quando viene premuto uno dei tasti di spostamento, che sono: TAB, MAIUSC+TAB, CTRL+TAB, CTRL+MAIUSC+TAB, tasti di direzione.
È possibile modificare il comportamento di spostamento di un contenitore di spostamento impostando le proprietà associate KeyboardNavigation TabNavigation, ControlTabNavigation e DirectionalNavigation. Queste proprietà sono di tipo KeyboardNavigationMode e i valori possibili sono Continue, Local, Contained, Cycle, Once e None. Il valore predefinito è Continue, che indica che l'elemento non è un contenitore di spostamento.
Nell'esempio riportato di seguito viene creato un controllo Menu con diversi oggetti MenuItem. La proprietà associata TabNavigation viene impostata su Cycle su Menu. Quando lo stato attivo viene modificato tramite il tasto TAB all'interno di Menu, lo stato attivo viene spostato da un elemento a un altro fino all'ultimo, quindi torna al primo elemento.
<Menu KeyboardNavigation.TabNavigation="Cycle">
<MenuItem Header="Menu Item 1" />
<MenuItem Header="Menu Item 2" />
<MenuItem Header="Menu Item 3" />
<MenuItem Header="Menu Item 4" />
</Menu>
Dim navigationMenu As New Menu()
Dim item1 As New MenuItem()
Dim item2 As New MenuItem()
Dim item3 As New MenuItem()
Dim item4 As New MenuItem()
navigationMenu.Items.Add(item1)
navigationMenu.Items.Add(item2)
navigationMenu.Items.Add(item3)
navigationMenu.Items.Add(item4)
KeyboardNavigation.SetTabNavigation(navigationMenu, KeyboardNavigationMode.Cycle)
Menu navigationMenu = new Menu();
MenuItem item1 = new MenuItem();
MenuItem item2 = new MenuItem();
MenuItem item3 = new MenuItem();
MenuItem item4 = new MenuItem();
navigationMenu.Items.Add(item1);
navigationMenu.Items.Add(item2);
navigationMenu.Items.Add(item3);
navigationMenu.Items.Add(item4);
KeyboardNavigation.SetTabNavigation(navigationMenu,
KeyboardNavigationMode.Cycle);
Spostamento dello stato attivo a livello di codice
Le API che è possibile utilizzare per lo stato attivo sono MoveFocus e PredictFocus.
MoveFocus sposta lo stato attivo sull'elemento successivo dell'applicazione. TraversalRequest consente di specificare la direzione. L'oggetto FocusNavigationDirection passato a MoveFocus specifica le direzioni nelle quali è possibile spostare lo stato attivo, ad esempio First, Last, Up e Down.
Nell'esempio riportato di seguito viene utilizzato MoveFocus per cambiare l'elemento con lo stato attivo.
' Creating a FocusNavigationDirection object and setting it to a
' local field that contains the direction selected.
Dim focusDirection As FocusNavigationDirection = _focusMoveValue
' MoveFocus takes a TraveralReqest as its argument.
Dim request As New TraversalRequest(focusDirection)
' Gets the element with keyboard focus.
Dim elementWithFocus As UIElement = TryCast(Keyboard.FocusedElement, UIElement)
' Change keyboard focus.
If elementWithFocus IsNot Nothing Then
elementWithFocus.MoveFocus(request)
End If
// Creating a FocusNavigationDirection object and setting it to a
// local field that contains the direction selected.
FocusNavigationDirection focusDirection = _focusMoveValue;
// MoveFocus takes a TraveralReqest as its argument.
TraversalRequest request = new TraversalRequest(focusDirection);
// Gets the element with keyboard focus.
UIElement elementWithFocus = Keyboard.FocusedElement as UIElement;
// Change keyboard focus.
if (elementWithFocus != null)
{
elementWithFocus.MoveFocus(request);
}
PredictFocus restituisce l'oggetto che riceverebbe lo stato attivo qualora questo venisse modificato. Attualmente, solo Up, Down, Left e Right sono supportati da PredictFocus.
Eventi di stato attivo
Gli eventi correlati allo stato attivo della tastiera sono PreviewGotKeyboardFocus, GotKeyboardFocus e PreviewLostKeyboardFocus, LostKeyboardFocus. Gli eventi sono definiti come eventi associati sulla classe Keyboard, ma sono maggiormente accessibili come eventi indirizzati equivalenti sulle classi dell'elemento di base. Per ulteriori informazioni sugli eventi, vedere Cenni preliminari sugli eventi indirizzati.
GotKeyboardFocus viene generato quando l'elemento ottiene lo stato attivo della tastiera, mentre LostKeyboardFocus viene generato quando l'elemento perde lo stato attivo della tastiera. Se l'evento PreviewGotKeyboardFocus o PreviewLostKeyboardFocusEvent viene gestito e Handled è impostato su true, lo stato attivo non cambia.
Nell'esempio riportato di seguito vengono collegati i gestori eventi GotKeyboardFocus e LostKeyboardFocus a un controllo TextBox.
<Border BorderBrush="Black" BorderThickness="1"
Width="200" Height="100" Margin="5">
<StackPanel>
<Label HorizontalAlignment="Center" Content="Type Text In This TextBox" />
<TextBox Width="175"
Height="50"
Margin="5"
TextWrapping="Wrap"
HorizontalAlignment="Center"
VerticalScrollBarVisibility="Auto"
GotKeyboardFocus="TextBoxGotKeyboardFocus"
LostKeyboardFocus="TextBoxLostKeyboardFocus"
KeyDown="SourceTextKeyDown"/>
</StackPanel>
</Border>
Quando TextBox ottiene lo stato attivo della tastiera, la proprietà Background di TextBox viene impostata su LightBlue.
Private Sub TextBoxGotKeyboardFocus(ByVal sender As Object, ByVal e As KeyboardFocusChangedEventArgs)
Dim source As TextBox = TryCast(e.Source, TextBox)
If source IsNot Nothing Then
' Change the TextBox color when it obtains focus.
source.Background = Brushes.LightBlue
' Clear the TextBox.
source.Clear()
End If
End Sub
private void TextBoxGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
TextBox source = e.Source as TextBox;
if (source != null)
{
// Change the TextBox color when it obtains focus.
source.Background = Brushes.LightBlue;
// Clear the TextBox.
source.Clear();
}
}
Quando TextBox perde lo stato attivo della tastiera, la proprietà Background di TextBox viene nuovamente impostata sul bianco.
Private Sub TextBoxLostKeyboardFocus(ByVal sender As Object, ByVal e As KeyboardFocusChangedEventArgs)
Dim source As TextBox = TryCast(e.Source, TextBox)
If source IsNot Nothing Then
' Change the TextBox color when it loses focus.
source.Background = Brushes.White
' Set the hit counter back to zero and updates the display.
Me.ResetCounter()
End If
End Sub
private void TextBoxLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
TextBox source = e.Source as TextBox;
if (source != null)
{
// Change the TextBox color when it loses focus.
source.Background = Brushes.White;
// Set the hit counter back to zero and updates the display.
this.ResetCounter();
}
}
Gli eventi correlati allo stato attivo logico sono GotFocus e LostFocus. Questi eventi sono definiti su FocusManager come eventi associati, ma FocusManager non espone wrapper di eventi CLR. UIElement e ContentElement espongono questi eventi in modo più appropriato.