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

    }

 

 

 

posted @ 2010-05-28 10:27  周银辉  阅读(5024)  评论(3编辑  收藏  举报