5.处理自定义控件焦点

其实要说啥是焦点 从口头上来说 我也不知道应该怎么描述 如果非要我描述一下的话 我也只能说:获得焦点的控件会接受用户的键盘事件

比如说 窗体上有两个文本框 我在上面一个文本框打字的时候 不会打到下面的一个文本框里面去 因为在那个时候 具有焦点的是 上面的一个文本框 相信平时 有看到这样的效果像这样的虚线框 表示此刻button2具有焦点 如果这个时候按下回车或者空格的话 就等同于点击了button2  而按下键盘上的Tab键会在窗体上的控件之间切换焦点、、而获得焦点的控件会变成什么样子的长相 这个得由控件自己去处理

假设就这样创建一个自定义控件放到窗体上 然后按下Tab把焦点切换到控件上去 是看不到任何效果的:


此时此刻 焦点在我的自定义控件上(为了方便显示 我设置成了红色) 还有 如果你在控件的单击事件里面写上一些代码 你按下 回车或者空格 你也是看不到啥效果的:

private void focusControl1_Click(object sender, EventArgs e) {
    MessageBox.Show("Focus");
}
虽然此刻 自定义控件获得了焦点 但不代表你此时此刻按下回车或者空格 就会自动的去触发单击事件 这个得自己写代码实现 因为谁会知道你的这个自定义控件是一个按钮控件呢 万一我是一个文本框控件呢

现在先来绘制控件 让他得到焦点的时候能有点不同:

protected override void OnPaint(PaintEventArgs e) {
    Graphics g = e.Graphics;
    if (this.Focused && this.ShowFocusCues/* this.ContainsFocus */) {
        using (Pen p = new Pen(Color.Gray)) {
            p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
            g.DrawRectangle(p, 2, 2, this.Width - 5, this.Height - 5);
        }
    }
    g.DrawRectangle(Pens.DarkCyan, 0, 0, this.Width - 1, this.Height - 1);
    base.OnPaint(e);
}
override OnPaint就不多说了其实也没有啥 看上去就是比起以前在里面加入了一个判断而已 判断当前是否得到焦点 注意 可以看到的是判断里面我注释了一个 一般情况下两者你都可以用 而他们的区别就是:
  • ContainsFocus:是否包涵有焦点 也就是如果说控件还有子控件 无论是自身得到焦点还是子控件得到焦点 都是true
  • Focused:不会去管子控件是否包涵有焦点
  • 注:其实这两者的区别我也是看资料上说的 并没有实际的实践过

ShowFocusCues 决定是否显示焦点框、、什么意思 意思就是默认情况下 窗体上是不会显示焦点框的 不信你试试新建一个窗体 上面搞几个button出来是时候 你去点击那些button看看有没有焦点框?或许被你点击过的按钮是高亮显示的 但是没有焦点框 不过如果这个时候 你点下过Tab来切换焦点 那么控件上面就会显示焦点框了 这个属性的值是只读的 由系统来觉得当前是否应该显示焦点框

但是这样写你运行的时候可能会发现 按下Tab把焦点切换到控件上面去的时候 没有出现什么反映 这里在前面的文章就已经说过了 Paint 只会在得到通知绘制的时候才会执行 而当控件得到或者失去焦点的时候没有去通知 Paint 事件绘制 所以得自己去处理

很简单有两个事件 GotFocus 和 LostFocus 在这两个事件里面通知重绘就可以了(注:这两个事件是在属性窗口找不到的 不只是自定义控件是这样的 其他控件也是 但是可以通过 点 出来)

public FocusControl() {
    this.GotFocus += (s, e) => this.Invalidate();
    this.LostFocus += (s, e) => this.Invalidate();
}

//protected override void OnGotFocus(EventArgs e) {
//    this.Invalidate();
//    base.OnGotFocus(e);
//}

//protected override void OnLostFocus(EventArgs e) {
//    this.Invalidate();
//    base.OnLostFocus(e);
//}

像上面一样 你可以选择override也可以选择像我上面那样 更具个人喜好 反正就是处理这两个事件为目的 处理的方式很多 选择自己喜欢的就可以了 通常情况下 要是代码不多 我就会选择像上面的一种写法 如果你不知道上面的那个写法是啥 请百度"匿名委托" 这样当焦点离开 或者得到焦点的时候 就能看到变化了:

