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);
        }

    }

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值