You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF MVVM应用中USB扫码枪集成及文本框持续聚焦问题求助

WPF MVVM中保持TextBox始终聚焦(含窗口激活后自动聚焦)

问题原因

  • FocusManager.FocusedElement仅在界面初始化时生效,焦点变更后不会自动重置焦点。
  • 直接调用TextBox.Focus()无效,通常是因为调用时机不对(比如控件未完全加载),或是未处理窗口激活后的焦点恢复逻辑。

解决方案

方案1:使用附加属性(MVVM友好,无代码隐藏)

创建一个附加属性,监听控件的LostFocus事件和窗口的Activated事件,确保目标TextBox始终保持焦点:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

public static class FocusHelper
{
    public static readonly DependencyProperty KeepFocusProperty =
        DependencyProperty.RegisterAttached(
            "KeepFocus",
            typeof(bool),
            typeof(FocusHelper),
            new PropertyMetadata(false, OnKeepFocusChanged));

    public static bool GetKeepFocus(DependencyObject obj)
    {
        return (bool)obj.GetValue(KeepFocusProperty);
    }

    public static void SetKeepFocus(DependencyObject obj, bool value)
    {
        obj.SetValue(KeepFocusProperty, value);
    }

    private static void OnKeepFocusChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is TextBox textBox)
        {
            if ((bool)e.NewValue)
            {
                textBox.LostFocus += TextBox_LostFocus;
                var window = Window.GetWindow(textBox);
                window.Activated += Window_Activated;
            }
            else
            {
                textBox.LostFocus -= TextBox_LostFocus;
                var window = Window.GetWindow(textBox);
                window?.Activated -= Window_Activated;
            }
        }
    }

    private static void Window_Activated(object sender, EventArgs e)
    {
        if (sender is Window window)
        {
            foreach (var textBox in FindVisualChildren<TextBox>(window))
            {
                if (GetKeepFocus(textBox))
                {
                    textBox.Focus();
                    Keyboard.Focus(textBox);
                    break;
                }
            }
        }
    }

    private static void TextBox_LostFocus(object sender, RoutedEventArgs e)
    {
        if (sender is TextBox textBox && GetKeepFocus(textBox))
        {
            textBox.Dispatcher.BeginInvoke(() =>
            {
                textBox.Focus();
                Keyboard.Focus(textBox);
            });
        }
    }

    private static IEnumerable<T> FindVisualChildren<T>(DependencyObject parent) where T : DependencyObject
    {
        if (parent == null) yield break;

        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
        {
            var child = VisualTreeHelper.GetChild(parent, i);
            if (child is T t)
                yield return t;

            foreach (var childOfChild in FindVisualChildren<T>(child))
                yield return childOfChild;
        }
    }
}

XAML中直接引用:

<TextBox x:Name="TextBox2" 
         local:FocusHelper.KeepFocus="True" />

方案2:使用Blend行为(轻量MVVM方案)

如果项目已引用Microsoft.Xaml.Behaviors.Wpf NuGet包,可以创建一个专用行为:

using Microsoft.Xaml.Behaviors;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

public class KeepFocusBehavior : Behavior<TextBox>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.LostFocus += AssociatedObject_LostFocus;
        var window = Window.GetWindow(AssociatedObject);
        window.Activated += Window_Activated;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.LostFocus -= AssociatedObject_LostFocus;
        var window = Window.GetWindow(AssociatedObject);
        window?.Activated -= Window_Activated;
    }

    private void Window_Activated(object sender, EventArgs e)
    {
        AssociatedObject.Focus();
        Keyboard.Focus(AssociatedObject);
    }

    private void AssociatedObject_LostFocus(object sender, RoutedEventArgs e)
    {
        AssociatedObject.Dispatcher.BeginInvoke(() =>
        {
            AssociatedObject.Focus();
            Keyboard.Focus(AssociatedObject);
        });
    }
}

XAML中使用:

xmlns:i="http://schemas.microsoft.com/xaml/behaviors"

<TextBox x:Name="TextBox2">
    <i:Interaction.Behaviors>
        <local:KeepFocusBehavior />
    </i:Interaction.Behaviors>
</TextBox>

方案3:代码隐藏(快速实现,适合小型项目)

如果不介意少量代码隐藏,可以直接在窗口代码文件中处理:

public MainWindow()
{
    InitializeComponent();
    Activated += MainWindow_Activated;
    TextBox2.LostFocus += TextBox2_LostFocus;
}

private void MainWindow_Activated(object sender, EventArgs e)
{
    TextBox2.Focus();
    Keyboard.Focus(TextBox2);
}

private void TextBox2_LostFocus(object sender, RoutedEventArgs e)
{
    TextBox2.Dispatcher.BeginInvoke(() =>
    {
        TextBox2.Focus();
        Keyboard.Focus(TextBox2);
    });
}

关键注意点

  • 必须同时调用Focus()和Keyboard.Focus(),确保设置逻辑焦点与键盘焦点,避免焦点丢失。
  • 使用Dispatcher.BeginInvoke()延迟聚焦,防止触发LostFocus事件循环。
  • 附加属性和行为方案更符合MVVM原则,避免视图与ViewModel的耦合。

内容的提问来源于stack exchange,提问作者Girish Mulye

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 06:55:27