FocusedElement未被尊重

8
我使用Prism和MVVM创建了一个基本应用程序。目前,它只包括Shell和一个View/ViewModel。
在应用程序加载期间,我将View加载到主要区域,并显示在屏幕上。这个方法可行,但我无法让视图中的文本框获得焦点。尽管光标看起来在文本框内(虽然没有闪烁),但在我单击文本框之前,它不接受输入。
我已经在一个新项目中重新创建了这个问题,在该项目中,我只安装了prism/prism.unityextensions,设置了shell和view,并将视图加载到shell区域中。两个xaml文件都没有任何代码后备。
Shell
<Window x:Class="MVVMFocusTest.Shell"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:prism="http://www.codeplex.com/prism"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DockPanel LastChildFill="True">            
            <ContentControl Name="MainRegion" DockPanel.Dock="Top" prism:RegionManager.RegionName="MainRegion" />
        </DockPanel>
    </Grid>
</Window>

视图1

<UserControl x:Class="MVVMFocusTest.View1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <StackPanel>
        <Grid FocusManager.FocusedElement="{Binding ElementName=Username}">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="100" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition Height="Auto" />               
            </Grid.RowDefinitions>

            <Label Grid.Row="0" Grid.Column="0">Username</Label>
            <TextBox Name="Username" Grid.Row="0" Grid.Column="1" ToolTip="Enter Username" TabIndex="0" />
            <Label Grid.Row="1" Grid.Column="0">Password</Label>
            <PasswordBox Grid.Row="1" Grid.Column="1" Name="LoginPassword" PasswordChar="*" ToolTip="Enter Password" TabIndex="1" />

        </Grid>
    </StackPanel>
</UserControl>

有人能指出我做错了什么吗?据我所知,FocusManager.FocusedElement="{Binding ElementName=Username}" 应该足以设置焦点。

我会创建一个虚拟转换器并将其放置在绑定中:FocusManager.FocusedElement="{Binding ElementName=Username , Path=. , Converter={StaticResource SomeDummyConverter}}"现在,在转换器的转换函数中设置断点并观察您的文本框元素,当您设置焦点时,它可能会被禁用(IsEnabled = False)。 - eran otzap
2个回答

19
根据FocusManager文档,逻辑焦点与特定焦点范围内的FocusManager.FocusedElement有关。因此,并非必须具有逻辑焦点的元素也一定具有键盘焦点,但反之亦然,即具有键盘焦点的元素一定也具有逻辑焦点。如文档所述,FocusManager.FocusedElement保证了逻辑焦点而不是键盘焦点。因此,您可以创建一个类似于FocusManager.FocusedElement的附加行为,它将在一个元素上设置键盘焦点。您可以参考Setting keyboard focus in WPF中的代码来使用附加行为设置键盘焦点。
namespace Invoices.Client.Wpf.Behaviors
{
    using System.Windows;
    using System.Windows.Input;

    public static class KeyboardFocus
    {
        public static readonly DependencyProperty OnProperty;

        public static void SetOn(UIElement element, FrameworkElement value)
        {
            element.SetValue(OnProperty, value);
        }

        public static FrameworkElement GetOn(UIElement element)
        {
            return (FrameworkElement)element.GetValue(OnProperty);
        }

        static KeyboardFocus()
        {
            OnProperty = DependencyProperty.RegisterAttached("On", typeof(FrameworkElement), typeof(KeyboardFocus), new PropertyMetadata(OnSetCallback));
        }

        private static void OnSetCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
        {
            var frameworkElement = (FrameworkElement)dependencyObject;
            var target = GetOn(frameworkElement);

            if (target == null)
                return;

            frameworkElement.Loaded += (s, e) => Keyboard.Focus(target);
        }
    }
}

在XAML中使用 -

<UserControl xmlns:behaviors="clr-namespace:Invoices.Client.Wpf.Behaviors">
    <Grid behaviors:KeyboardFocus.On="{Binding ElementName=TextBoxToFocus}">
        <TextBox x:Name="TextBoxToFocus" />
    </Grid>
</UserControl>

2
我个人认为,首选方法是保持在XAML中。 - DonBoitnott
1
这太棒了。我还会将目标检索和Keyboard.Focus(target)提取到一个“OnLoaded”方法中,并添加textBox.CaretIndex = textBox.Text.Length以在更新时改善功能。 - GeorgiG

6

FocusManager.FocusedElement="{Binding ElementName=Username}"可以设置逻辑焦点但不是物理焦点。

物理焦点是正常的焦点,逻辑焦点是一种次级焦点,在wpf 4.0中仍有一些错误。

我建议您使用Keyboard.Focus(this.Username)


我不知道你在说什么,而且如果你要声称某人发布了错误的内容,一定要写清楚为什么是错误的。我的回答没有任何问题。我会为你引用MSDN上的内容,Eran。"FocusedElement是具有特定焦点范围的逻辑焦点元素。该对象可能具有键盘焦点,也可能没有。键盘焦点是指接收键盘输入的元素。" http://msdn.microsoft.com/en-us/library/system.windows.input.focusmanager.focusedelement(v=vs.110).aspx(请看逻辑焦点部分,逻辑焦点并非真正的焦点) - dev hedgehog
那个在View的codebehind中添加到Loaded事件中运行了。谢谢。不过,有没有一种方法可以通过xaml标记来实现呢?如果必须的话我可以接受在codebehind中实现,但是如果有可能最好避免。 - Obsidian Phoenix
@ObsidianPhoenix,即使使用FocusManager也有一种方法。没有任何附加属性或代码。我不知道你遇到了什么情况,所以建议你使用Keyboard.Focus(..)方法,但是在某些情况下,FocusManager也可以起作用。 - dev hedgehog

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接