Не удается установить фокус на дочерний элемент UserControl

у меня есть UserControl, которая содержит TextBox. Когда мое главное окно загружается, я хочу установить фокус на это текстовое поле, поэтому я добавил Focusable="True" GotFocus="UC_GotFocus" до UserControls определение и FocusManager.FocusedElement="{Binding ElementName=login}" к моему основному определению windows. В UC_GotFocus метод я просто называю .Focus() на элементе управления я хочу сосредоточиться, но это не работает.

все, что мне нужно сделать, это иметь TextBox на UserControl получать Фокус при запуске приложения.

любая помощь будет оценили, спасибо.

18 ответов


недавно я исправил эту проблему для заставки входа в систему, которая отображается через раскадровку при первой загрузке главного окна.

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

эта раскадровка делает холст имени пользователя и пароля видимым, а затем исчезает в 100% непрозрачный. Ключ в том, что элемент управления username не был виден до запуска раскадровки, и поэтому этот элемент управления не мог получить клавиатура фокус, пока он не был виден. На какое-то время меня сбило с толку то, что у него был "фокус" (т. е. фокус был правдой, но, как оказалось, это был только логический фокус), и я не знал, что WPF имел концепцию как логического, так и клавиатурного фокуса, пока не прочитал ответ Кента Бугаарта и не посмотрел на WPF от Microsoft ссылка текст

как только я это сделал, решение моей конкретной проблемы было простым:

1) Сделайте содержащий элемент областью фокусировки

<Canvas FocusManager.IsFocusScope="True" Visibility="Collapsed">
    <TextBox x:Name="m_uxUsername" AcceptsTab="False" AcceptsReturn="False">
    </TextBox>
</Canvas>

2) Прикрепите обработчик завершенных событий к раскадровке

    <Storyboard x:Key="Splash Screen" Completed="UserNamePassword_Storyboard_Completed">
...
</Storyboard>

и

3) Установите мое имя пользователя TextBox, чтобы иметь фокус клавиатуры в обработчике завершенных событий раскадровки.

void UserNamePassword_Storyboard_Completed(object sender, EventArgs e)
{
 m_uxUsername.Focus();
}

обратите внимание, что вызов позиции.Focus () приводит к клавиатуре вызова.Фокус(это), так вам не нужно называть это явно. См. этот вопрос о разница между клавиатурой.Фокус (пункт) и пункт.Фокусировать.


его глупо, но это работает:

Pop поток, который ждет некоторое время, а затем возвращается и устанавливает фокус, который вы хотите. Он даже работает в контексте хоста элемента.

private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{

 System.Threading.ThreadPool.QueueUserWorkItem(
                   (a) =>
                        {
                            System.Threading.Thread.Sleep(100);
                            someUiElementThatWantsFocus.Dispatcher.Invoke(
                            new Action(() =>
                            {
                                someUiElementThatWantsFocus.Focus();

                            }));
                        }
                   );

}

совсем недавно у меня был список, в котором размещались некоторые текстовые блоки. Я хотел иметь возможность дважды щелкнуть по текстовому блоку и превратить его в текстовое поле, а затем сосредоточиться на нем и выбрать весь текст, чтобы пользователь мог просто начать вводить новое имя (сродни слоям Adobe)

в любом случае, я делал это с событием, и это просто не работало. Волшебная пуля для меня здесь была убедиться, что я установил событие в handled. Я понял это был настройки фокуса, но как только событие шло по пути переключения логического фокуса.

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


"при установке начального фокуса при запуске приложения, элемент для получения фокуса должен быть подключен к PresentationSource и элемент должен иметь Focusable и IsVisible значение true. Рекомендуемое место для установки начального фокуса находится в обработчике событий Loaded " (MSDN)

просто добавьте обработчик событий "Loaded" в конструктор вашего окна (или элемента управления), и в этом обработчике событий вызовите метод Focus() для целевого элемента управления.

    public MyWindow() {
        InitializeComponent();
        this.Loaded += new RoutedEventHandler(MyWindow_Loaded);
    }

    void MyWindow_Loaded(object sender, RoutedEventArgs e) {
        textBox.Focus();
    }

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

                casted.Dispatcher.BeginInvoke(new Action<UIElement>(x =>
                {
                    x.Focus();
                }), DispatcherPriority.ApplicationIdle, casted);

нет резьбы.Сна, нет класса ThreadPool. Надеюсь, достаточно чисто. Может действительно использовать некоторую репутацию, чтобы я мог комментировать решения других народов.

обновление:

Так как люди, похоже, любят довольно код:

public static class WpfExtensions
{
    public static void BeginInvoke<T>(this T element, Action<T> action, DispatcherPriority priority = DispatcherPriority.ApplicationIdle) where T : UIElement
    {
        element.Dispatcher.BeginInvoke(priority, action);
    }
}

Теперь вы можете называть это так:

child.BeginInvoke(d => d.Focus());

