web-dev-qa-db-fra.com

WPF et objectif initial

Il semble que quand une application WPF démarre, rien n’est actif.

C'est vraiment bizarre. Tous les autres frameworks que j'ai utilisés font exactement ce que vous attendez: met l'accent sur le premier contrôle dans l'ordre de tabulation. Mais j'ai confirmé qu'il s'agissait de WPF, et pas seulement de mon application. Si je crée une nouvelle fenêtre et que je place simplement une zone de texte dans celle-ci et que je l'exécute, la zone de texte n'a pas le focus jusqu'à ce que je clique dessus ou que je clique sur Tab. . Beurk.

Mon application actuelle est plus compliquée qu'une simple zone de texte. J'ai plusieurs couches de contrôles utilisateur dans UserControls. L'un de ces UserControls a Focusable = "True" et les gestionnaires KeyDown/KeyUp, et je veux qu'il ait le focus dès que ma fenêtre s'ouvre. Je suis toujours un peu novice sur WPF, cependant, et je n'ai pas beaucoup de chance de savoir comment faire cela.

Si je lance mon application et que j'appuie sur la touche de tabulation, la mise au point se fait sur le contrôle que je peux mettre au point et le système commence à fonctionner comme je le souhaite. Mais je ne veux pas que mes utilisateurs soient obligés d'appuyer sur Tab avant de pouvoir utiliser la fenêtre.

J'ai joué avec FocusManager.FocusedElement, mais je ne suis pas sûr du contrôle sur lequel le définir (la fenêtre de niveau supérieur? Le parent qui contient le contrôle focusable? Le contrôle focusable lui-même?) Ou sur quoi le définir.

Que dois-je faire pour que mon contrôle profondément imbriqué ait le focus initial dès que la fenêtre s'ouvre? Ou mieux encore, pour focaliser le premier contrôle focalisable dans l'ordre de tabulation?

167
Joe White

J'ai eu la bonne idée de creuser dans Reflector pour voir où la propriété Focusable est utilisée, et j'ai trouvé le moyen de trouver cette solution. J'ai juste besoin d'ajouter le code suivant au constructeur de ma fenêtre:

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

Cela sélectionnera automatiquement le premier contrôle dans l'ordre de tabulation. Il s'agira donc d'une solution générale qui devrait pouvoir être glissée dans n'importe quelle fenêtre et Just Work.

149
Joe White

Cela fonctionne aussi:

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

   <DataGrid x:Name="SomeElement">
     ...
   </DataGrid>
</Window>
137
Sean

Basé sur le comportement accepté answer implémenté en tant que comportement attaché:

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

Utilisez-le comme ceci:

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

J'ai trouvé une autre solution possible. Mark Smith a publié l'extension de balisage FirstFocusedElement à utiliser avec FocusManager.FocusedElement.

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

Vous pouvez facilement faire en sorte que le contrôle se définisse comme l'élément ciblé dans XAML.

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

Je n'ai jamais essayé de définir cela dans un contrôle utilisateur et de voir si cela fonctionne, mais cela se peut.

8
Simon Gillbee

Après avoir eu un 'WPF Initial Focus Nightmare' et basé sur quelques réponses sur pile, ce qui suit s’est révélé être pour moi la meilleure solution. 

Tout d’abord, ajoutez votre App.xaml OnStartup () aux éléments suivants:

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

Ajoutez ensuite l'événement 'WindowLoaded' également dans 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));

        }));
    }

Le problème de thread doit être utilisé car la focalisation initiale de WPF échoue principalement en raison de certaines conditions de concurrence du framework.

J'ai trouvé la solution suivante la meilleure car elle est utilisée globalement pour l'ensemble de l'application.

J'espère que ça aide...

Oran

8
OrPaz

Le même problème a-t-il été résolu avec une solution simple: Dans la fenêtre principale:

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

Dans le contrôle utilisateur:

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

Une version minimale de La réponse de Mizipzor pour 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));
}

Utilisez dans votre XAML:

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

Si vous êtes comme moi et que vous utilisez des frameworks qui, en quelque sorte, dérangent les comportements de focus de base et rendent toutes les solutions ci-dessus inutiles, vous pouvez toujours le faire:

1 - Notez l'élément qui fait la mise au point (quoi que ce soit!)

2 - Ajoutez ceci dans votre code derrière xxx.xaml.cs

private bool _firstLoad;

3 - Ajoutez ceci sur l'élément qui obtient le premier focus:

GotFocus="Element_GotFocus"

4 - Ajoutez la méthode Element_GotFocus dans le code derrière et spécifiez l'élément nommé WPF ayant besoin du premier focus:

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

5 - Gérer l'événement chargé

en XAML

Loaded="MyWindow_Loaded"   

dans xaml.cs

private void MyWindow_Loaded(object sender, RoutedEventArgs e)
{
        _firstLoad = true;
        this.Element_GotFocus(null, null);
}

J'espère que cela aidera comme solution de dernier recours

1
G.Dealmeida

J'ai aussi fait face au même problème. J'avais trois zones de texte dans le conteneur de canevas et je voulais que la première zone de texte soit ciblée à l'ouverture du contrôle utilisateur. Le code WPF a suivi le modèle MVVM. J'ai créé une classe de comportement distincte pour la focalisation de l'élément et je l'ai lié à mon point de vue.

Code de comportement de la toile

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

    }

}

Code pour la vue

<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>
0
BSG

La solution ci-dessus ne fonctionnait pas comme prévu pour moi, j'ai légèrement modifié le comportement proposé par Mizipzor comme suit:

De cette partie

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

Pour ça

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

Et je n'attache pas ce comportement à Window ou à UserControl, mais pour contrôler je veux commencer par faire le point, par exemple:

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

Oh, désolé pour le nommage différent, j'utilise le nom InitialFocus pour la propriété attachée.

Et cela fonctionne pour moi, peut-être que cela pourrait aider quelqu'un d'autre.

0
BrightShadow