【C#】让工具栏ToolStrip能触发焦点控件的Leave、Validating、DataError等事件以验证数据

----------------更新:2014-04-21---------------

蒙doggo兄指教,得知有更好的方法可以代替蹩脚的0尺寸Button法,即调用窗体的验证方法Form.Validate(),该方会触发窗体中焦点控件的Validating事件以验证数据,达到与0尺寸Button法几乎相同的效果。先看采用新方法的代码:

public class ToolStripEx : ToolStrip
{
    protected override void OnClick(EventArgs e)
    {
        base.OnClick(e);
        Form fm = FindForm();
        if (fm != null) { fm.Validate(); }
    }
}

之所以说几乎,是因为还是有一点不同,就是Form.Validate()并不会触发焦点控件的Leave事件,所以需要该事件的猿友恐怕还得继续沿用0尺寸Button法或另想他法。

另外发现ToolStrip还有个操蛋的问题,就是上述方法都只对ToolStripButton的Click事件有效,但如果按钮是分离按钮ToolStripSplitButton,大家知道,按钮部分的单击事件就该用ButtonClick而不是Click,单击按钮部分虽然也会先触发ToolStrip.Click事件进行验证,但不管验证结果如何,ButtonClick都会被执行,不像ToolStripButton.Click那样,验证不过就不会执行。所以对付ButtonClick,在找到更好的办法前,我还得在事件处理方法中加判断才行。真他娘的让人不省心。

----------------原文:2014-03-24---------------

如题,Winform码农大概都知道这样一个问题,就是当输入焦点仍处在TextBox、DataGridViewCell等控件中时,如果单击普通Button、CheckBox等控件,那么该验证的会得到验证,该提交的会提交,该报错的会报错,该被阻止的操作会被阻止。但如果单击的是工具栏上的项目(如ToolStripButton,之所以说项目而不是控件,你懂的),是不会触发焦点控件的验证事件的,而是会直接执行按钮事件,这样带来的影响相信大家深有体会。总之不解决ToolStrip的这个问题我不会幸福。先看办法:

/// <summary>
/// 工具栏(无右侧竖线、无手柄、可触发其它控件验证)
/// </summary>
public class ToolStripEx : ToolStrip
{
    readonly Button btn;//定义一个用来转移焦点的控件,如Button

    public ToolStripEx()
    {
        //初始化并指定控件尺寸为0,0。因为你不会希望这个按钮被看到
        btn = new Button { Width = 0, Height = 0 };
        
        //下面为可选项
        //让工具栏在视觉上更地道。如被按下的ToolStripButton更明显,否则只有一个惨淡的线框
        ToolStripManager.VisualStylesEnabled = false;

        //不显示拖曳抓柄
        GripStyle = ToolStripGripStyle.Hidden;
    }

    //在工具栏获得句柄后将控件添加进窗体,之所以不在构造函数中做这事是因为那个时候窗体也许还是null
    protected override void OnHandleCreated(EventArgs e)
    {
        base.OnHandleCreated(e);
        Form fm = this.FindForm();
        if (fm != null) { fm.Controls.Add(btn); }//这样添加后,btn.Location会是0,0
    }

    //在工具栏被碰到时(其实选用其它类似事件也行)将焦点转移到btn上,以此触发焦点控件的验证
    //注意虽然是工具栏的Click,但经过实践点击其中的子项都会优先触发该事件
    //所以当焦点控件验证通不过时,不会再执行子项的Click事件,这一点我想是由win32消息机制实现的
    protected override void OnClick(EventArgs e)
    {
        base.OnClick(e);
        btn.Focus();
    }

    //可选。把工具栏最右边的1px竖线K掉,这种瑕疵对于我来说简直不能忍受,草泥马微软,有病
    protected override void OnPaint(PaintEventArgs e)
    {
        e.Graphics.SetClip(new Rectangle(0, 0, Width - 1, Height));
        base.OnPaint(e);
    }
}

