Wpf Can';t将焦点设置为UserControl的子级

Wpf Can';t将焦点设置为UserControl的子级,wpf,focus,user-controls,focusmanager,Wpf,Focus,User Controls,Focusmanager,我有一个用户控件,其中包含一个文本框。加载主窗口时,我希望将焦点设置到此文本框,因此我将Focusable=“True”GotFocus=“UC_GotFocus”添加到UserControls定义中,并将FocusManager.FocusedElement=“{Binding ElementName=login}”添加到主窗口定义中。在UC\u GotFocus方法中,我只需对我想要关注的控件调用.Focus(),但这不起作用 我所需要做的就是在应用程序启动时在UserControl接收焦点

我有一个
用户控件
,其中包含一个
文本框
。加载主窗口时,我希望将焦点设置到此文本框,因此我将
Focusable=“True”GotFocus=“UC_GotFocus”
添加到
UserControl
s定义中,并将
FocusManager.FocusedElement=“{Binding ElementName=login}”添加到主窗口定义中。在
UC\u GotFocus
方法中,我只需对我想要关注的控件调用
.Focus()
,但这不起作用

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


感谢您的帮助。

WPF支持两种不同的焦点:

  • 键盘焦点
  • 逻辑焦点
  • FocusedElement
    属性获取或设置焦点范围内的逻辑焦点。我怀疑您的
    文本框
    具有逻辑焦点,但其包含的焦点范围不是活动焦点范围。因此,它没有键盘焦点


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

    最近我有一个列表框,其中包含一些文本块。我希望能够双击文本块并将其变成一个文本框,然后集中精力并选择所有文本,这样用户就可以开始键入新名称(类似于Adobe Layers)

    不管怎样,我是在一个活动中这样做的,但它就是不起作用。对我来说,这里的魔弹是确保我将事件设置为已处理。我认为这是在设置焦点,但一旦事件沿着这条路径发展,它就在切换逻辑焦点


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

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

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


    我最近修复了登录启动屏幕的问题,该屏幕在首次加载主窗口时通过故事板显示

    我相信有两个解决办法。一个是使包含元素成为焦点范围。另一个是处理由加载的窗口触发的故事板的故事板完成事件

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

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

    1) 使包含的元素成为焦点范围

    <Canvas FocusManager.IsFocusScope="True" Visibility="Collapsed">
        <TextBox x:Name="m_uxUsername" AcceptsTab="False" AcceptsReturn="False">
        </TextBox>
    </Canvas>
    
    请注意,调用item.Focus()会导致调用Keyboard.Focus(this),因此不需要显式调用它。看到这个问题了吗

  • 将用户控件设置为Focusable=“True”(XAML)
  • 处理控件上的GotFocus事件,并调用yourTextBox.Focus()
  • 处理窗口上加载的事件,并调用控件。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();
    
                                }));
                            }
                       );
    
    }
    

    我发现了一系列关于WPF focus的博客文章


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

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

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

        public MyWindow() {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(MyWindow_Loaded);
        }
    
        void MyWindow_Loaded(object sender, RoutedEventArgs e) {
            textBox.Focus();
        }
    

    在经历了一场“WPF初始焦点噩梦”之后,根据stack上的一些答案,以下内容证明是最好的解决方案

    首先,在启动时添加App.xaml(),如下所示:

    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初始焦点由于某些框架竞争条件而大部分失败

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

    希望对你有帮助


    Oran

    我有一个带有两个文本框的用户控件-堆栈面板。这些文本框是在CONSTRUCTOR中添加的,而不是在xaml中添加的。当我尝试聚焦第一个文本框时,什么都没有发生。
    加载事件的siggestion修复了我的问题。只需在加载事件和所有事件中调用control.Focus()。

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

    在控件的构造函数中:

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

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

  • 在XAML中,将元素设置为
    Focusable=“True”
  • element\u mousemove
    事件中创建简单检查:

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

  • 就我而言,它很好用。

    在尝试了上述建议的组合之后,我能够
    this.Loaded += (sender, e) => Keyboard.Focus(txtUsername);
    
    if(!element.IsKeyBoardFocused)
        element.Focus();
    
    private void OnWindowLoaded(object sender, RoutedEventArgs e)
    {
        ControlXYZ.Focus();
    }
    
    private void OnControlLoaded(object sender, RoutedEventArgs e)
    {
        TextBoxXYZ.Focus();
    }
    
                    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());
    
    <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);
    }
    
    <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>
    
    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();
                    }));
                });
            }
        }
    }