UserControl中TextBox设置Focus失效的问题2

本文介绍了一种解决在Form中使用SplitContainer和TreeView时,点击TreeView节点后焦点未正确转移到自定义UserControl中的TextBox的方法。通过定义委托和在TreeView的AfterSelect事件中调用该委托,实现了在UserControl加载完成后自动获取焦点的效果。
还是获取焦点的问题,不过这次不同。
这次是Form中有SplitContainer,左边有TreeView,点击TreeView节点后,在SplitContainer右边加不同的UserControl。加载完UserControl之后,UserControl中的TextBox自动获取焦点。

按照上一篇的做法,运行看不到效果。分析了一下原因,应该是点击了TreeView的某个节点后,焦点还在TreeView内,虽然代码中控制了UserControl的TextBox获得焦点,但实际上UserControl的TextBox没获得焦点,依然无效。找了TreeView的API,也没发现有什么方法,可以让TreeView失去焦点。

于是,头脑风暴,换了种写法。
首先定义一个委托。
delegate void ShowChangeCouponFocus(ChangeCoupon ctl);
ShowChangeCouponFocus ShowChangeCouponFun;
private void FunShowChangeCouponFocus(ChangeCoupon ctl)
{
   ctl.Capture = true;
   ctl.SetBacodeFocus();
}

其中,ChangeCoupon为自定义的UserControl。ctl.SetBacodeFocus();为UserControl中的自定义方法,用来设置TextBox的焦点。
public void SetBacodeFocus()
{
    this.txtBarcode.Focus();
}

然后,在TreeView的AfterSelect做出如下修改
switch(e.Node.Name)
{
    case "NodeChangeCoupon":
         var ctlChangeCoupon = ChangeCoupon() { Dock = DockStyle.Fill };
         this.panelCategory.Controls.Clear();
         this.panelCategory.Controls.Add(ctlChangeCoupon);
         
         ShowChangeCouponFun = new ShowChangeCouponFocus(FunShowChangeCouponFocus);
         this.BeginInvoke(ShowChangeCouponFun, ctlChangeCoupon);
         break;    
    //.....其他内容
}

仅仅将设置焦点的代码放到了BeginInvoke中执行,在点击TreeView之后,自定义UserControl加载后,TextBox自动获得焦点。
/// <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
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值