zoukankan      html  css  js  c++  java
  • 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);
            }

        }

  • 相关阅读:
    利用Lambda获取属性名称
    Entity Framework 6.0 源码解读笔记(一)
    [转]Sql server2005中如何格式化时间日期
    python之路_RabbitMQ相关介绍
    python之路_redis相关介绍
    python之路_django之contenttype介绍
    python之路_最简单的Git介绍
    python之路_rest-framework之分页、路由、视图、渲染
    python之路_rest-framework之版本、解析器、序列化
    python之路_rest-framework之认证、权限、频率
  • 原文地址:https://www.cnblogs.com/zhouyinhui/p/1746031.html
Copyright © 2011-2022 走看看