zoukankan      html  css  js  c++  java
  • 小议Focus()方法

          这里说的是System.Windows.Forms.Control.Focus()方法,就是给桌面控件设定焦点的方法。以前也曾无数次使用过该方法,没有怎么注意。偶然间我在MSDN上发现Focus方法的声明是: public bool Focus() 。也就是说Focus()方法设置焦点有可能是会失败的。

          怎么会呢? 我试了一下,在一个Form上放了一个Button和一个TextBox,Tab顺序是Button:0 TextBox:1如图:


        在Form_Load函数里面写下了下面的代码:
            private void Form1_Load(object sender, EventArgs e)
            {
                this.txtTestTextBox.Text = "测试一下";
                this.txtTestTextBox.Focus();
            }

         并给Button添加了一个按钮事件响应函数:
            private void btnTestButton_Click(object sender, EventArgs e)
            {
                System.Windows.Forms.MessageBox.Show("测试按钮被按下");           
            }

          运行程序后,拍空格键。居然发现是弹出的"测试一下"提示框:
           


          显然,这是因为在Form_Load函数中的this.txtTestTextBox.Focus() 返回了false。那么,为什么会返回false呢?这就是我要研究的问题。追赶一下流行,使用了.NET Framework 3.5 源代码调试。
          我先是将断点停在了Form_Load函数的this.txtTestTextBox.Focus();行上。然后再Call Stack窗口上Load System.Windows.Forms.dll的Symbols(就是那个10M大的PDB文件)。
          然后按F11 Step in……
         

            [EditorBrowsable(EditorBrowsableState.Advanced)]
            public bool Focus() {
                Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "Control::Focus - " + this.Name);
                Debug.WriteLineIf(IntSecurity.SecurityDemand.TraceVerbose, "ModifyFocus Demanded");
                IntSecurity.ModifyFocus.Demand();

                //here, we call our internal method (which form overrides)
                //see comments in FocusInternal
                //
                return FocusInternal();
             }
             代码中执行了两个方法一个是IntSecurity.ModifyFocus.Demand(),另一个是FocusInternal()。IntSecurity.ModifyFocus.Demand()方法是判断是否有相应的权限的,属于安全检查,不去管他。FocusInternal()方法是研究的重点,接着Step in……
           

            [ResourceExposure(ResourceScope.None)]
            internal virtual bool FocusInternal() {
                Debug.WriteLineIf(Control.FocusTracing.TraceVerbose, "Control::FocusInternal - " + this.Name);
                if (CanFocus){
                    UnsafeNativeMethods.SetFocus(new HandleRef(this, Handle));
                }
                if (Focused && this.ParentInternal != null) {
                    IContainerControl c = this.ParentInternal.GetContainerControlInternal();
     
                    if (c != null) {
                        if (c is ContainerControl) {
                            ((ContainerControl)c).SetActiveControlInternal(this);
                        }
                        else {
                            c.ActiveControl = this;
                        }
                    }
                }

     
                return Focused;
            }
          从上面的代码看来,Focus()方法返回的false,就是这个FocusInternal方法给出的。这个方法的关键代码是:
    UnsafeNativeMethods.SetFocus(new HandleRef(this, Handle));简短截说,UnsafeNativeMethods.SetFocus()方法就是通过Send一个Windows 的Message让控件自己具有焦点的。F10单步执行,结果发现代码执行路径居然跳过了这行代码,也就是说,这个消息根本就没有发出去!为啥呢?从代码上看只有一种可能就是CanFocus的值是false。
          那CanFocus是又是什么呢?重新来过,执行到if(CanFocus)一行时 F11 Step in……
                  public bool CanFocus {
                [ResourceExposure(ResourceScope.None)]
                get {
                    if (!IsHandleCreated) {
                        return false;
                    }
                    bool visible = SafeNativeMethods.IsWindowVisible(new HandleRef(window, Handle));
                    bool enabled = SafeNativeMethods.IsWindowEnabled(new HandleRef(window, Handle));
                    return (visible && enabled);
                }
            } 

            哈哈,原来是一个可访问的公共属性,从代码中我们可以看到决定一个控件是否可以被Focus的条件有两个:一个是控件可见(bool visible = SafeNativeMethods.IsWindowVisible(new HandleRef(window, Handle));),另一个是控件是可用的(bool enabled = SafeNativeMethods.IsWindowEnabled(new HandleRef(window, Handle)); )。只有这两个条件同时具备时,才能支持控件设置焦点。
           那么为什么Form_Load函数里面执行Focus()方法会失败呢?控件的Enable属性为true是肯定的,因为我从没有修改过控件的Enable属性,只有Visible属性有可能是false。也就是说在Form_Load方法在返回之前,程序的界面还没有显示呢。我们再做一个实验:
           在按钮的事件响应函数里面填上this.txtTestTextBox.Focus();代码,测试一下。
           编译,运行,此时焦点在Button上,拍下空格键,弹出MessageBox,确定之后,发现焦点已经转移到TextBox上了,如下图:
          
           这次是设置成功了。那么我们能否在Form_Load中也设置成功呢?肯定能啊,只要在调用Focus方法前让控件编程Visible就可以了。我们可以在Form_Load方法中加入一行this.Show();代码,如下:
            private void Form1_Load(object sender, EventArgs e)
            {
                this.txtTestTextBox.Text = "测试一下";
                this.Show();
                this.txtTestTextBox.Focus();
            }

            这下界面显示出来时,焦点就在TextBox上了。说了这么多,其实就明白了一件事儿,一个控件要具有焦点,需要两个条件:一个是控件是可见的,另一个是控件是Enable状态的。我用Reflector看了一下,.NET 1.1、2.0、3.0、3.5的相关代码都是一样的。

  • 相关阅读:
    虚拟机的类加载机制
    数组
    Intellij快捷键
    Wireshark过滤器语法设置
    Git命令(转)
    Git命令
    字节码指令简介(转)
    Java异常了解
    Class类文件的结构
    垃圾收集器与内存分配策略(六)之内存分配与回收策略
  • 原文地址:https://www.cnblogs.com/michaellee/p/1053366.html
Copyright © 2011-2022 走看看