UserControl中TextBox设置Focus失效的问题

本文探讨了在C# WinForm应用中为加载到Panel上的UserControl内的TextBox设置焦点的方法。作者通过逐步调试发现了问题所在,并最终实现了焦点的正确设置。
最近改代码遇到这样一个问题。
C# WinForm中有Panel,根据业务逻辑需要在Panel中加载不同的UserControl。其中一个UserControl中有一个TextBox,需要在UserControl Load的时候,为其设置焦点。暂定这个UserControl的名字为ControlA。

Form中加载UserControl的代码很简单。
public void AddControl(UserControl control)
        {
            this.pnlMian.Controls.Clear();
            this.pnlMian.Controls.Add(control);
        }
然后在一个公共类(AppControl.cs)中,添加了一些公共方法。用来加载新的UserControl。
 public static void MainFormRefreshControl(UserControl current, UserControl needLoad)
        {
             Form frm = current.FindForm();
             if (frm != null && frm is frmMain)
             {
                 frmMain fMain = (frmMain)frm;
                 fMain.AddControl(needLoad);
             }
        }

由于只需要设置Focus,所以认为代码很简单。
于是在ControlA的Load方法最下面加了这样一段代码。
this.txtBarcode.Focus();
用来设置TextBox获得焦点,但很遗憾,运行时没有预期的效果,TextBox没有获取焦点。

一开始以为代码写错,在代码中(用VS2008开发的项目)设置断点。断点设置后,正常运行,代码也顺利通过。运行有了预期效果,TextBox获得了焦点。取消断点,重新运行,问题又出来了。重新设置断点又OK了。反反复复,代码没有做改动,为什么设置断点就好了,不加断点就不行了?问题究竟出在哪里?

网上有人说,要设置TabIndex。参照过程设置了,没用。
网上也有人说,UserControl需要有焦点,才有用。于是在ControlA的Load方法中加入代码 this.Focus();试了,但还是没用。难道更换UserControl后,整个Form都失去焦点?带着这个疑问,在Load方法中替换为如下代码
var findForm = this.FindForm();
if (findForm != null && findForm is frmMain)
{
    var mainForm = (frmMain)findForm;
    this.Focus();
    this.txtBarcode.Focus();
}
运行还是没效果,但设置断点后,却发现问题了。问题在于findForm为NULL。

在所有更换UserControl的地方设置断点,查看系统的运行情况。更换UserControl的代码如下
AppControl.MainFormRefreshControl(this, new ucBuyYardageConfirm() { Dock = DockStyle.Fill });

通过断点运行发现,代码先走ControlA的Load方法,然后在走Form中的AddControl方法。但我印象中应该是this.pnlMian.Controls.Add(control);执行时,才走ControlA的Load方法。这里为什么反了过来?还是说,有什么我还不清楚的地方。这里的确挺奇怪的。

问题明了了,当ControlA完全被加载后(ControlA可找到所在的Form),在设置TextBox的Focus不就行了。将代码稍作修改。
首先在ControlA中增加一个Public方法
public void SetBarcodeFocus()
{
   this.txtBarcode.Focus();           
}
然后将代码
AppControl.MainFormRefreshControl(this, new ucBuyYardageConfirm() { Dock = DockStyle.Fill });稍作修改,如下.
var ctl = new ucBuyYardageSendCoupon() { Dock = DockStyle.Fill };
AppControl.MainFormRefreshControl(this, ctl);
ctl.SetBarcodeFocus();

测试,运行成功。代码按照预期效果运行。
/// <summary> /// 输入框自动失去焦点 /// </summary> public static class AutoLoseFocusBehavior { public static readonly DependencyProperty EnableAutoLoseFocusProperty = DependencyProperty.RegisterAttached( "EnableAutoLoseFocus", typeof(bool), typeof(AutoLoseFocusBehavior), new PropertyMetadata(false, OnEnableChanged)); public static bool GetEnableAutoLoseFocus(DependencyObject d) => (bool)d.GetValue(EnableAutoLoseFocusProperty); public static void SetEnableAutoLoseFocus(DependencyObject d, bool value) => d.SetValue(EnableAutoLoseFocusProperty, value); //private static void OnEnableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) //{ // if (d is UIElement element) // { // if ((bool)e.NewValue) // { // element.PreviewMouseDown += OnPreviewMouseDown; // } // else // { // element.PreviewMouseDown -= OnPreviewMouseDown; // } // } //} private static void OnEnableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if ((bool)e.NewValue) { // 添加事件 if (d is UIElement element) { element.PreviewMouseDown += OnPreviewMouseDown; } // 尝试为支持 Background 的元素设置 Transparent 背景 SetTransparentBackgroundIfApplicable(d); } else { // 移除事件 if (d is UIElement element) { element.PreviewMouseDown -= OnPreviewMouseDown; } } } private static void SetTransparentBackgroundIfApplicable(DependencyObject element) { switch (element) { case Panel p when p.Background == null: p.Background = Brushes.Transparent; break; case Border b when b.Background == null: b.Background = Brushes.Transparent; break; case Control c when c.Background == null: c.Background = Brushes.Transparent; break; case Canvas ca when ca.Background == null: ca.Background = Brushes.Transparent; break; // 可扩展其他需要的情况 } } private static void OnPreviewMouseDown(object sender, MouseButtonEventArgs e) { var element = sender as UIElement; if (element == null) return; // 查找所有 TextBox 项 var textBoxes = FindVisualChildren<TextBox>(element).ToList(); foreach (var tb in textBoxes) { if (tb.IsKeyboardFocusWithin) { // 强制更新绑定源 → 触发 ViewModel 更新 + 自定义校验 UpdateBindingSource(tb); } //SimulateLostFocus(tb); } // 将焦点转移到父容器 TransferFocusToParent(element); //// 最后再清除焦点,无法触发写入方法 // Keyboard.ClearFocus(); } private static void TransferFocusToParent(UIElement child) { var parent = VisualTreeHelper.GetParent(child); while (parent != null) { if (parent is UIElement uiElement && uiElement.Focusable) { uiElement.Focus(); // 触发完整的 LostFocus/GotFocus 流程 break; } parent = VisualTreeHelper.GetParent(parent); } // 如果找不到可聚焦父级,至少让窗口处理一次焦点更新 if (!Keyboard.IsKeyDown(Key.Tab) && !Keyboard.IsKeyDown(Key.LeftShift)) { // 可选:聚焦到自身(防丢失) child.Focus(); } } private static void UpdateBindingSource(DependencyObject target) { if (!(target is TextBox textBox)) return; var bindingExpr = textBox.GetBindingExpression(TextBox.TextProperty); if (bindingExpr == null) return; // 在 UpdateSource 前确保内容是合法状态(可选) if (textBox is FloatBox floatBox) { // 主动调用其校验逻辑 floatBox.EnforceMinMaxAndFormat(); // 这会触发 UpdateSource return; } // 普通 TextBox 正常更新 try { bindingExpr.UpdateSource(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"Binding 更新失败: {ex.Message}"); // 可视化错误提示? } } private static IEnumerable<T> FindVisualChildren<T>(DependencyObject parent) where T : DependencyObject { int childrenCount = VisualTreeHelper.GetChildrenCount(parent); for (int i = 0; i < childrenCount; i++) { var child = VisualTreeHelper.GetChild(parent, i); if (child is T t) yield return t; foreach (var descendant in FindVisualChildren<T>(child)) yield return descendant; } } } }它具体的方法,再重新分析为什么一个成功一个失败
最新发布
12-12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值