原文:WPF版的HideCaret()

WPF版的HideCaret()

周银辉

事情是这样的

一般说来,对于那些拥有句柄的TextBox(RichTextBox同理)控件,比如win32的,WinForm,如果我们想手动隐藏或显示其插入符(Caret),可以调用HideCaret和ShowCaret这样的Windows API,比如WinForm而言,我们可以这样:

        [DllImport("user32.dll")]
        public static extern bool HideCaret(IntPtr hWnd);         [DllImport("user32.dll")]
        public static extern bool ShowCaret(IntPtr hWnd);

那个hWnd嘛,传入TextBox的句柄就可以了。

但到了WPF这里,恩,不好使了,因为在WPF中,窗口级别的东东有句柄,文本框之类的控件根本就没有。

另外,把WPF的TextBox 的 IsReadOnly属性设置为True,插入符自然没有了, 如果你的应用里面的确可以将其设置为只读的话,这是可行的,当然,我比较背,我发现将其设置成只读后在某种情况之下其光标还在那里闪啊闪,难道是WPF的BUG?反正这足够让我郁闷的了。

WPF TextBox的插入符是如何实现的:

据我的粗略”研究“表明,其根本就不是调用Win32 API来显示插入符的,其用的是一个Adorner,然后对这个Adorner做的一点动画效果。

解决方案:

那么找出这个显示的插入符的Adorner,那么隐藏起来不就OK了。但是,WPF TextBox自然不会暴露出这样的”内部组件“,所以不那么容易找啊。没关系,Reflector这样的工具能够反编译出.net api的一切东东,那么就说明要把那个Adorner找出来不是没有可能的。所以我折腾出了下面的代码:

        private static Adorner GetCaret(this TextBoxBase textBox)
        {
            var textContainer = textBox.GetPrivateProperty("TextContainer").GetValue(textBox, null);
            var textSelection = textContainer.GetPrivateProperty("TextSelection").GetValue(textContainer, null);
            var caretElement = textSelection.GetPrivateProperty("CaretElement").GetValue(textSelection, null);
            var caret = caretElement as Adorner;             return caret;
        }

然后 caret.Visibility = Visibility.Collapsed (或Visible)便可以控制插入符的隐藏或显示了

但,郁闷的事情接踵而至,我发现,当你隐藏掉你查找出了的Adorner后,TextBox会在某些情况之下,完全重新创建一个Adorner来显示,Oh,My lady GaGa,

