WPF 和初始焦点
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/817610/
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
WPF and initial focus
提问by Joe White
It seems that when a WPF application starts, nothing has focus.
似乎当 WPF 应用程序启动时,没有任何焦点。
This is really weird. Every other framework I've used does just what you'd expect: puts initial focus on the first control in the tab order. But I've confirmed that it's WPF, not just my app -- if I create a new Window, and just put a TextBox in it, and run the app, the TextBox doesn't have focus until I click on it or press Tab. Yuck.
这真的很奇怪。我使用过的所有其他框架都符合您的预期:将初始焦点放在 Tab 键顺序中的第一个控件上。但我已经确认它是 WPF,而不仅仅是我的应用程序——如果我创建一个新窗口,并在其中放置一个 TextBox,然后运行该应用程序,则 TextBox 没有焦点,直到我单击它或按 Tab . 哎呀。
My actual app is more complicated than just a TextBox. I have several layers of UserControls within UserControls. One of those UserControls has Focusable="True" and KeyDown/KeyUp handlers, and I want it to have the focus as soon as my window opens. I'm still somewhat of a WPF novice, though, and I'm not having much luck figuring out how to do this.
我的实际应用程序比 TextBox 更复杂。我在 UserControls 中有多层 UserControls。其中一个 UserControls 具有 Focusable="True" 和 KeyDown/KeyUp 处理程序,我希望它在我的窗口打开后立即获得焦点。不过,我仍然是一个 WPF 新手,而且我没有多少运气弄清楚如何做到这一点。
If I start my app and press the Tab key, then focus goes to my focusable control, and it starts working the way I want. But I don't want my users to have to hit Tab before they can start using the window.
如果我启动我的应用程序并按下 Tab 键,那么焦点就会转到我的可聚焦控件,它就会按照我想要的方式开始工作。但我不希望我的用户在开始使用窗口之前必须点击 Tab。
I've played around with FocusManager.FocusedElement, but I'm not sure which control to set it on (the top-level Window? the parent that contains the focusable control? the focusable control itself?) or what to set it to.
我玩过 FocusManager.FocusedElement,但我不确定将它设置在哪个控件上(顶级窗口?包含可聚焦控件的父级?可聚焦控件本身?)或将它设置为什么。
What do I need to do to get my deeply-nested control to have initial focus as soon as the window opens? Or better yet, to focus the first focusable control in the tab order?
我需要做什么才能让我的深层嵌套控件在窗口打开后立即获得初始焦点?或者更好的是,按 Tab 键顺序聚焦第一个可聚焦控件?
采纳答案by Joe White
I had the bright idea to dig through Reflector to see where the Focusable property is used, and found my way to this solution. I just need to add the following code to my Window's constructor:
我有一个聪明的想法来挖掘 Reflector 以查看使用 Focusable 属性的位置,并找到了解决此问题的方法。我只需要将以下代码添加到我的 Window 的构造函数中:
Loaded += (sender, e) =>
MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
This will automatically select the first control in the tab order, so it's a general solution that should be able to be dropped into any window and Just Work.
这将自动选择 Tab 键顺序中的第一个控件,因此它是一个通用解决方案,应该能够放入任何窗口和 Just Work。
回答by Sean
This works, too:
这也有效:
<Window FocusManager.FocusedElement="{Binding ElementName=SomeElement}">
<DataGrid x:Name="SomeElement">
...
</DataGrid>
</Window>
回答by Mizipzor
Based on the accepted answerimplemented as an attached behavior:
基于作为附加行为实现的公认答案:
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));
}
}
}
}
Use it like this:
像这样使用它:
<Window xmlns:Behaviors="clr-namespace:UI.Behaviors"
Behaviors:FocusBehavior.FocusFirst="true">
回答by Joe White
I found another possible solution. Mark Smith posted a FirstFocusedElement markup extensionfor use with FocusManager.FocusedElement.
我找到了另一种可能的解决方案。Mark Smith 发布了一个用于 FocusManager.FocusedElement的FirstFocusedElement 标记扩展。
<UserControl x:Class="FocusTest.Page2"
xmlns:FocusTest="clr-namespace:FocusTest"
FocusManager.FocusedElement="{FocusTest:FirstFocusedElement}">
回答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 Vladik Y
Had same problem solved it with simple solution: In the main window:
用简单的解决方案解决了同样的问题:在主窗口中:
<Window ....
FocusManager.FocusedElement="{Binding ElementName=usercontrolelementname}"
... />
In the user control:
在用户控件中:
private void UserControl_GotFocus_1(object sender, RoutedEventArgs e)
{
targetcontrol.Focus();
this.GotFocus -= UserControl_GotFocus_1; // to set focus only once
}
回答by Simon Gillbee
You can easily have the control set itself as the focused element in XAML.
您可以轻松地将控件本身设置为 XAML 中的焦点元素。
<Window>
<DataGrid FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}">
...
</DataGrid>
</Window>
I've never tried setting this in a usercontrol and seeing if this works, but it may.
我从来没有尝试过在用户控件中设置它并查看它是否有效,但它可能。
回答by Drew Noakes
A minimal version of Mizipzor's answerfor 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));
}
Use in your XAML:
在您的 XAML 中使用:
<Window local:FocusBehavior.GiveInitialFocus="True" />
回答by G.Dealmeida
If you are like me, and you are using some frameworks that, somehow, mess up with the basic focus behaviors, and make all solutions above irrelevant, you can still do this :
如果你像我一样,并且你正在使用一些框架,不知何故,弄乱了基本的焦点行为,并使上述所有解决方案无关紧要,你仍然可以这样做:
1 - Note the element which get the focus (whatever it is!)
1 - 注意获得焦点的元素(无论它是什么!)
2 - Add this in your code behind xxx.xaml.cs
2 - 将其添加到 xxx.xaml.cs 后面的代码中
private bool _firstLoad;
3 - Add this on the element which get the first focus :
3 - 将此添加到获得第一个焦点的元素上:
GotFocus="Element_GotFocus"
4 - Add the Element_GotFocus method in the code behind, and specify the WPF named element who need the first focus :
4 - 在后面的代码中添加 Element_GotFocus 方法,并指定需要第一个焦点的 WPF 命名元素:
private void Element_GotFocus(object sender, RoutedEventArgs e)
{
if(_firstLoad)
{
this.MyElementWithFistFocus.Focus();
_firstLoad = false;
}
}
5 - Manage the Loaded event
5 - 管理 Loaded 事件
in XAML
在 XAML 中
Loaded="MyWindow_Loaded"
in xaml.cs
在 xaml.cs 中
private void MyWindow_Loaded(object sender, RoutedEventArgs e)
{
_firstLoad = true;
this.Element_GotFocus(null, null);
}
Hope this will help as a last resort solution
希望这将有助于作为最后的解决方案
回答by BrightShadow
Above solution was not working as expected for me, I've changed slightly the behavior proposed by Mizipzor as following:
上面的解决方案对我来说没有按预期工作,我稍微改变了 Mizipzor 提出的行为如下:
From this part
从这部分
if ((bool)args.NewValue)
{
control.Loaded += (sender, e) =>
control.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
To this
对此
if ((bool)args.NewValue)
{
control.Loaded += (sender, e) => control.Focus();
}
ANd I'm not attaching this behavior to Window or UserControl, but to control I want to focus initially, e.g.:
而且我没有将此行为附加到 Window 或 UserControl,而是为了控制我最初想要关注的焦点,例如:
<TextBox ui:FocusBehavior.InitialFocus="True" />
Oh, sorry for different naming I'm using InitialFocus name for the attached property.
哦,对不起,我使用 InitialFocus 名称作为附加属性的不同命名。
And this is working for me, maybe it could help someone else.
这对我有用,也许可以帮助其他人。