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

    }

 

 

 

目录
相关文章
C#编程-126:WPF初步
C#编程-126:WPF初步
103 0
C#编程-126:WPF初步
WPF-ComplexLayout
WPF-ComplexLayout-DockPanel
253 0
WPF-ComplexLayout
|
C# Windows
WPF WriteableBitmap的使用
选中项目按右键添加-资源文件-WPF-窗口generate_bitmap.xml文件内容为
2145 0
|
数据可视化 C#
WPF 中的 NameScope
原文:WPF 中的 NameScope 版权声明:本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:http://blog.csdn.net/wpwalter/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。
1015 0
|
C# 图形学 数据格式
WPF中的3D Wireframe
原文:WPF中的3D Wireframe WPF不支持画三维线,但开发人员提供了ScreenSpaceLines3D 类用于实现这个功能。
1315 0
|
C# 前端开发
WPF 小技巧
原文:WPF 小技巧 在使用mvvm模式开发时,对于Command的绑定是一件很伤脑筋的事情,尽管有强大的Blend类库支持: xmlns:Custom="http://www.galasoft.ch/mvvmlight"xmlns:i="http://schemas.
771 0
|
C#
使用WPF实现3D场景[一]
原文:使用WPF实现3D场景[一] 在这篇文章里,将介绍如何实现一个简单的三维场景,一个三维的空间,包括空间内的三维物体的组合. 首先介绍一下一个三维场景里的基本元素: 先是定义一个简单的三维的场景环境 代码如下: 以上是定义了一个名称叫做 myViewport 的的三维场景,接下来可以在这个三位场景里添加一些元素: 元素一:照相机 照相机是三维场景内用户的视角,当然照相机也是唯一的。
1619 0
|
C#
浅谈WPF中的PreviewTextInput
原文:浅谈WPF中的PreviewTextInput     今天在使用TextBox的TextInput事件的时候,发现无论如何都不能触发该事件,然后百思不得其解,最后在MSDN上找到了答案:TextInput 事件可能已被标记为由复合控件的内部实现进行处理。
1188 0
|
算法 C#
WPF 实现水纹效果
原文:WPF 实现水纹效果 鼠标滑过产生水纹,效果图如下:     XMAL就放置了一个img标签   后台主要代码 窗体加载: private void Window_Loaded(object s...
1547 0