既然你不停地创建,那么我就不停地扼杀吧,呵呵呵,完整的代码如下:

    internal static class CaretHelper    {        private static Thread GetBackgourndThread(DependencyObject obj)        {            return (Thread)obj.GetValue(BackgourndThreadProperty);        }        private static void SetBackgourndThread(DependencyObject obj, Thread value)        {            obj.SetValue(BackgourndThreadProperty, value);        }        private static readonly DependencyProperty BackgourndThreadProperty =            DependencyProperty.RegisterAttached("BackgourndThread", typeof(Thread), typeof(CaretHelper), new UIPropertyMetadata(null));        public static void HideCaret(this TextBoxBase textBox)        {            var pts = new ParameterizedThreadStart(HideCaretCore);            var thread = GetBackgourndThread(textBox);            if (thread == null)            {                thread = new Thread(pts) {IsBackground = true};                SetBackgourndThread(textBox, thread);                thread.Start(textBox);            }            else            {                try                {#pragma warning disable 618,612                    thread.Resume();#pragma warning restore 618,612                }// ReSharper disable EmptyGeneralCatchClause                catch// ReSharper restore EmptyGeneralCatchClause                {                }            }        }        private static void HideCaretCore(this object textBox)        {            while (true)            {                var caret = ((TextBoxBase)textBox).GetCaret();                if (caret != null)                {                    Action a = () => caret.Visibility = Visibility.Collapsed;                    caret.Dispatcher.Invoke(a, null);                }                Thread.Sleep();            }// ReSharper disable FunctionNeverReturns        }// ReSharper restore FunctionNeverReturns        public static void ShowCaret(this TextBoxBase textBox)        {            var thread = GetBackgourndThread(textBox);            if (thread != null)            {#pragma warning disable 618,612                thread.Suspend();#pragma warning restore 618,612            }            var caret = textBox.GetCaret();            if (caret != null)            {                caret.Visibility = Visibility.Visible;            }        }        private static Adorner GetCaret(this TextBoxBase textBox)        {            var textContainer = textBox.GetPrivateProperty("TextContainer").GetValue(textBox, null);            var textSelection = textContainer.GetPrivateProperty("TextSelection").GetValue(textContainer, null);            var caretElement = textSelection.GetPrivateProperty("CaretElement").GetValue(textSelection, null);            var caret = caretElement as Adorner;            return caret;        }        private static PropertyInfo GetPrivateProperty(this object obj, string name)        {            return obj.GetType().GetProperty(name, BindingFlags.GetProperty | BindingFlags.NonPublic | BindingFlags.Instance);        }    }

WPF版的HideCaret()的更多相关文章

  1. Visual Studio 版本转换工具WPF版开源了

    想法的由来 入职一家新公司,领导给了个任务,要编写一个视频监控软件,等我编写调试好,领导满意了以后,这个软件要加入到公司的一个软件系统中去(这个添加工作不用我来做,嘻嘻,看着自己的软件被别人使用,心情 ...

  2. CefSharp.v49.0.1浏览器控件完全WPF版,实现禁止弹出新窗口,在同一窗口打开链接,并且支持带type="POST" target="_blank"的链接

    需求场景:在查询页面,填写查询条件,查询条件包括上传的图片,根据图片的特征查询,这就需要在提交的时候,使用POST提交,因为GET提交无法提交图片数据,提交查询条件之后,在新的窗口展示查询结果.(当然 ...

  3. 使用 GMap.NET 实现添加标注、移动标注功能。(WPF版)

    前言 在WPF嵌入地图,有两种方式: 浏览器方式:控件方式. 1)浏览器方式就是使用浏览器控件WebBrowser,设置好网址就行了.这种方式与地图的交互不太直接,需要懂html.javascript ...

  4. 使用GMap.NET类库,实现地图轨迹回放。(WPF版)

    前言 实现轨迹回放,GMap.NET有对应的类GMapRoute.这个类函数很少,功能有限,只能实现简单的轨迹回放.要实现更复杂的轨迹回放,就需要自己动手了. 本文介绍一种方法,可以实现复杂的轨迹回放 ...

  5. 【MEF】构建一个WPF版的ERP系统

    原文:[MEF]构建一个WPF版的ERP系统 引言 MEF是微软的一个扩展性框架,遵循某种约定将各个部件组合起来.而ERP系统的一大特点是模块化,它们两者的相性很好,用MEF构建一个ERP系统是相当合 ...

  6. wpf版扫雷游戏

    近来觉得wpf做出来的界面很拉风,自己也很喜欢搞些小游戏,感觉这做出来的会很炫,很装逼,(满足自己的一点小小的虚荣心)于是就去自学,发现感觉很不错,可是属性N多,太多了,而且质料也少,很多不会用,只会 ...

  7. GMap.NET实现电子围栏功能(WPF版)

    前言 GMap.NET是一个强大.免费.跨平台.开源的.NET控件.分为WPF和winform版.GMap.NET的基本知识不做过多介绍,本文主要介绍如何使用该控件实现电子围栏功能. 电子围栏主要有两 ...

  8. WPF版的权限管理系统

    好多技术人员都有一个通病,不关注用户的需求,产品的可用性,只看使用的技术的新不新,潮不潮,这就是所谓的技术发烧友. 这段时间,断断续续的开发一个WPF的软件,也拿出来Show一下.要不放在硬盘里就发霉 ...

  9. 一款高效视频播放控件的设计思路(c# WPF版)

    因工作的需要,开发了一款视频播放程序.期间也经历许多曲折,查阅了大量资料,经过了反复测试,终于圆满完成了任务. 我把开发过程中的一些思路.想法写下来,以期对后来者有所帮助. 视频播放的本质 就是连续的 ...

随机推荐

  1. php实现数值的整数次方

    php实现数值的整数次方 一.总结 没有考虑到指数为负数的情况 二.php实现数值的整数次方 题目描述: 给定一个double类型的浮点数base和int类型的整数exponent.求base的exp ...

  2. BZOJ 1588 HNOI2002 营业额统计 裸Treap

    题目大意:...题目描写叙述不全看这里好了 给定一个序列 对于每一个元素我们定义该数的最小波动值为这个数与前面全部数的差中的最小值(第一个数的最小波动值为第一个数本身) 求最小波动值之和 找近期的数仅 ...

  3. Jupyter Notebook 常用快捷键

    Jupyter Notebook 提供了比 IPython 美观的多得多的可视化形式.(比如对于 pandas 下的 DataFrame 的展示,df.head(5)) Jupyter Noteboo ...

  4. tspitr(tablespace point in time recovery)实验

    ===========环境模拟================= -----------模拟数据---------------- SYS@ORCL>create tablespace test ...

  5. Yii学习笔记之四(表单验证 api 翻译)

    1.表单验证 对于用户输入的全部数据,你不能信任,必须加以验证. 全部框架如此.对于yii 能够使用函数  yii\base\Model::validate()  进行验证 他会返回boolean值的 ...

  6. PHP移动互联网开发笔记(3)——运算符与流程控制

    一.PHP的运算符 PHP中有丰富的运算符集,它们中大部分直接来自于C语言.按照不同功能区分,运算符可以分为:算术运算符.字符串运算符.赋值运算符.位运算符.条件运算符,以及逻辑运算符等.当各种运算符 ...

  7. OpenGLES 关于 数学 的分支 - 线性变化量、离散量、随机量

    关于 数学 的分支 - 线性变化量.离散量.随机量 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作 ...

  8. UITextView的一些事1

    TextView如果只是作为展示文本时,要设置如下属性: //不可编辑(是否弹出键盘) TextView_label.editable=NO; //不可选择(长按不会出现复制.粘贴) TextView ...

  9. vmware tools 的安装(Read-only file system 的解决)

    安装 vmware tools 之后,才能将 vmware 创建的虚拟机以全屏的形式显示. 下载:在 vmware 软件的菜单栏点击[虚拟机],在[虚拟机]的主菜单中选择[安装 VMware Tool ...

  10. 用Ruby实现的论坛灌水工具:CC98 Post Machine

    介绍 ZJU 的校网论坛 CC98 比较活跃.论坛只对校内网开放,而且账号跟学生绑定,每个学生注册的账号数量有限.『十大』是 CC98 的经典页面:基于关注的人数(回帖的用户数而不是回帖的数量)用算法 ...