(- -! 不要嫌弃好丑 这一篇只讲焦点 所以为了代码简洁 就暂时不添加其他代码)

注:在[GDI+程序设计]一书中 是用的ChangeUICues事件来搞的 但是当时我用了却没有效果 所以就用的Got/LostFocus事件

但是 就这样看上去是没有啥问题了 其实还有bug 比如 假设现在焦点在button1上 而此时我点击自定义控件 控件也执行了Click事件 但是焦点还是在button1上面   现在反过来看看 加入焦点现在在自定义控件上 如果此时我去点击button1可以发现button1得到了焦点 而自定义控件失去了焦点 所以还得处理自定义控件的点击 使他点击的时候得到焦点 可能这个时候你会想到处理控件的Click事件 在里面让他得到焦点 确实 不过还是建议处理MouseDown事件 仔细看看 当焦点在自定义控件的时候 点击button1 是不是点下去 鼠标还没有抬起来的时候 button1就已经得到了焦点了?(注:Click = MouseDown + MouseUp)

所以再加代码:

protected override void OnMouseDown(MouseEventArgs e) {
        this.Focus();
        base.OnMouseDown(e);
}
或者构造器中:
this.MouseDown += (s,e) => this.Focus();
到此 焦点切换基本就没啥问题了

现在来处理一下按键 让它能像button那样得到焦点时 按下回车或者空格 就会触发Click事件 那么处理一下KeyDown事件:

protected override void OnKeyDown(KeyEventArgs e) {
    if (e.Modifiers == Keys.None 
        && (e.KeyCode == Keys.Space || e.KeyCode == Keys.Enter)){
        this.OnClick(new EventArgs());
    }
    base.OnKeyDown(e);
}

那个Modifiers是指 有没有按下Ctrl或者Alt或者Shift 如果没有按下任意一个 并且按下了回车或者空格 那么调用一下控件的单击事件 这个时候 最上面的时候写在控件Click事件里面的提示框也会弹出来了

对于 焦点 这里就讲这么多 其实一个控件不只是有焦点和无焦点的状态 比如还有Enable Enable的状态就不打算单独讲了 因为和上面一样只是在Paint里面多一个判断而已 控件禁用时候张什么样子 绘制就是了 不过不同的是 不用像上面的Got/LostFocus那样去通知Paint 当Enable的值发生变化的时候 会自己执行Paint 所以可以直接在Paint里面判断就行了比如:

protected override void OnPaint(PaintEventArgs e) {
    Graphics g = e.Graphics;
    if (!this.Enabled) {
        g.FillRectangle(Brushes.Gray, this.ClientRectangle);
    } else {
        if (this.Focused && this.ShowFocusCues/* this.ContainsFocus */) {
            using (Pen p = new Pen(Color.Gray)) {
                p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
                g.DrawRectangle(p, 2, 2, this.Width - 5, this.Height - 5);
            }
        }
        g.DrawRectangle(Pens.DarkCyan, 0, 0, this.Width - 1, this.Height - 1);
    }
    base.OnPaint(e);
}
如果控件禁用的时候 我填充一整块灰色(只是演示而已 禁用的时候要张什么样子你想怎么画 怎么画) 否则正常绘制

至于 焦点 为什么要单独列出来说 是因为焦点扯到的东西比较多 如上面Enable的和焦点的比较 焦点不只是在Paint里面来个判断而已 还要扯到其他的 所以单独列了出来、、在下一篇中 再讲解一下热键就基本差不多了 然后综合前几篇的文章 来个小练习、、然后继续讲一些关于在窗体设计器时候的一些东西..


添加时间:2014-03-28 02:10:08 编辑时间:2016-11-07 13:34:50 阅读:1572 
C#自定义控件开发 C#控件开发
还没有人留言 要不你来抢一个沙发?
  • 编写评论

      我觉得区分大小写是一个码农的基本素质
[访问统计] 今天:59 总数:147832 提示:未成年人 请在大人陪同下浏览本站内容 还有:世界上最帅的码农 -> 石头 RSS:http://st233.com/rss Powered by -> Crystal_lz