English 中文(简体)
无法将焦点设置为UserControl的子级
原标题:
  • 时间:2009-03-23 14:16:30
  •  标签:

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

我所需要做的就是在应用程序启动时,在UserControl中有一个TextBox接收焦点。

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

最佳回答

我最近修复了首次加载主窗口时通过情节提要显示的登录启动屏幕的问题。

我相信有两把钥匙可以解决问题。一种是使包含元素成为焦点范围。另一个是处理由加载的窗口触发的情节提要的“情节提要已完成”事件。

这个故事板使用户名和密码画布可见,然后逐渐变为100%不透明。关键是用户名控件在脚本运行之前是不可见的,因此该控件在可见之前无法获得键盘的焦点。让我有一段时间感到困惑的是,它有“焦点”(即焦点是真的,但事实证明这只是逻辑焦点),直到阅读Kent Boogaart的答案并查看微软的WPF链接文本

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

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) Set my username TextBox to have the keyboard focus in the storyboard completed event h和ler.

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) 和 item.Focus.

问题回答

这很愚蠢,但很有效:

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

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

                            }));
                        }
                   );

}

就在最近,我有一个列表框,里面有一些文本块。我希望能够双击文本块并将其转换为TextBox,然后专注于它并选择所有文本,这样用户就可以开始键入新名称(Akin to Adobe Layers)

不管怎样,我是在一个活动中这样做的,但它根本不起作用。对我来说,这里的灵丹妙药是确保我将事件设置为可处理的。我认为设置焦点,但事件一经过,它就切换了逻辑焦点。

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

“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)

只需在Window(或Control)的构造函数中添加一个“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);

没有线程。睡眠,没有线程池。我希望足够干净。

更新:

由于人们似乎喜欢漂亮的代码:

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支持两种不同风格的焦点:

  1. Keyboard focus
  2. Logical focus

<code>FocusedElement</code>属性获取或设置焦点范围内的逻辑焦点。我怀疑您的<code>TextBox</code><em>确实有逻辑焦点,但其包含的焦点范围不是活动焦点范围。呃,它没有键盘焦点。

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

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

它们都很好阅读,但第三部分专门讨论了将焦点设置为UserControl中的UI元素。

  1. Set your user control to Focusable="True" (XAML)
  2. Handle the GotFocus event on your control and call yourTextBox.Focus()
  3. Handle the Loaded event on your window and call 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.

在经历了一场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的初始焦点大多由于一些框架竞争条件而失败。

我发现以下解决方案最好,因为它在全球范围内用于整个应用程序。

希望能有所帮助。。。

奥兰

我将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();
                }));
            });
        }
    }
}

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

我不确定这是否与其他人正在经历的问题相同,但你可以通过下面的链接深入了解细节。

在WindowsForms子MDI窗体的ElementHost中托管的WPF UserControl中设置焦点的问题

我不喜欢为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);
}

对我有用的是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>

……它成功了!:)

I have user control - stack panel with two text boxes.The text boxes were added in contructor, not in the xaml. When i try to focus first text box, nothing happend. The siggestion with Loaded event fix my problem. Just called control.Focus() in Loaded event and everthing.

假设您想为用户名文本框设置焦点,这样用户就可以在每次出现时直接输入。

在您控制的构造函数中:

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

在尝试了以上建议的组合后,我能够通过以下内容可靠地将焦点分配给子用户控制上所需的文本框。基本上,将焦点放在子控件上,并让子用户控制将焦点放给它的TextBox。TextBox的焦点语句本身返回true,但直到用户控制也获得焦点后,才产生所需的结果。我还应该注意到,用户控制无法为自己请求焦点,必须由窗提供。

为了简洁起见,我省略了在窗和用户控制上注册Loaded事件。

private void On窗Loaded(object sender, RoutedEventArgs e)
{
    ControlXYZ.Focus();
}

用户控制

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

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

I had same problem with setting keyboard focus to canvas in WPF user control. My solution

  1. In XAML set element to Focusable="True"
  2. 在<code>element_musemove</code>事件中创建简单检查:

    if(!element.IsKeyBoardFocused)
        element.Focus();
    

就我而言,它运行良好。





相关问题