Не удается установить фокус на дочерний элемент UserControl
у меня есть UserControl
, которая содержит TextBox
. Когда мое главное окно загружается, я хочу установить фокус на это текстовое поле, поэтому я добавил Focusable="True" GotFocus="UC_GotFocus"
до UserControl
s определение и 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.
- Часть 1: это в основном фокус
- Часть 2: изменение фокуса WPF в коде
- Часть 3: переключение фокуса на первый доступный элемент в WPF
все они хороши для чтения, но 3-я часть специально посвящена настройке фокуса на элемент пользовательского интерфейса в UserControl.
- установите пользовательский элемент управления в Focusable= "True" (XAML)
- обработки события gotfocus событие на вашем контроле и вызове yourTextBox.Focus ()
- обработки загружается событие в вашем окне и вызов элемента управления.Focus ()
у меня есть пример приложения, работающего с этим решением, как я печатаю. Если это не работает для вас, должно быть что-то конкретное для вашего приложения или среды вот в чем проблема. В вашем первоначальном вопросе я думаю, что привязка вызывает проблему. Надеюсь, это поможет.
WPF поддерживает два разных вида фокуса:
- фокус
- логический фокус
на FocusedElement
свойство получает или задает логический фокус в области фокуса. Я подозреваю, что ваш TextBox
тут имеют логический фокус, но его содержащая область фокуса не является активной областью фокуса. Ergo, it не в фокусе.
Итак, вопрос в том, есть ли у вас несколько областей фокусировки в вашем визуальном дерево?
Я заметил проблему фокуса, конкретно связанную с размещением WPF UserControls в ElementHosts, которые содержатся в форме, заданной как дочерний MDI через свойство MdiParent.
Я не уверен, что это та же проблема, что и другие, но вы копаетесь в деталях, следуя ссылке ниже.
после того, как у меня был "кошмар начального фокуса 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. Мое решение
- в XAML установите элемент в
Focusable="True"
-
на
element_mousemove
событие создание простой проверки:if(!element.IsKeyBoardFocused) element.Focus();
В моем случае это отлично работает.