【问题标题】:Why does WPF IsKeyboardFocused provide false information?为什么 WPF IsKeyboardFocused 提供虚假信息?
【发布时间】:2018-01-08 20:24:36
【问题描述】:

我尝试遵循此问题的答案中描述的模式。 How to set focus to textbox using MVVM?

但是,我在键盘焦点的概念上遇到了麻烦。如果我有记事本或其他一些应用程序与我的 WPF 应用程序同时运行并单击记事本将键盘焦点放在那里,然后做一些事情使我的其他应用程序将焦点放在它的一个文本框中,然后触发给出我的应用程序的文本框现在具有键盘焦点的视觉提示。但是,当我开始输入时,我可以看到情况并非如此,因为文本实际上是进入记事本。

这是我的触发器的 xaml。

<TextBox.Style>
    <Style TargetType="{x:Type TextBox}">
        <Style.Triggers>
            <DataTrigger Binding="{Binding ReadyForDataEntry}" Value="True">
                <Setter Property="FocusManager.FocusedElement" Value="{Binding RelativeSource={RelativeSource Self}}" />
           </DataTrigger>
            <Trigger Property="IsKeyboardFocused" Value="true">
                <Setter Property="Background" Value="Lavender"/>
                <Setter Property="BorderBrush" Value="Blue"/>
            </Trigger>
        </Style.Triggers>
 </Style>

基本上文本框有时会亮起边框和背景颜色,指示该文本框的 IsKeyboardFocused = true,即使键盘输入将由最后单击的任何应用程序(例如,一个笔记、记事本)接收。什么是我失踪了?当键盘焦点显然根本不正确时,为什么 WPF 控件会将 IsKeyboardFocused 设置为 true?

【问题讨论】:

  • 完整的最小示例请。您的示例在我的机器上运行良好
  • 什么意思?你完全按照我说的做了吗?
  • 是的。单击记事​​本会将背景变回白色(默认)。您的 DataTrigger 实际上可能是问题所在。一个完整的最小示例总是有帮助
  • 我正在努力。最初单击记事本不是问题。问题是当键盘焦点丢失到记事本后服务触发事件将焦点放回控件时。问题是键盘焦点并没有真正回到控件中,即使触发器点亮它。我必须使用会复制问题的后台服务编写一些代码,然后我将发布一个 github 链接。创建一个最小的示例需要一些时间,但我会在有链接时编辑问题。
  • 我在答案中包含了一个最小的示例,它可靠地重现了 Windows 7 上的问题。这是一个古老的已知问题(至少我知道)。

标签: c# wpf


【解决方案1】:

你没有做错任何事;这是 WPF 的一个已知怪癖。

当控件获得逻辑焦点时,WPF 也会尝试为其提供键盘焦点。 但是,当您在非活动 WPF 应用程序中分配键盘焦点时,应用程序表现 就好像它是活动。这意味着,除其他外,聚焦的TextBox 将显示一个闪烁的插入符号,并且将设置IsKeyboardFocused 和相关属性。

我过去曾看到过这个问题,重现起来很简单。

Xaml:

<Window x:Class="WpfTest.FocusTest"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Grid>
    <StackPanel HorizontalAlignment="Center"
                VerticalAlignment="Center">
      <TextBox x:Name="_textBox"
               Width="150">
        <TextBox.Style>
          <Style TargetType="{x:Type TextBox}">
            <Style.Triggers>
              <Trigger Property="IsKeyboardFocused"
                       Value="true">
                <Setter Property="Background"
                        Value="Lavender" />
                <Setter Property="BorderBrush"
                        Value="Blue" />
              </Trigger>
            </Style.Triggers>
          </Style>
        </TextBox.Style>
      </TextBox>
      <Button Margin="0,7,0,0"
              Content="_Click Me"
              Click="OnButtonClick" />
    </StackPanel>
  </Grid>
</Window>

背后的代码:

public partial class FocusTest
{
    public FocusTest()
    {
        InitializeComponent();
    }

    private void OnButtonClick(object sender, RoutedEventArgs e)
    {
        _textBox.Text = "";

        // NOTE: Requires System.Reactive.Core, available in NuGet.
        System.Reactive.Concurrency.Scheduler.Default.Schedule(
            TimeSpan.FromSeconds(5),
            () => this.Dispatcher.BeginInvoke(new Action(this.SetFocus)));
    }

    private void SetFocus()
    {
        _textBox.Text = "Focused";
        FocusManager.SetFocusedElement(_textBox, _textBox);
    }
}

点击按钮,Alt+Tab 切换到记事本,然后等待 5 秒,让 TextBox 获得焦点。 IsKeyboardFocused 触发器被触发,闪烁的插入符号出现,但键盘输入仍发送到记事本。

这里的关键点是,只有当一个元素获得焦点而另一个应用程序处于活动状态时才会出现问题(因此会出现人为延迟)。请注意,如果将 SetFocusedElement 调用替换为 Keyboard.Focus(_textBox)_textBox.Focus() 和其他变体,问题仍然存在。

不幸的是,我不知道有一种可靠的、简单的方法来解决这个问题。我不记得我花了多少时间在这上面,但我最终认为这不值得麻烦。这不是经常出现的事情。

【讨论】:

  • 当您陈述解决此问题的非 hacky 方式时,您的确切意思是什么?通过修复,您的意思是让 IsKeyboardFocused 在另一个应用程序处于活动状态时保持为假,或者有没有办法让我自己的应用程序也成为活动应用程序?也许这里真正的问题是另一个应用程序处于活动状态,但我希望激活我的应用程序。另一方面,也许我想要的不是合适的设计。
  • @shawn1874 我只是说我花了一些时间试图“解决”这个问题,但我没有想出一个可以接受的解决方案。我不记得我是否找到了一个 hacky 解决方案或根本没有找到解决方案。 '修复'本来应该是“修复它,这样应用程序的行为就不会像它实际上没有键盘焦点一样”。我不认为你能得到你想要的行为。至少从 Windows Vista/7 开始,您的应用程序只能在 (1) 您的应用程序处于活动状态时更改前台窗口;或 (2) 活动应用程序已授予您的应用程序更改前台窗口的权限。
  • 好的。我找到了一种将我的应用程序窗口设置为相关用例的活动窗口的方法。因为我只在发现设备后才这样做一次,它完成了我需要的工作并防止了这个奇怪的问题发生。我的目标是 Windows 7,但听起来你暗示这不适用于在 Windows 8 或 10 上运行的应用程序。
  • @shawn1874 我的理解是它也不应该在 Windows 7 上运行。据我所知,您只能从当前活动的应用程序中设置前景窗口 (1),或者 (2) 当当前活动的应用程序允许您这样做时,例如,通过调用 Win32 方法 AllowSetForegroundWindow。也许我弄错了,但是当我有单独的应用程序和启动程序进程时,如果启动程序进程明确允许它这样做,我只能让应用程序将自己置于前台。但是,嘿,如果你成功了,那就太好了:)。
  • @shawn1874 另外,请确保在没有附加调试器的情况下使用 and 进行测试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-03
  • 2015-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多