当前位置: 首页 > news >正文

WPF双击修改文本2

1. 与 IsEnabled 的核心区别

属性 作用 视觉效果 事件传递
IsEnabled 控件是否启用 通常变灰(取决于控件模板) 禁用后控件仍可见,但无法交互
IsHitTestVisible 元素是否参与命中测试 无默认视觉效果 禁用后元素对鼠标“透明”,事件穿透到下层

2. 命中测试机制

WPF 的鼠标事件从最顶层元素开始向下传递,直到找到第一个 IsHitTestVisible = true 且能处理该事件的元素。

示例

xml

<StackPanel Background="LightGray" MouseDown="StackPanel_MouseDown"><Button Content="普通按钮"/><Rectangle Width="100" Height="50" Fill="Red" IsHitTestVisible="False"/><Button Content="被遮挡的按钮"/>
</StackPanel>
  • 点击 Rectangle 区域时,因为 IsHitTestVisible=False,事件穿透到下层
  • 如果下层是 StackPanel(背景可命中),它会接收鼠标事件

3. 实战

xml

 <Button Grid.Row="2" Width="200" Height="30" attached:NewEditBehavior.EnableDoubleClick="True"><Button.Content><TextBox Width="195" Height="26" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" IsHitTestVisible="False" Text="{Binding NewContent}"/></Button.Content></Button>

未编辑时

  • IsHitTestVisible = False
  • TextBox 对鼠标“透明”
  • 点击看起来像在点 Button(Button 接收到双击事件)

编辑时

  • IsHitTestVisible = True
  • TextBox 开始接收鼠标事件
  • 用户可以输入、移动光标、选择文本

