Domanda
Sembra che quando si avvia un'applicazione WPF, nulla ha lo stato attivo.
Questo è davvero strano. Ogni altro framework che ho usato fa esattamente quello che ti aspetteresti: mette il focus iniziale sul primo controllo nell'ordine di tabulazione. Ma ho confermato che si tratta di WPF, non solo della mia app: se creo una nuova finestra, inserisco semplicemente una TextBox ed eseguo l'app, la TextBox non ha lo stato attivo finché non faccio clic su di essa o premo Tab . Yuck.
La mia app attuale è più complicata di una semplice TextBox. Ho diversi livelli di UserControls in UserControls. Uno di questi UserControls ha Focusable = " True " e i gestori KeyDown / KeyUp, e voglio che abbia lo stato attivo non appena si apre la mia finestra. Sono comunque un po 'un novizio del WPF, e non ho molta fortuna a capire come farlo.
Se avvio la mia app e premo il tasto Tab, lo stato attivo passa al mio controllo attivabile e inizia a funzionare nel modo desiderato. Ma non voglio che i miei utenti debbano premere Tab prima di poter iniziare a utilizzare la finestra.
Ho giocato con FocusManager.FocusedElement, ma non sono sicuro su quale controllo impostarlo (la finestra di livello superiore? il genitore che contiene il controllo attivabile? il controllo attivabile stesso?) o cosa impostare a.
Cosa devo fare per fare in modo che il mio controllo profondamente annidato abbia lo stato iniziale non appena si apre la finestra? O meglio ancora, focalizzare il primo controllo focalizzabile nell'ordine di tabulazione?
Soluzione
Ho avuto la brillante idea di scavare in Reflector per vedere dove viene utilizzata la proprietà Focusable e ho trovato la mia strada per questa soluzione. Devo solo aggiungere il seguente codice al costruttore di Windows:
Loaded += (sender, e) =>
MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
Questo selezionerà automaticamente il primo controllo nell'ordine di tabulazione, quindi è una soluzione generale che dovrebbe essere in grado di essere rilasciata in qualsiasi finestra e Just Work.
Altri suggerimenti
Anche questo funziona:
<Window FocusManager.FocusedElement="{Binding ElementName=SomeElement}">
<DataGrid x:Name="SomeElement">
...
</DataGrid>
</Window>
Basato sulla risposta accettata implementata come comportamento associato:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace UI.Behaviors
{
public static class FocusBehavior
{
public static readonly DependencyProperty FocusFirstProperty =
DependencyProperty.RegisterAttached(
"FocusFirst",
typeof(bool),
typeof(FocusBehavior),
new PropertyMetadata(false, OnFocusFirstPropertyChanged));
public static bool GetFocusFirst(Control control)
{
return (bool)control.GetValue(FocusFirstProperty);
}
public static void SetFocusFirst (Control control, bool value)
{
control.SetValue(FocusFirstProperty, value);
}
static void OnFocusFirstPropertyChanged(
DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
Control control = obj as Control;
if (control == null || !(args.NewValue is bool))
{
return;
}
if ((bool)args.NewValue)
{
control.Loaded += (sender, e) =>
control.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
}
}
Usalo in questo modo:
<Window xmlns:Behaviors="clr-namespace:UI.Behaviors"
Behaviors:FocusBehavior.FocusFirst="true">
Ho trovato un'altra possibile soluzione. Mark Smith ha pubblicato una estensione di markup FirstFocusedElement da utilizzare con FocusManager.FocusedElement.
<UserControl x:Class="FocusTest.Page2"
xmlns:FocusTest="clr-namespace:FocusTest"
FocusManager.FocusedElement="{FocusTest:FirstFocusedElement}">
Dopo aver avuto un 'Incubo di messa a fuoco iniziale WPF' e basato su alcune risposte in pila, la seguente è stata la soluzione migliore per me.
Innanzitutto, aggiungi App.xaml OnStartup () come segue:
EventManager.RegisterClassHandler(typeof(Window), Window.LoadedEvent,
new RoutedEventHandler(WindowLoaded));
Quindi aggiungi l'evento 'WindowLoaded' anche in App.xaml:
void WindowLoaded(object sender, RoutedEventArgs e)
{
var window = e.Source as Window;
System.Threading.Thread.Sleep(100);
window.Dispatcher.Invoke(
new Action(() =>
{
window.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
}));
}
Il problema del threading deve essere usato poiché il focus iniziale del WPF fallisce principalmente a causa di alcune condizioni di gara del framework.
Ho trovato la soluzione seguente migliore poiché viene utilizzata a livello globale per l'intera app.
Spero che aiuti ...
Oran
Lo stesso problema è stato risolto con una soluzione semplice: Nella finestra principale:
<Window ....
FocusManager.FocusedElement="{Binding ElementName=usercontrolelementname}"
... />
Nel controllo utente:
private void UserControl_GotFocus_1(object sender, RoutedEventArgs e)
{
targetcontrol.Focus();
this.GotFocus -= UserControl_GotFocus_1; // to set focus only once
}
Puoi facilmente impostare il controllo stesso come elemento attivo in XAML.
<Window>
<DataGrid FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}">
...
</DataGrid>
</Window>
Non ho mai provato a impostarlo in un controllo utente e vedere se funziona, ma potrebbe.
Una versione minima di la risposta di Mizipzor per C # 6+.
public static class FocusBehavior
{
public static readonly DependencyProperty GiveInitialFocusProperty =
DependencyProperty.RegisterAttached(
"GiveInitialFocus",
typeof(bool),
typeof(FocusBehavior),
new PropertyMetadata(false, OnFocusFirstPropertyChanged));
public static bool GetGiveInitialFocus(Control control) => (bool)control.GetValue(GiveInitialFocusProperty);
public static void SetGiveInitialFocus(Control control, bool value) => control.SetValue(GiveInitialFocusProperty, value);
private static void OnFocusFirstPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var control = obj as Control;
if (control == null || !(args.NewValue is bool))
return;
if ((bool)args.NewValue)
control.Loaded += OnControlLoaded;
else
control.Loaded -= OnControlLoaded;
}
private static void OnControlLoaded(object sender, RoutedEventArgs e) => ((Control)sender).MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
Utilizza nel tuo XAML:
<Window local:FocusBehavior.GiveInitialFocus="True" />
Se sei come me e stai usando alcuni framework che, in qualche modo, confondono i comportamenti di focus di base e rendono irrilevanti tutte le soluzioni sopra, puoi ancora farlo:
1 - Nota l'elemento che ottiene lo stato attivo (qualunque esso sia!)
2 - Aggiungi questo nel tuo codice dietro xxx.xaml.cs
private bool _firstLoad;
3 - Aggiungi questo sull'elemento che ottiene il primo focus:
GotFocus="Element_GotFocus"
4 - Aggiungi il metodo Element_GotFocus nel codice dietro e specifica l'elemento denominato WPF che necessita del primo focus:
private void Element_GotFocus(object sender, RoutedEventArgs e)
{
if(_firstLoad)
{
this.MyElementWithFistFocus.Focus();
_firstLoad = false;
}
}
5 - Gestisci l'evento Loaded
in XAML
Loaded="MyWindow_Loaded"
in xaml.cs
private void MyWindow_Loaded(object sender, RoutedEventArgs e)
{
_firstLoad = true;
this.Element_GotFocus(null, null);
}
Spero che questo possa essere di aiuto come ultima soluzione
Ho anche affrontato lo stesso problema. Avevo tre caselle di testo all'interno del contenitore di tela e volevo che la prima casella di testo fosse focalizzata quando si apriva il controllo utente. Il codice WPF ha seguito il modello MVVM. Ho creato una classe di comportamento separata per focalizzare l'elemento e l'ho legato alla mia vista in questo modo.
Codice comportamento tela
public class CanvasLoadedBehavior : Behavior<Canvas>
{
private Canvas _canvas;
protected override void OnAttached()
{
base.OnAttached();
_canvas = AssociatedObject as Canvas;
if (_canvas.Name == "ReturnRefundCanvas")
{
_canvas.Loaded += _canvas_Loaded;
}
}
void _canvas_Loaded(object sender, RoutedEventArgs e)
{
FocusNavigationDirection focusDirection = FocusNavigationDirection.Next;
// MoveFocus takes a TraveralReqest as its argument.
TraversalRequest request = new TraversalRequest(focusDirection);
UIElement elementWithFocus = Keyboard.FocusedElement as UIElement;
if (elementWithFocus != null)
{
elementWithFocus.MoveFocus(request);
}
}
}
Codice per visualizzazione
<Canvas Name="ReturnRefundCanvas" Height="200" Width="1466" DataContext="{Binding RefundSearchViewModel}">
<i:Interaction.Behaviors>
<b:CanvasLoadedBehavior />
</i:Interaction.Behaviors>
<uc:Keyboard Canvas.Left="973" Canvas.Top="111" ToolTip="Keyboard" RenderTransformOrigin="-2.795,9.787"></uc:Keyboard>
<Label Style="{StaticResource Devlbl}" Canvas.Left="28" Content="Return and Refund Search" Canvas.Top="10" />
<Image Height="30" Width="28" Canvas.Top="6" Canvas.Left="5" Source="pack://application:,,,/HomaKiosk;component/images/searchF.png">
<Image.OpacityMask>
<ImageBrush ImageSource="pack://application:,,,/HomaKiosk;component/images/searchF.png"/>
</Image.OpacityMask>
</Image>
<Separator Height="4" Canvas.Left="6" Margin="0" Canvas.Top="35" Width="1007"/>
<ContentControl Canvas.Top="45" Canvas.Left="21"
ContentTemplate="{StaticResource ErrorMsg}"
Visibility="{Binding Error, Converter={c:StringNullOrEmptyToVisibilityConverter}}"
Content="{Binding Error}" Width="992"></ContentControl>
<Label Style="{StaticResource Devlbl}" Canvas.Left="29" Name="FirstName" Content="First Name" Canvas.Top="90" />
<wpf:AutoCompleteTextBox Style="{StaticResource AutoComp}" Height="32" Canvas.Left="33" ToolTip="First Name" Canvas.Top="120" Width="205" Padding="10,5" TabIndex="1001"
VerticalAlignment="Top"
Watermark=""
IconPlacement="Left"
IconVisibility="Visible"
Delay="100"
Text="{Binding FirstName, Mode=TwoWay, TargetNullValue=''}"
Provider="{Binding FirstNameSuggestions}">
<wpf:AutoCompleteTextBox.ItemTemplate>
<DataTemplate>
<Border Padding="5">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding}"
FontWeight="Bold" />
</StackPanel>
</Border>
</DataTemplate>
</wpf:AutoCompleteTextBox.ItemTemplate>
</wpf:AutoCompleteTextBox>
<Label Style="{StaticResource Devlbl}" Canvas.Left="250" Content="Last Name" Canvas.Top="90" />
<wpf:AutoCompleteTextBox Style="{StaticResource AutoComp}" Height="32" ToolTip="Last Name" Canvas.Left="250" Canvas.Top="120" Width="205" Padding="10,5" TabIndex="1002"
VerticalAlignment="Top"
Watermark=""
IconPlacement="Left"
IconVisibility="Visible"
Delay="100"
Text="{Binding LastName, Mode=TwoWay, TargetNullValue=''}"
Provider="{Binding LastNameSuggestions}">
<wpf:AutoCompleteTextBox.ItemTemplate>
<DataTemplate>
<Border Padding="5">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding}"
FontWeight="Bold" />
</StackPanel>
</Border>
</DataTemplate>
</wpf:AutoCompleteTextBox.ItemTemplate>
</wpf:AutoCompleteTextBox>
<Label Style="{StaticResource Devlbl}" Canvas.Left="480" Content="Receipt No" Canvas.Top="90" />
<wpf:AutoCompleteTextBox Style="{StaticResource AutoComp}" Height="32" ToolTip="Receipt No" Canvas.Left="480" Canvas.Top="120" Width="205" Padding="10,5" TabIndex="1002"
VerticalAlignment="Top"
Watermark=""
IconPlacement="Left"
IconVisibility="Visible"
Delay="100"
Text="{Binding ReceiptNo, Mode=TwoWay, TargetNullValue=''}"
Provider="{Binding ReceiptIdSuggestions}">
<wpf:AutoCompleteTextBox.ItemTemplate>
<DataTemplate>
<Border Padding="5">
<StackPanel Orientation="Vertical" >
<TextBlock Text="{Binding}"
FontWeight="Bold">
</TextBlock>
</StackPanel>
</Border>
</DataTemplate>
</wpf:AutoCompleteTextBox.ItemTemplate>
<i:Interaction.Behaviors>
<b:AllowableCharactersTextBoxBehavior RegularExpression="^[0-9]+<*>quot; MaxLength="15" />
</i:Interaction.Behaviors>
</wpf:AutoCompleteTextBox>
<!--<Label Style="{StaticResource Devlbl}" Canvas.Left="710" Content="Duration" Canvas.Top="79" />-->
<!--<ComboBox AllowDrop="True" Canvas.Left="710" ToolTip="Duration" Canvas.Top="107" Width="205" TabIndex="1004"
Style="{StaticResource CommonComboBox}"
ItemsSource="{Binding Durations}" DisplayMemberPath="Description" SelectedValuePath="Id" SelectedValue="{Binding SelectedDate, Mode=TwoWay}">
</ComboBox>-->
<Button Content="Search" Style="{StaticResource MyButton}" ToolTip="Search"
Canvas.Top="116" Canvas.Left="710" Cursor="Hand"
Command="{Binding SearchCommand}" TabIndex="2001">
</Button>
<Button Content="Clear" Style="{StaticResource MyButton}" ToolTip="Clear"
Canvas.Top="116" Canvas.Left="840" Cursor="Hand"
Command="{Binding ClearCommand}" TabIndex="2002">
</Button>
<Image Height="25" Width="25" Canvas.Top="175" Canvas.Left="25" Source="pack://application:,,,/HomaKiosk;component/images/chkpending.png"/>
<Label Style="{StaticResource LegendLbl}" Canvas.Left="50" Content="Check Returned and Payment Pending" Canvas.Top="178" />
<Image Height="25" Width="25" Canvas.Top="175" Canvas.Left="300" Source="pack://application:,,,/HomaKiosk;component/images/chkrepaid.png"/>
<Label Style="{StaticResource LegendLbl}" Canvas.Left="325" Content="Repaid" Canvas.Top="178" />
<Image Height="25" Width="25" Canvas.Top="175" Canvas.Left="395" Source="pack://application:,,,/HomaKiosk;component/images/refund.png"/>
<Label Style="{StaticResource LegendLbl}" Canvas.Left="415" Content="Refunded" Canvas.Top="178" />
</Canvas>
La soluzione sopra non funzionava come previsto per me, ho modificato leggermente il comportamento proposto da Mizipzor come segue:
Da questa parte
if ((bool)args.NewValue)
{
control.Loaded += (sender, e) =>
control.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
A questo
if ((bool)args.NewValue)
{
control.Loaded += (sender, e) => control.Focus();
}
E non sto associando questo comportamento a Window o UserControl, ma per controllare voglio concentrarmi inizialmente, ad es .:
<TextBox ui:FocusBehavior.InitialFocus="True" />
Oh, scusa per il nome diverso che sto usando il nome InitialFocus per la proprietà allegata.
E questo funziona per me, forse potrebbe aiutare qualcun altro.
<Window FocusManager.FocusedElement="{Binding ElementName=yourControlName}">