wpf 无法将焦点设置到 UserControl 的子级
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/673536/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Can't set focus to a child of UserControl
提问by EightyOne Unite
I have a UserControl
which contains a TextBox
. When my main window loads I want to set the focus to this textbox so I added Focusable="True" GotFocus="UC_GotFocus"
to the UserControl
s definition and FocusManager.FocusedElement="{Binding ElementName=login}"
to my main windows definition. In the UC_GotFocus
method i simply call .Focus()
on the control i want to focus on but this doesn't work.
我有一个UserControl
包含TextBox
. 当我的主窗口加载时,我想将焦点设置到这个文本框,所以我添加Focusable="True" GotFocus="UC_GotFocus"
到UserControl
s 定义和FocusManager.FocusedElement="{Binding ElementName=login}"
我的主窗口定义。在该UC_GotFocus
方法中,我只是调用.Focus()
我想要关注的控件,但这不起作用。
All i need to do is have a TextBox
in a UserControl
receive focus when the application starts.
我需要做的就是在应用程序启动时有TextBox
一个UserControl
接收焦点。
Any help would be appreciated, thanks.
任何帮助将不胜感激,谢谢。
采纳答案by eesh
I recently fixed this problem for a login splash screen that is being displayed via a storyboard when the main window is first loaded.
我最近为登录初始屏幕解决了这个问题,该问题在主窗口首次加载时通过情节提要显示。
I believe there were two keys to the fix. One was to make the containing element a focus scope. The other was to handle the Storyboard Completed event for the storyboard that was triggered by the window being loaded.
我相信修复有两个关键。一种是使包含元素成为焦点范围。另一个是处理由正在加载的窗口触发的故事板的 Storyboard Completed 事件。
This storyboard makes the username and password canvas visible and then fades into being 100% opaque. The key is that the username control was not visible until the storyboard ran and therefore that control could not get keyboardfocus until it was visible. What threw me off for awhile was that it had "focus" (i.e. focus was true, but as it turns out this was only logical focus) and I did not know that WPF had the concept of both logical and keyboard focus until reading Kent Boogaart's answer and looking at Microsoft's WPF link text
这个故事板使用户名和密码画布可见,然后逐渐变为 100% 不透明。关键是用户名控件在故事板运行之前不可见,因此该控件在可见之前无法获得键盘焦点。有一段时间让我失望的是它有“焦点”(即焦点是真的,但事实证明这只是逻辑焦点)而且我不知道 WPF 有逻辑和键盘焦点的概念,直到阅读 Kent Boogaart 的回答并查看 Microsoft 的 WPF链接文本
Once I did that the solution for my particular problem was straightforward:
一旦我这样做了,我的特定问题的解决方案就很简单了:
1) Make the containing element a focus scope
1) 使包含元素成为焦点作用域
<Canvas FocusManager.IsFocusScope="True" Visibility="Collapsed">
<TextBox x:Name="m_uxUsername" AcceptsTab="False" AcceptsReturn="False">
</TextBox>
</Canvas>
2) Attach a Completed Event Handler to the Storyboard
2) 将一个已完成的事件处理程序附加到故事板
<Storyboard x:Key="Splash Screen" Completed="UserNamePassword_Storyboard_Completed">
...
</Storyboard>
and
和
3) Set my username TextBox to have the keyboard focus in the storyboard completed event handler.
3) 将我的用户名 TextBox 设置为故事板完成事件处理程序中的键盘焦点。
void UserNamePassword_Storyboard_Completed(object sender, EventArgs e)
{
m_uxUsername.Focus();
}
Note that calling item.Focus() results in the call Keyboard.Focus(this), so you don't need to call this explicitly. See this question about the difference between Keyboard.Focus(item) and item.Focus.
请注意,调用 item.Focus() 会导致调用 Keyboard.Focus(this),因此您无需显式调用 this。请参阅有关Keyboard.Focus(item) 和 item.Focus 之间区别的问题。
回答by fuzquat
Its stupid but it works:
它愚蠢但有效:
Pop a thread that waits a while then comes back and sets the focus you want. It even works within the context of an element host.
弹出一个等待一段时间然后返回并设置您想要的焦点的线程。它甚至可以在元素宿主的上下文中工作。
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();
}));
}
);
}
回答by Vassi
Just recently I had a list-box that housed some TextBlocks. I wanted to be able to double click on the text block and have it turn into a TextBox, then focus on it and select all the text so the user could just start typing the new name (Akin to Adobe Layers)
就在最近,我有一个包含一些 TextBlock 的列表框。我希望能够双击文本块并将其变成一个文本框,然后专注于它并选择所有文本,这样用户就可以开始输入新名称(类似于 Adobe 图层)
Anyway, I was doing this with an event and it just wasn't working. The magic bullet for me here was making sure that I set the event to handled. I figure it wassetting focus, but as soon as the event went down the path it was switching the logical focus.
无论如何,我正在通过一个事件来做这件事,但它不起作用。对我来说,这里的魔法子弹是确保我将事件设置为处理。我认为它正在设置焦点,但是一旦事件沿着路径进行,它就会切换逻辑焦点。
The moral of the story is, make sure you're marking the event as handled, that might be your issue.
这个故事的寓意是,确保您将事件标记为已处理,这可能是您的问题。
回答by David
“When setting initial focus at application startup, the element to receive focus must be connected to a PresentationSource and the element must have Focusable and IsVisible set to true. The recommended place to set initial focus is in the Loaded event handler" (MSDN)
“在应用程序启动时设置初始焦点时,接收焦点的元素必须连接到 PresentationSource,并且该元素必须将 Focusable 和 IsVisible 设置为 true。设置初始焦点的推荐位置在 Loaded 事件处理程序中”(MSDN)
Simply add a "Loaded" event handler in the constructor of your Window (or Control), and in that event handler call the Focus() method on the target control.
只需在窗口(或控件)的构造函数中添加一个“已加载”事件处理程序,然后在该事件处理程序中调用目标控件上的 Focus() 方法。
public MyWindow() {
InitializeComponent();
this.Loaded += new RoutedEventHandler(MyWindow_Loaded);
}
void MyWindow_Loaded(object sender, RoutedEventArgs e) {
textBox.Focus();
}
回答by Dbl
since i tried a fuzquat's solution and found it the most generic one, i thought i'd share a different version, since some complained about it looking messy. so here it is:
因为我尝试了 Fuzquat 的解决方案并发现它是最通用的解决方案,所以我想我会分享一个不同的版本,因为有些人抱怨它看起来很乱。所以这里是:
casted.Dispatcher.BeginInvoke(new Action<UIElement>(x =>
{
x.Focus();
}), DispatcherPriority.ApplicationIdle, casted);
no Thread.Sleep, no ThreadPool. Clean enough i hope. Could really use some rep so i can comment on other peoples solutions.
没有 Thread.Sleep,没有 ThreadPool。我希望足够干净。真的可以使用一些代表,这样我就可以评论其他人的解决方案。
UPDATE:
更新:
Since people seem to like pretty code:
因为人们似乎喜欢漂亮的代码:
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);
}
}
now you can call it like this:
现在你可以这样称呼它:
child.BeginInvoke(d => d.Focus());
回答by Kent Boogaart
WPF supports two different flavors of focus:
WPF 支持两种不同的焦点风格:
- Keyboard focus
- Logical focus
- 键盘焦点
- 逻辑焦点
The FocusedElement
property gets or sets logical focus within a focus scope. I suspect your TextBox
doeshave logical focus, but its containing focus scope is not the active focus scope. Ergo, it does nothave keyboard focus.
该FocusedElement
属性获取或设置焦点范围内的逻辑焦点。我怀疑您TextBox
确实具有逻辑焦点,但其包含的焦点范围不是活动焦点范围。因此,它没有键盘焦点。
So the question is, do you have multiple focus scopes in your visual tree?
所以问题是,您的可视化树中是否有多个焦点范围?
回答by Ashley Davis
I found a good series of blog posts on WPF focus.
我发现了一系列关于 WPF 焦点的很好的博客文章。
- Part 1: It's Basically Focus
- Part 2: Changing WPF focus in code
- Part 3: Shifting focus to the first available element in WPF
They are all good to read, but the 3rd part specifically deals with setting focus to a UI element in a UserControl.
它们都很好读,但第 3 部分专门处理将焦点设置到 UserControl 中的 UI 元素。
回答by Josh F.
- Set your user control to Focusable="True"(XAML)
- Handle the GotFocusevent on your control and call yourTextBox.Focus()
- Handle the Loadedevent on your window and call yourControl.Focus()
- 将您的用户控件设置为Focusable="True"(XAML)
- 处理控件上的GotFocus事件并调用yourTextBox.Focus()
- 处理窗口上的Loaded事件并调用yourControl.Focus()
I have a sample app running with this solution as I type. If this does not work for you, there must be something specific to your app or environment that causes the problem. In your original question, I think the binding is causing the problem. I hope this helps.
我在输入时有一个使用此解决方案运行的示例应用程序。如果这对您不起作用,则一定有特定于您的应用程序或环境的原因导致了问题。在您最初的问题中,我认为绑定是导致问题的原因。我希望这有帮助。
回答by OrPaz
After having a 'WPF Initial Focus Nightmare' and based on some answers on stack, the following proved for me to be the best solution.
在经历了“WPF Initial Focus Nightmare”并基于堆栈上的一些答案之后,以下对我来说证明是最好的解决方案。
First, add your App.xaml OnStartup() the followings:
首先,添加您的 App.xaml OnStartup() 以下内容:
EventManager.RegisterClassHandler(typeof(Window), Window.LoadedEvent,
new RoutedEventHandler(WindowLoaded));
Then add the 'WindowLoaded' event also in App.xaml :
然后在 App.xaml 中添加 'WindowLoaded' 事件:
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));
}));
}
The threading issue must be use as WPF initial focus mostly fails due to some framework race conditions.
必须使用线程问题,因为 WPF 初始焦点主要由于某些框架竞争条件而失败。
I found the following solution best as it is used globally for the whole app.
我发现以下解决方案最好,因为它在整个应用程序中全局使用。
Hope it helps...
希望能帮助到你...
Oran
奥兰
回答by white.zaz
I don't like solutions with setting another tab scope for UserControl. In that case, you will have two different carets when navigating by keyboard: on the window and the another - inside user control. My solution is simply to redirect focus from user control to inner child control. Set user control focusable (because by default its false):
我不喜欢为 UserControl 设置另一个选项卡范围的解决方案。在这种情况下,当通过键盘导航时,您将有两个不同的插入符号:在窗口上和另一个 - 内部用户控件。我的解决方案只是将焦点从用户控件重定向到内部子控件。设置用户控件可聚焦(因为默认情况下它是 false):
<UserControl ..... Focusable="True">
and override focus events handlers in code-behind:
并在代码隐藏中覆盖焦点事件处理程序:
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
MyTextBox.Focus();
}
protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
base.OnGotKeyboardFocus(e);
Keyboard.Focus(MyTextBox);
}