WPF и начальный фокус - PullRequest
174 голосов
/ 03 мая 2009

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

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

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

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

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

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

Ответы [ 12 ]

152 голосов
/ 04 мая 2009

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

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

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

142 голосов
/ 28 августа 2009

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

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

   <DataGrid x:Name="SomeElement">
     ...
   </DataGrid>
</Window>
57 голосов
/ 23 января 2012

На основании принятого ответа , реализованного в виде прикрепленного поведения:

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">
14 голосов
/ 10 мая 2009

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

<UserControl x:Class="FocusTest.Page2"
    xmlns:FocusTest="clr-namespace:FocusTest"
    FocusManager.FocusedElement="{FocusTest:FirstFocusedElement}">
8 голосов
/ 28 февраля 2015

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

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

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

8 голосов
/ 15 февраля 2013

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

  <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
        }
8 голосов
/ 24 января 2011

После того, как у меня был «кошмар 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 в основном не выполняется из-за некоторых базовых условий гонки.

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

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

Оран

1 голос
/ 02 февраля 2018

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

1 - Обратите внимание на элемент, который получает фокус (что бы это ни было!)

2 - добавьте это в свой код за xxx.xaml.cs

private bool _firstLoad;

3 - Добавьте это к элементу, который получает первый фокус:

GotFocus="Element_GotFocus"

4 - Добавьте метод Element_GotFocus в коде позади и укажите именованный элемент WPF, которому нужен первый фокус:

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

5 - Управление загруженным событием

в XAML

Loaded="MyWindow_Loaded"   

в xaml.cs

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

Надеюсь, это поможет в качестве крайней меры

1 голос
/ 02 февраля 2017

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

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

<Window local:FocusBehavior.GiveInitialFocus="True" />
0 голосов
/ 12 февраля 2018

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

Из этой части

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

К этому

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

И я не привязываю это поведение к Window или UserControl, но для управления хочу изначально сфокусироваться, например ::

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

Ой, простите за другое наименование. Я использую имя InitialFocus для прикрепленного свойства.

И это работает для меня, может быть, это может помочь кому-то еще.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...