it-swarm.com.de

WPF und anfänglicher Fokus

Es scheint, dass beim Start einer WPF-Anwendung nichts den Fokus hat.

Das ist wirklich komisch. Jedes andere Framework, das ich verwendet habe, erfüllt genau das, was Sie erwarten: Der anfängliche Fokus liegt auf dem ersten Steuerelement in der Tab-Reihenfolge. Ich habe jedoch bestätigt, dass es sich um WPF handelt, nicht nur um meine App. Wenn ich ein neues Fenster erstelle und einfach eine TextBox darin platziere und die App ausführt, hat die TextBox keinen Fokus, bis ich darauf klicke oder die Tabulatortaste drücke . Yuck.

Meine eigentliche App ist komplizierter als nur eine TextBox. Ich habe mehrere Ebenen von UserControls in UserControls. Eines dieser UserControls hat Focusable = "True" und KeyDown/KeyUp-Handler, und ich möchte, dass es den Fokus hat, sobald sich mein Fenster öffnet. Ich bin immer noch ein bisschen WPF-Neuling, und ich habe nicht viel Glück, um herauszufinden, wie das geht.

Wenn ich meine App starte und die Tabulatortaste drücke, wird der Fokus auf mein fokussierbares Steuerelement gesetzt, und es funktioniert wie gewünscht. Ich möchte jedoch nicht, dass meine Benutzer die Tabulatortaste drücken müssen, bevor sie das Fenster verwenden können.

Ich habe mit FocusManager.FocusedElement herumgespielt, aber ich bin mir nicht sicher, auf welches Steuerelement es eingestellt werden soll (das übergeordnete Fenster - das übergeordnete Element, das das fokussierbare Steuerelement enthält - das fokussierbare Steuerelement selbst) oder das einzustellende Element.

Was muss ich tun, damit mein tief verschachteltes Steuerelement den anfänglichen Fokus erhält, sobald das Fenster geöffnet wird? Oder noch besser, das erste fokussierbare Steuerelement in der Tab-Reihenfolge zu fokussieren?

167
Joe White

Ich hatte die gute Idee, durch den Reflektor zu graben, um zu sehen, wo die Eigenschaft Focusable verwendet wird, und habe zu dieser Lösung gefunden. Ich muss nur den folgenden Code zum Konstruktor meines Fensters hinzufügen:

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

Dadurch wird automatisch das erste Steuerelement in der Tabulatorreihenfolge ausgewählt. Es handelt sich also um eine allgemeine Lösung, die in jedes Fenster und in Just Work eingefügt werden kann.

149
Joe White

Das funktioniert auch:

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

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

Basierend auf der akzeptierten Antwort als angefügtes Verhalten implementiert:

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

Verwenden Sie es so:

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

Ich habe eine andere mögliche Lösung gefunden. Mark Smith hat eine FirstFocusedElement-Markierungserweiterung zur Verwendung mit FocusManager.FocusedElement veröffentlicht.

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

Sie können das Steuerelement einfach als fokussiertes Element in XAML festlegen.

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

Ich habe noch nie versucht, dies in einer Benutzerkontrolle einzustellen und zu sehen, ob dies funktioniert, aber es kann sein.

8
Simon Gillbee

Nach einem WPF Initial Focus Nightmare und basierend auf einigen Antworten im Stapel erwies sich das Folgende als die beste Lösung für mich. 

Fügen Sie zunächst App.xaml OnStartup () wie folgt hinzu:

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

Fügen Sie dann das Ereignis 'WindowLoaded' auch in App.xaml hinzu: 

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

        }));
    }

Das Threading-Problem muss verwendet werden, da der anfängliche Fokus von WPF aufgrund einiger Rahmenrennen meistens fehlschlägt.

Ich fand die folgende Lösung am besten, da sie global für die gesamte App verwendet wird.

Ich hoffe es hilft...

Oran

8
OrPaz

Hatte dasselbe Problem mit einer einfachen Lösung gelöst: Im Hauptfenster:

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

In der Benutzersteuerung:

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

Eine minimale Version von Mizipzors Antwort für 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));
}

Verwenden Sie in Ihrer XAML:

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

Wenn Sie wie ich sind und einige Frameworks verwenden, die irgendwie mit den grundlegenden Fokusverhalten durcheinander kommen und alle oben genannten Lösungen irrelevant machen, können Sie dies immer noch tun:

1 - Beachten Sie das Element, das den Fokus erhält (was auch immer es ist!)

2 - Fügen Sie dies in Ihren Code hinter xxx.xaml.cs ein

private bool _firstLoad;

3 - Fügen Sie dies dem Element hinzu, das den ersten Fokus erhält:

GotFocus="Element_GotFocus"

4 - Fügen Sie die Element_GotFocus-Methode im Code dahinter hinzu, und geben Sie das benannte WPF-Element an, das den ersten Fokus benötigt:

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

5 - Verwalten Sie das Loaded-Ereignis

in XAML

Loaded="MyWindow_Loaded"   

in xaml.cs

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

Hoffen Sie, dass dies als letzte Lösung helfen wird

1
G.Dealmeida

Ich stand auch vor dem gleichen Problem. Ich hatte drei Textfelder im Canvas-Container und wollte, dass das erste Textfeld beim Öffnen des Benutzersteuerelements angezeigt wird. Der WPF-Code folgte dem MVVM-Muster. Ich habe eine eigene Verhaltensklasse erstellt, um das Element zu fokussieren, und es auf diese Weise an meine Ansicht gebunden.

Verhaltenscode für Canvas

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 zur Ansicht

<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

Obige Lösung funktionierte nicht wie erwartet für mich, ich habe das von Mizipzor vorgeschlagene Verhalten wie folgt leicht geändert:

Aus diesem Teil

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

Zu diesem

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

Ich füge dieses Verhalten nicht an Window oder UserControl an, aber um zu steuern, dass ich mich anfangs konzentrieren möchte, z.

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

Tut mir leid für die andere Benennung. Ich benutze den InitialFocus-Namen für die angefügte Eigenschaft.

Und das funktioniert für mich, vielleicht könnte es jemand anderem helfen.

0
BrightShadow