WPF版的HideCaret()

简介: 原文:WPF版的HideCaret()                                                   WPF版的HideCaret()                                                              ...
原文: 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(
100 );
            }
//  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-ComplexLayout
WPF-ComplexLayout-DockPanel
241 0
WPF-ComplexLayout
|
数据建模 C#
WPF InkCanvas 毛笔效果
原文:WPF InkCanvas 毛笔效果 1、先来看看InkCanvas的一般用法:                                                                                 2、自定义InkCanvas,实现毛笔效果...
1212 0
|
C#
WPF“天狗食月”效果
原文:WPF“天狗食月”效果 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yangyisen0713/article/details/18596419 ...
725 0
|
C#
浅谈WPF中的PreviewTextInput
原文:浅谈WPF中的PreviewTextInput     今天在使用TextBox的TextInput事件的时候,发现无论如何都不能触发该事件,然后百思不得其解,最后在MSDN上找到了答案:TextInput 事件可能已被标记为由复合控件的内部实现进行处理。
1147 0
|
C#
WPF党旗和国徽!
原文:WPF党旗和国徽! 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yangyisen0713/article/details/18087007 ...
620 0
|
C#
浅谈WPF的VisualBrush
原文:浅谈WPF的VisualBrush     首先看看VisualBrush的解释,msdn上面的解释是使用 Visual 绘制区域,那么我们再来看看什么是Visual呢?官方的解释是:获取或设置画笔的内容,Visual 是直接继承自DependencyObject,UIElement也是直接继...
1993 0
|
C# 测试技术
[WPF] PerformClick ?
原文:[WPF] PerformClick ?                                       [WPF] PerformClick ?                                                   周银辉   WPF没有提供这个方法,还真是让人觉得有些讨厌啊。
1046 0
|
C# Windows
WPF Adorner
原文:WPF Adorner 之前做项目时,为了实现类似微信消息数目的效果   image.png   ,我之前是修改的ControlTemplate。类似于将一个带数字的控件,放在另一个控件的右上角,来实现的这个效果。
1185 0
|
C# 索引
WPF MeshGeometry3D
原文:WPF MeshGeometry3D 说说 MeshGeometry3D 里 常用的 四个属性。 先看看 MSDN 的 简介 先说说 Positions,介绍说 是顶点位置的集合,什么意思,看张图片。
884 0