关键优势

  • 不破坏 Button 的原有交互:Button 仍然存在,只是它的 Content 区域对鼠标透明
  • 不需要替换 Content:TextBox 一直在那里,只是切换状态
    cs代码:
 public static class NewEditBehavior{public static readonly DependencyProperty EnableDoubleClickProperty =DependencyProperty.RegisterAttached("EnableDoubleClick", typeof(bool), typeof(NewEditBehavior),new PropertyMetadata(false, OnEnableDoubleClickChanged));public static void SetEnableDoubleClick(DependencyObject obj, bool value) => obj.SetValue(EnableDoubleClickProperty, value);public static bool GetEnableDoubleClick(DependencyObject obj) => (bool)obj.GetValue(EnableDoubleClickProperty);private static void OnEnableDoubleClickChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){if (d is Button button && (bool)e.NewValue){button.MouseDoubleClick += OnButtonDoubleClick;}}private static void OnButtonDoubleClick(object sender, MouseButtonEventArgs e){var button = sender as Button;if (button?.Content is TextBox textBox){// 保存原始值string originalText = textBox.Text;// 进入编辑模式textBox.IsHitTestVisible = true;// 延迟设置焦点,确保光标显示button.Dispatcher.BeginInvoke(new Action(() =>{textBox.Focus();textBox.SelectAll();}), System.Windows.Threading.DispatcherPriority.Input);// 临时事件处理RoutedEventHandler lostFocusHandler = null;KeyEventHandler keyDownHandler = null;MouseButtonEventHandler previewMouseDownHandler = null;lostFocusHandler = (s, args) =>{ExitEditMode(textBox, button, originalText, false);CleanupEvents(textBox, lostFocusHandler, keyDownHandler, previewMouseDownHandler, button);};keyDownHandler = (s, args) =>{if (args.Key == Key.Enter){// 保存修改ExitEditMode(textBox, button, originalText, true);CleanupEvents(textBox, lostFocusHandler, keyDownHandler, previewMouseDownHandler, button);args.Handled = true;}else if (args.Key == Key.Escape){// 取消修改,恢复原值textBox.Text = originalText;ExitEditMode(textBox, button, originalText, false);CleanupEvents(textBox, lostFocusHandler, keyDownHandler, previewMouseDownHandler, button);args.Handled = true;}};// 处理点击空白区域失去焦点previewMouseDownHandler = (s, args) =>{var clickedElement = args.OriginalSource as DependencyObject;// 检查点击的元素是否在 TextBox 内部if (!IsChildOfTextBox(clickedElement, textBox)){// 点击了外部区域,退出编辑模式(不保存)ExitEditMode(textBox, button, originalText, false);CleanupEvents(textBox, lostFocusHandler, keyDownHandler, previewMouseDownHandler, button);}};textBox.LostFocus += lostFocusHandler;textBox.KeyDown += keyDownHandler;// 在窗口级别监听鼠标点击var window = Window.GetWindow(button);if (window != null){window.PreviewMouseDown += previewMouseDownHandler;}e.Handled = true;}}private static void ExitEditMode(TextBox textBox, Button button, string originalText, bool saveChanges){// 如果不保存,恢复原值if (!saveChanges && textBox.Text != originalText){textBox.Text = originalText;}textBox.IsHitTestVisible = false;// 将焦点移到 Button 上button.Focus();}private static void CleanupEvents(TextBox textBox, RoutedEventHandler lostFocusHandler,KeyEventHandler keyDownHandler, MouseButtonEventHandler previewMouseDownHandler, Button button){textBox.LostFocus -= lostFocusHandler;textBox.KeyDown -= keyDownHandler;var window = Window.GetWindow(button);if (window != null && previewMouseDownHandler != null){window.PreviewMouseDown -= previewMouseDownHandler;}}private static bool IsChildOfTextBox(DependencyObject element, TextBox textBox){while (element != null){if (element == textBox)return true;element = VisualTreeHelper.GetParent(element);}return false;}}
http://www.jsqmd.com/news/693230/

相关文章:

  • 2026年二甲基硅油与有机化工溶剂供应商深度横评:扬州天达如何成为制造企业的首选 - 年度推荐企业名录
  • 2026年新疆装修公司怎么选?乌鲁木齐工装家装一站式服务深度横评 - 优质企业观察收录
  • 碧蓝航线全自动助手Alas:解放双手的终极挂机解决方案
  • 从游戏角色碰撞到无人机航测:不规则多边形‘质心’计算的3个硬核实战场景
  • 2026年二甲基硅油深度横评:扬州天达与行业头部品牌全面对标 - 年度推荐企业名录
  • 机器学习进阶必读:10本被低估的经典教材
  • 互联网大厂 Java 求职面试:燕双非的搞笑挑战与技术探讨
  • 别再手动排座位了!用Vue3写个智能座位编辑器,支持拖拽换号与横竖切换
  • Python Playwright项目打包避坑指南:解决‘Please run the following command’错误
  • 营口同润网络科技客服咨询AI流量赋能,重塑智能体验新标杆高报行业圆满落幕 - 速递信息
  • CSS:导航栏三角箭头
  • 2026陕西系统门窗十大品牌权威榜单 - 深度智识库
  • 告别炼丹:用MoCo v3的‘冻结Patch层’技巧,让你的ViT自监督训练稳如老狗
  • 告别复制粘贴:用CANdelaStudio 17从CDDT模板到定制CDD的完整避坑指南
  • 2026年二甲基硅油与有机化工溶剂深度横评:工业原料采购完全指南 - 年度推荐企业名录
  • Houdini POP学习02
  • HC32L130开发避坑实录:从官方Demo到稳定工程,我踩过的那些编译器与库的‘坑’
  • HackGen编程字体完全指南:为什么它是开发者的终极选择
  • 从零构建JavaEE网上书城:MVC架构与购物车系统实战指南
  • 从CUDA到CANN:给NVIDIA开发者的昇腾AscendCL迁移避坑指南
  • Happy Island Designer:终极岛屿规划工具完全指南 [特殊字符]️
  • React Native Modals完整教程:打造滑动关闭和自定义动画的完美弹窗
  • 百万词元的智慧觉醒:DeepSeek-V4如何点亮超长上下文的星辰大海
  • 告别点灯实验:用STM32CubeMX+HAL库5分钟搞定按键控制LED,效率翻倍
  • 英雄联盟皮肤自由切换:R3nzSkin内存换肤技术实战指南
  • 盘点2026年天津宝奥之星奔驰汽车维修,场地大且服务质量好值得选择 - 工业品牌热点
  • Rust的#[derive(Hash)]一致性
  • 游戏性能优化新选择:sguard_limit 如何解决腾讯游戏卡顿问题
  • 别再对着Segmentation fault干瞪眼了!手把手教你用ulimit和kernel.core_pattern捕获Linux核心转储
  • HiveWE:魔兽争霸III终极地图编辑器完整指南