办法很简单,就是在点击工具栏时先把焦点移到其它能正常获得焦点的控件上,以此来触发先前控件的Leave/Validating/DataError等事件。

其实为了解决这个问题我颇费了一番周折,最开始想到的其实就是这招,但觉得猥琐了点,作为一个有追求的码农,我认为应该从消息层面去解决,所以一开始就把这个阴招放在一边,专心捣鼓消息。开始我认为这个问题的本质是因为,工具栏就像Panel之类的控件,是得不到焦点的控件,不像Button之流,能够让其他控件的焦点转移过来,所以才有这个问题。那么我就想通过调用win32 API,让工具栏能发出与Button一样的消息,让焦点控件受骗,以为点到的是Button,从而验证自己的数据,移交自己的焦点。经过多番实践,确实让工具栏获得了焦点,让焦点控件失去焦点,用Spy++看焦点控件接收到的消息也与点击Button接收到的消息看起来一样了,但仍然不会触发验证,这就扯蛋了~我那个沮丧啊。BTW~其实给工具栏设置SetStyle(ControlStyles.Selectable, true)也可以达到同样目的,但一样解决不了问题。

也许是还没摸透问题的本质,也许是win32消息还是玩不转~总之是经历过若干次失败的尝试,我不得不放弃高大上的解决办法,这才回头来重新拾起猥琐方案,所以文中办法其实是妥协的结果,难免心有不甘,等他日机缘到了,我定再次尝试“正统”的解决办法。如有路过高人点拨,感激不尽!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏埋名

React UI 组件库【uiw】发布

高品质的UI工具包,基于React 16+的组件库。 ? 为了表示支持,点击 阅读原文 搞点Star,多多益善。

1572
来自专栏小狼的世界

IE6下实现Width:auto

看了这个题目,很多人肯定觉得有点太老土了,IE6都快到末路了,不过这个方法确实非常经典,我觉得很有必要记下一笔。

762
来自专栏Material Design组件

Human Interface Guidelines —— 导航栏(Navigation Bars)

36611
来自专栏企鹅号快讯

前端小知识:为什么你写的 height:100% 不起作用?

作者:JiaXinYi https://segmentfault.com/a/1190000012707337 这个知识不算冷门的,但是用的时候可能还是会有些懵...

1985
来自专栏青玉伏案

iOS开发之虾米音乐频道选择切换效果分析与实现

今天博客的内容比较简单,就是看一下虾米音乐首页中频道选择的一个动画效果的实现。之前用mask写过另外一种Tab切换的一种效果,网易云音乐里边的一种Tab切换效果...

2483
来自专栏吴老师移动开发

【iOS开发】iOS 动画详解

在移动开发中,为了提高用户体验,会用到一些动画来提高应用的视觉效果。让人有眼前一亮的感觉。同时有动画的过渡过程,会让应用看起来不是那么的生硬,更吸引用户。

2586
来自专栏葡萄城控件技术团队

Web页面中5种超酷的Hover效果

想在自己的网站中应用超酷的hover效果吗?也许你可以从如下的这些实例中获得一些灵感,如果你喜欢这些效果,也可以直接拷贝代码并应用到你的站点。 给平淡的站点带来...

2599
来自专栏吾爱乐享

php学习之html的标签属性(三)

1272
来自专栏hbbliyong

基于Three.js的360度全景--photo-sphere-viewer--简介

这个是基于three.js的全景插件  photo-sphere-viewer.js  ————————————————————————————————————...

6159
来自专栏空木白博客

Photoshop常用快捷组合技巧工具箱快捷键

M 矩形、椭圆选框工具 C 裁剪工具 V 移动工具 L 套索、多边形套索、磁性套索 W 魔棒工具 J 喷枪工具 B 画笔工具 S 像皮图章、图案图...

942

扫码关注云+社区

领取腾讯云代金券