无法将焦点设置为UserControl的子节点

时间:2009-03-23 14:16:30

标签: wpf focus user-controls focusmanager

我有UserControl,其中包含TextBox。当我的主窗口加载时,我想将焦点设置为此文本框,因此我将Focusable="True" GotFocus="UC_GotFocus"添加到UserControl的定义,并将FocusManager.FocusedElement="{Binding ElementName=login}"添加到我的主窗口定义中。在UC_GotFocus方法中,我只需在我想要关注的控件上调用.Focus(),但这不起作用。

我需要做的就是在应用程序启动时TextBox获得焦点UserControl

任何帮助都将不胜感激,谢谢。

18 个答案:

答案 0 :(得分:26)

我最近修复了这个问题,因为当首次加载主窗口时,通过故事板显示登录启动画面。

我相信修复有两个关键。一个是使包含元素成为焦点范围。另一个是处理由加载窗口触发的故事板的Storyboard Completed事件。

此故事板使用户名和密码画布可见,然后逐渐变为100%不透明。关键是在故事板运行之前,用户名控件不可见,因此该控件在可见之前无法获得键盘焦点。让我离开一段时间的是它有“焦点”(即焦点是真的,但事实证明这只是逻辑焦点)并且我不知道WPF在阅读Kent Boogaart之前有逻辑和键盘焦点的概念回答并查看Microsoft的WPF link text

一旦我这样做,我的特定问题的解决方案很简单:

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,使键盘焦点在storyboard completed事件处理程序中。

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

请注意,调用item.Focus()会导致调用Keyboard.Focus(this),因此您无需显式调用它。请参阅有关difference between Keyboard.Focus(item) and item.Focus.

的此问题

答案 1 :(得分:16)

它很愚蠢,但确实有效:

弹出等待一段时间的线程,然后返回并设置您想要的焦点。它甚至可以在元素主机的上下文中工作。

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

                            }));
                        }
                   );

}

答案 2 :(得分:7)

就在最近,我有一个包含一些TextBlocks的列表框。我希望能够双击文本块并将其转换为TextBox,然后专注于它并选择所有文本,以便用户可以开始键入新名称(类似于Adobe图层)

无论如何,我是通过一个活动做到这一点而且它不起作用。我这里的神奇子弹是确保我将事件设置为处理。我认为它设置焦点,但是一旦事件发生故障,它就会切换逻辑焦点。

故事的寓意是,确保你将事件标记为已处理,这可能是你的问题。

答案 3 :(得分:6)

“在应用程序启动时设置初始焦点时,接收焦点的元素必须连接到PresentationSource,并且元素必须将Focusable和IsVisible设置为true。建议初始焦点的建议位置是“已加载的事件处理程序”(MSDN)

只需在Window(或Control)的构造函数中添加一个“Loaded”事件处理程序,并在该事件处理程序中调用目标控件上的Focus()方法。

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

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

答案 4 :(得分:5)

因为我尝试了一个fuzquat的解决方案并发现它是最通用的解决方案,我想我会分享一个不同的版本,因为有些人抱怨它看起来很乱。所以这是:

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

没有Thread.Sleep,没有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());

答案 5 :(得分:4)

我发现了一系列关于WPF焦点的博客文章。

它们都很好阅读,但第3部分专门处理将焦点设置为UserControl中的UI元素。

答案 6 :(得分:3)

WPF支持两种不同的焦点:

  1. 键盘焦点
  2. 逻辑焦点
  3. FocusedElement属性获取或设置焦点范围内的逻辑焦点。我怀疑您的TextBox 具有逻辑焦点,但其包含的焦点范围不是主动焦点范围。因此,它 没有键盘焦点。

    所以问题是,你的可视化树中有多个焦点范围吗?

答案 7 :(得分:3)

  1. 将您的用户控件设置为 Focusable =“True”(XAML)
  2. 在您的控件上处理 GotFocus 事件并调用 yourTextBox.Focus()
  3. 在您的窗口处理已加载事件并致电 yourControl.Focus()
  4. 在我输入时,我有一个使用此解决方案运行的示例应用。如果这对您不起作用,则必须存在导致问题的应用或环境特定的内容。在您的原始问题中,我认为绑定导致问题。 我希望这会有所帮助。

答案 8 :(得分:1)

我将fuzquat的答案转换为扩展方法。我正在使用它而不是Focus(),其中Focus()不起作用。

var node = list.Cast<XmlNode>()
               .Where(n => n.Attributes["id"].InnerText == "abc")
               .Select(x => x.Attributes["abv"].InnerText);

答案 9 :(得分:1)

对我来说,诀窍是 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>

......它有效! :)

答案 10 :(得分:1)

在'WPF初始焦点噩梦'并基于堆栈上的一些答案后,以下证明我是最佳解决方案。

首先,添加App.xaml OnStartup()以下内容:

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

然后在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));

        }));
    }

必须使用线程问题,因为WPF的初始焦点主要是由于某些框架竞争条件而失败。

我发现以下解决方案最佳,因为它在整个应用程序中全局使用。

希望它有所帮助...

奥兰

答案 11 :(得分:1)

我注意到一个焦点问题,特别是在ElementHosts中托管WPF UserControls,它包含在一个Form中,该Form通过MdiParent属性设置为MDI子级。

我不确定这是否与其他人遇到的问题相同,但您可以通过以下链接深入了解详情。

Issue with setting focus within a WPF UserControl hosted within an ElementHost in a WindowsForms child MDI form

答案 12 :(得分:0)

假设您要为Username文本框设置焦点,因此用户可以在每次显示时直接输入。

在您的控件的构造函数中:

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

答案 13 :(得分:0)

在尝试上述建议的组合之后,我能够使用以下内容可靠地将焦点分配给子UserControl上的所需文本框。基本上,将焦点放在子控件上,让子UserControl将焦点放在TextBox上。 TextBox的焦点语句本身返回true,但是在UserControl也被赋予焦点之前没有产生所需的结果。我还应该注意到,UserControl无法为自己请求焦点,必须由Window提供。

为简洁起见,我省略了在Window和UserControl上注册Loaded事件。

窗口

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

用户控件

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

答案 14 :(得分:0)

我在PageLoaded()或控件加载中设置它,但后来我正在调用WCF异步服务并做一些似乎失去焦点的东西。我必须在我做的所有事情的最后设置它。这很好,但有时我会对代码进行更改,然后我忘记了我也在设置光标。

答案 15 :(得分:0)

我有用户控件 - 带有两个文本框的堆栈面板。文本框是在构造函数中添加的,而不是在xaml中。当我尝试聚焦第一个文本框时,没有任何事情发生。 使用Loaded事件的siggestion修复了我的问题。刚刚在Loaded事件和everthing中调用control.Focus()。

答案 16 :(得分:0)

我不喜欢为UserControl设置另一个标签范围的解决方案。在这种情况下,当您通过键盘导航时,您将有两个不同的插入符号:在窗口上,另一个 - 在用户控件内部。我的解决方案只是将焦点从用户控件重定向到内部子控件。将用户控件设置为可聚焦(因为默认情况下为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);
}

答案 17 :(得分:-2)

我在WPF用户控件中将键盘焦点设置为画布时遇到了同样的问题。 我的解决方案

  1. 在XAML中将元素设置为Focusable="True"
  2. element_mousemove事件中创建简单检查:

    if(!element.IsKeyBoardFocused)
        element.Focus();
    
  3. 就我而言,它运作良好。