Я нашел хорошую серию сообщений в блоге на WPF focus.

все они хороши для чтения, но 3-я часть специально посвящена настройке фокуса на элемент пользовательского интерфейса в UserControl.


  1. установите пользовательский элемент управления в Focusable= "True" (XAML)
  2. обработки события gotfocus событие на вашем контроле и вызове yourTextBox.Focus ()
  3. обработки загружается событие в вашем окне и вызов элемента управления.Focus ()

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


WPF поддерживает два разных вида фокуса:

  1. фокус
  2. логический фокус

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

Итак, вопрос в том, есть ли у вас несколько областей фокусировки в вашем визуальном дерево?


Я заметил проблему фокуса, конкретно связанную с размещением WPF UserControls в ElementHosts, которые содержатся в форме, заданной как дочерний MDI через свойство MdiParent.

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

проблема с установкой фокуса в WPF UserControl, размещенном в ElementHost в дочерней MDI-форме WindowsForms


после того, как у меня был "кошмар начального фокуса WPF" и на основе некоторых ответов на стек, следующее оказалось для меня лучшим решением.

Сначала добавьте свое приложение.xaml OnStartup () следующие действия:

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

затем добавьте событие "WindowLoaded" также в приложение.язык 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 в основном не удается из-за некоторых условий гонки фреймворка.

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

надеюсь, что это помогает...

Оране


что сделал трюк для меня был FocusManager.FocusedElement. Сначала я попытался установить его на UserControl, но это не сработало.

поэтому я попытался поместить его на первого ребенка UserControl:

<UserControl x:Class="WpfApplication3.UserControl1"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid FocusManager.FocusedElement="{Binding ElementName=MyTextBox, Mode=OneWay}">
    <TextBox x:Name="MyTextBox"/>
</Grid>

... и это сработало! :)


я преобразовал ответ fuzquat в метод расширения. Я использую это вместо Focus (), где Focus () не работает.

using System;
using System.Threading;
using System.Windows;

namespace YourProject.Extensions
{
    public static class UIElementExtension
    {
        public static void WaitAndFocus(this UIElement element, int ms = 100)
        {
            ThreadPool.QueueUserWorkItem(f =>
            {
                Thread.Sleep(ms);

                element.Dispatcher.Invoke(new Action(() =>
                {
                    element.Focus();
                }));
            });
        }
    }
}

У меня есть панель user control - stack с двумя текстовыми полями.Текстовые поля были добавлены в contructor, а не в xaml. Когда я пытаюсь сосредоточиться на первом текстовом поле, ничего не происходит. Siggestion с загруженным событием исправляет мою проблему. Только что звонили в управление.Focus () в загруженном событии и everthing.


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

в конструкторе элемента управления:

this.Loaded += (sender, e) => Keyboard.Focus(txtUsername);

попробовав комбинации предложений выше, я смог надежно назначить фокус желаемому текстовому полю на дочернем UserControl со следующим. В принципе, сосредоточьтесь на дочернем элементе управления и попросите дочерний UserControl сосредоточить внимание на его текстовом поле. Оператор фокуса TextBox вернул true сам по себе, однако не дал желаемого результата, пока UserControl не получил фокус. Я также должен отметить, что UserControl не смог запросить фокус для себя и имел от окна.

для краткости я оставил регистрацию загруженных событий в окне и UserControl.

окно

private void OnWindowLoaded(object sender, RoutedEventArgs e)
{
    ControlXYZ.Focus();
}

UserControl

private void OnControlLoaded(object sender, RoutedEventArgs e)
{
    TextBoxXYZ.Focus();
}

Я установил его в PageLoaded () или control loaded, но затем я вызываю асинхронную службу WCF и делаю вещи, которые, кажется, теряют фокус. Я должен установить его в конце всего, что я делаю. Это нормально и все, но иногда я делаю изменения в коде, а затем забываю, что я также устанавливаю курсор.


мне не нравятся решения с установкой другой области вкладки для UserControl. В этом случае при навигации по клавиатуре у вас будет два разных курсора: на окне и другой - внутри пользовательского элемента управления. Мое решение - просто перенаправить фокус с пользовательского контроля на внутренний дочерний контроль. Установите пользовательский элемент управления focusable (потому что по умолчанию его false):

<UserControl ..... Focusable="True">

и переопределить обработчики событий фокуса в коде:

protected override void OnGotFocus(RoutedEventArgs e)
{
    base.OnGotFocus(e);
    MyTextBox.Focus();
}

protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
    base.OnGotKeyboardFocus(e);
    Keyboard.Focus(MyTextBox);
}

у меня была такая же проблема с настройкой фокуса клавиатуры на холст в пользовательском управлении WPF. Мое решение

  1. в XAML установите элемент в Focusable="True"
  2. на element_mousemove событие создание простой проверки:

    if(!element.IsKeyBoardFocused)
        element.Focus();
    

В моем случае это отлично работает.