2025-04-08 06:00:04

WPF和初始焦点

当WPF应用程序启动时,似乎没有任何东西具有焦点。

这真的很奇怪。我所使用的其他框架所做的正是您所期望的:将初始焦点放在选项卡顺序中的第一个控件上。但我已经确认,这是WPF,不只是我的应用程序-如果我创建一个新的窗口,只是把一个文本框在它,并运行应用程序,文本框没有焦点,直到我点击它或按Tab。讨厌的东西。

我的实际应用程序比一个文本框更复杂。我在UserControls中有几个UserControls层。其中一个UserControls有Focusable="True"和KeyDown/KeyUp处理程序,我希望它在我的窗口打开时就有焦点。不过,我在某种程度上仍然是一个WPF新手,我没有太多的运气来弄清楚如何做到这一点。

如果我启动我的应用并按下Tab键,焦点就会转到我的可聚焦控件,它开始以我想要的方式工作。但我不希望我的用户在开始使用窗口之前必须按Tab键。

我使用过FocusManager。FocusedElement,但我不确定要在哪个控件上设置它(顶级窗口?包含可聚焦控件的父控件?可聚焦控件本身?)或者设置为什么。

我需要做什么让我的深嵌套控件有初始焦点,只要窗口打开?或者更好,聚焦第一个可聚焦控件在制表符的顺序?


当前回答

基于作为附加行为实现的可接受答案:

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">

其他回答

我有一个聪明的想法,通过Reflector挖掘,看看Focusable属性被使用的地方,并找到了这个解决方案。我只需要添加以下代码到我的窗口的构造函数:

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

这将自动选择选项卡顺序中的第一个控件,所以这是一个通用的解决方案,应该能够被拖放到任何窗口和只是工作。

我找到了另一个可能的解决方案。Mark Smith发布了一个FirstFocusedElement标记扩展,用于FocusManager.FocusedElement。

<UserControl x:Class="FocusTest.Page2"
    xmlns:FocusTest="clr-namespace:FocusTest"
    FocusManager.FocusedElement="{FocusTest:FirstFocusedElement}">

如果你像我一样,正在使用一些框架,在某种程度上,混淆了基本的焦点行为,并使上述所有解决方案都无关紧要,你仍然可以这样做:

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);
}

希望这将有助于作为最后的解决方案

您可以轻松地将控件本身设置为XAML中的焦点元素。

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

我从来没有试过在用户控件中设置这个,看看这是否有效,但它可能。

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