ventanas tutorial navegacion español entre ejemplos wpf focus

navegacion - wpf tutorial español



WPF y enfoque inicial (12)

Parece que cuando se inicia una aplicación WPF, nada tiene foco.

Esto es muy extraño. Cada otro marco que he utilizado hace exactamente lo que cabría esperar: pone el foco inicial en el primer control en el orden de tabulación. Pero he confirmado que es WPF, no solo mi aplicación: si creo una nueva ventana, y simplemente pongo un TextBox en ella, y ejecuto la aplicación, el TextBox no tiene foco hasta que haga clic en él o presione Tab . Yuck.

Mi aplicación real es más complicada que solo un TextBox. Tengo varias capas de UserControls dentro de UserControls. Uno de esos UserControls tiene controladores Focusable = "True" y KeyDown / KeyUp, y quiero que tenga el foco tan pronto como se abra mi ventana. Todavía soy un principiante de WPF, y no estoy teniendo mucha suerte en descubrir cómo hacerlo.

Si inicio mi aplicación y presiono la tecla Tab, el foco se dirige a mi control enfocable y comienza a funcionar como yo quiero. Pero no quiero que mis usuarios tengan que presionar la tecla Tab antes de que puedan comenzar a usar la ventana.

He jugado con FocusManager.FocusedElement, pero no estoy seguro de qué control activar (¿la ventana de nivel superior? ¿La principal que contiene el control enfocable? ¿El control enfocable en sí?) O en qué configurarlo.

¿Qué debo hacer para que mi control profundamente anidado tenga foco inicial tan pronto como se abra la ventana? ¿O mejor aún, para enfocar el primer control enfocable en el orden de tabulación?


Basado en la answer aceptada implementada como un comportamiento adjunto:

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)); } } } }

Úselo así:

<Window xmlns:Behaviors="clr-namespace:UI.Behaviors" Behaviors:FocusBehavior.FocusFirst="true">


Después de tener un ''WPF Initial Focus Nightmare'' y basado en algunas respuestas en la pila, lo siguiente me resultó ser la mejor solución.

Primero, agregue su App.xaml OnStartup () los siguientes:

EventManager.RegisterClassHandler(typeof(Window), Window.LoadedEvent, new RoutedEventHandler(WindowLoaded));

A continuación, agregue el evento ''WindowLoaded'' también en 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)); })); }

El problema de enhebrado debe ser utilizado ya que el enfoque inicial de WPF falla principalmente debido a algunas condiciones de carrera de infraestructura.

Encontré la siguiente solución mejor, ya que se usa globalmente para toda la aplicación.

Espero eso ayude...

Orán


Encontré otra solución posible. Mark Smith publicó una extensión de marcado FirstFocusedElement para utilizar con FocusManager.FocusedElement.

<UserControl x:Class="FocusTest.Page2" xmlns:FocusTest="clr-namespace:FocusTest" FocusManager.FocusedElement="{FocusTest:FirstFocusedElement}">


Esto funciona, también:

<Window FocusManager.FocusedElement="{Binding ElementName=SomeElement}"> <DataGrid x:Name="SomeElement"> ... </DataGrid> </Window>


La solución anterior no funcionaba como esperaba, cambié ligeramente el comportamiento propuesto por Mizipzor de la siguiente manera:

De esta parte

if ((bool)args.NewValue) { control.Loaded += (sender, e) => control.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next)); }

A esto

if ((bool)args.NewValue) { control.Loaded += (sender, e) => control.Focus(); }

Y no estoy adjuntando este comportamiento a Window o UserControl, pero para controlar quiero centrarme inicialmente, por ejemplo:

<TextBox ui:FocusBehavior.InitialFocus="True" />

Oh, lo siento por nombres diferentes. Estoy usando el nombre de InitialFocus para la propiedad adjunta.

Y esto está funcionando para mí, tal vez podría ayudar a alguien más.


Puede hacer que el control se establezca fácilmente como el elemento enfocado en XAML.

<Window> <DataGrid FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"> ... </DataGrid> </Window>

Nunca he intentado configurar esto en un control de usuario y ver si esto funciona, pero puede.


Si eres como yo, y estás utilizando algunos marcos que, de alguna manera, estropean los comportamientos de enfoque básicos y hacen irrelevantes todas las soluciones anteriores, aún puedes hacer esto:

1 - Observe el elemento que obtiene el foco (¡sea lo que sea!)

2 - Agregue esto en su código detrás de xxx.xaml.cs

private bool _firstLoad;

3 - Agregue esto al elemento que obtiene el primer foco:

GotFocus="Element_GotFocus"

4 - Agregue el método Element_GotFocus en el código subyacente y especifique el elemento con nombre de WPF que necesita el primer foco:

private void Element_GotFocus(object sender, RoutedEventArgs e) { if(_firstLoad) { this.MyElementWithFistFocus.Focus(); _firstLoad = false; } }

Espero que esto ayude como una solución de último recurso


También me enfrenté al mismo problema. Tenía tres cuadros de texto dentro del contenedor canvas y quería que el primer cuadro de texto se enfocara cuando se abre el control del usuario. El código WPF siguió el patrón MVVM. Creé una clase de comportamiento separada para enfocar el elemento y lo vinculé a mi vista de esta manera.

Código de comportamiento del lienzo

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); } } }

Código para ver

<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]+$" 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>


Tenía el mismo problema resuelto con una solución simple: En la ventana principal:

<Window .... FocusManager.FocusedElement="{Binding ElementName=usercontrolelementname}" ... />

En el control de usuario:

private void UserControl_GotFocus_1(object sender, RoutedEventArgs e) { targetcontrol.Focus(); this.GotFocus -= UserControl_GotFocus_1; // to set focus only once }


Tuve la brillante idea de cavar a través de Reflector para ver dónde se usa la propiedad Focusable, y encontré mi camino hacia esta solución. Solo necesito agregar el siguiente código al constructor de mi ventana:

Loaded += (sender, e) => MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));

Esto seleccionará automáticamente el primer control en el orden de tabulación, por lo que es una solución general que debería poder soltarse en cualquier ventana y Just Work.


Una versión mínima de la respuesta de Mizipzor para 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)); }

Uso en tu XAML:

<Window local:FocusBehavior.GiveInitialFocus="True" />


<Window FocusManager.FocusedElement="{Binding ElementName=yourControlName}">