WPF и первоначальный фокус

Похоже, что когда приложение WPF запускается, ничто не фокусируется.

Это действительно странно. Каждая другая структура, которую я использовал, делает именно то, что вы ожидаете: ставит первоначальный фокус на первый элемент управления в порядке вкладок. Но я подтвердил, что это WPF, а не только мое приложение - если я создаю новое окно и просто поставлю в него текстовое поле и запустим приложение, у TextBox не будет фокуса, пока я не нажму на него или не нажму Tab, Тьфу.

Мое фактическое приложение сложнее, чем просто TextBox. У меня есть несколько уровней UserControls в UserControls. Один из этих UserControls имеет обработчики Focusable = "True" и KeyDown/KeyUp, и я хочу, чтобы он имел фокус, как только откроется мое окно. Тем не менее, я по-прежнему новичок в WPF, и мне не очень повезло, как это сделать.

Если я запустил приложение и нажимаю клавишу Tab, тогда фокус переходит на мой настраиваемый элемент управления, и он начинает работать так, как я хочу. Но я не хочу, чтобы моим пользователям приходилось ударять Tab, прежде чем они смогут начать использовать это окно.

Я играл с FocusManager.FocusedElement, но я не уверен, какой элемент управления установить (окно верхнего уровня - родительский элемент, который содержит настраиваемый элемент управления - сам настраиваемый элемент управления) или что нужно установить это к.

Что мне нужно сделать, чтобы мой глубоко вложенный элемент управления имел первоначальный фокус, как только откроется окно? Или еще лучше, чтобы сфокусировать первый настраиваемый элемент управления в порядке вкладок?

Ответ 1

У меня была яркая идея перекопать Reflector, чтобы увидеть, где используется свойство Focusable, и нашел свой путь к этому решению. Мне просто нужно добавить следующий код к моему конструктору Window:

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

Это автоматически выберет первый элемент управления в порядке табуляции, так что это общее решение, которое должно быть удалено в любое окно и Just Work.

Ответ 2

Это тоже работает:

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

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

Ответ 3

Основываясь на принятом ответе, реализованном как приложенное поведение:

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

Используйте его следующим образом:

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

Ответ 4

Я нашел другое возможное решение. Марк Смит опубликовал расширение разметки FirstFocusedElement для использования с FocusManager.FocusedElement.

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

Ответ 5

После "WPF Initial Focus Nightmare" и основанного на некоторых ответах на стеке, для меня было лучшим решением.

Сначала добавьте App.xaml OnStartup() следующее:

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

Затем добавьте событие WindowLoaded также в 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));

        }));
    }

Проблема с потоками должна использоваться, поскольку первоначальный фокус WPF в основном не работает из-за некоторых условий рамочной гонки.

Я нашел следующее решение наилучшим, поскольку оно используется глобально для всего приложения.

Надеюсь, что это поможет...

Оран

Ответ 6

Если бы такая же проблема была решена с помощью простого решения: В главном окне:

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

В элементе управления пользователя:

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

Ответ 7

Вы можете легко настроить элемент управления как сфокусированный элемент в XAML.

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

Я никогда не пробовал устанавливать это в usercontrol и видеть, работает ли это, но может.

Ответ 8

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

Ответ 9

Минимальная версия Ответ Mizipzor для С# 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));
}

Используйте в своем XAML:

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

Ответ 10

Я также столкнулся с той же проблемой. У меня было три текстовых поля внутри контейнера для холста и хотелось, чтобы первое текстовое поле фокусировалось при открытии пользовательского элемента управления. Код WPF следовал за шаблоном MVVM. Я создал отдельный класс поведения для фокусировки элемента и привязал его к моему представлению следующим образом.

Код поведения холста

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

    }

}

Код для просмотра

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