8.派生一个控件

为了方便讲解之后的内容 这里还是按照[GDI+程序设计]一书上面的内容派生一个控件吧 因为后面的内容会用到这个控件来举例子

还是和[GDI+程序设计]一样派生一个TextBox不过功能就不做书上那么全了 简单演示一下效果就行了 因为主要目的是为了方便后面的内容来举例子 所以就做个简单的 做这样一个效果:

给控件一个[NumberRange]属性 这个属性用来验证用户的输入 如果用户在控件里面输入的是一个在范围内的数字 那么控件不做任何变化 否则背景色变成黄色

首先创建一个控件继承TextBox然后给他一个NumberRange属性 不过注意了 这个属性是字符串类型:

private int m_nHigh = 100;
private int m_nLow = 0;

private string _NumberRange = "0,100";
[DefaultValue("0,100")]
public string NumberRange {
    get { return _NumberRange; }
    set { _NumberRange = value; }
}
就像这样 明显你能感觉到真正要用的是上面的两个数据 或许这里你有些郁闷 直接把上面两个做成属性不就完了 的确可以这样干 但是我这里只想提供一个属性让用户去设置 而不是两个 想一下这种情况又不是没有 比如窗体的Size属性Location属性 是不是也都和我上面的截图那个样子用【数字1,数字2】的形式来赋值 只是Size和Location可以把属性展开就像这样:


至于这个问题 后面的内容再说先一步一步的来 然后后面在做成像上面截图的样子

继续回到代码的问题 就想上面那些写代码还没完 还得把NumberRange和m_nLow和m_nHigh两个数关联起来 要关联其实也是在set里面 当用户给NumberRange设置值的时候实质上是在给m_nLow和m_nHigh赋值 而我们需要在set赋值的时候去完成这个转换:

public string NumberRange {
    get { return _NumberRange; }
    set {
        if (string.IsNullOrEmpty(value))
            throw new ArgumentException("[NumberRange] can not be empty or null!");
        string[] v = ((string)value).Split(',');
        if (v.Length != 2)
            throw new ArgumentException("Invalid input!");
        try {
            m_nLow = int.Parse(v[0]);
            m_nHigh = int.Parse(v[1]);
        } catch (FormatException ex) {
            throw ex;
        }
        _NumberRange = value;
    }
}
代码很简单 估计也不用解释了 接下来写个方法来验证用户输入的值是否合法:
private bool CheckRange(string strNum) {
    if (string.IsNullOrEmpty(strNum)) return false;
    try {
        int v = int.Parse(strNum);
        return v >= m_nLow && v <= m_nHigh;
    } catch (FormatException) {
        return false;
    }
}
方法接受一个字符串 判断这个字符串是否是合法的 最后剩下的就是去验证用户输入的数据是否合法了

在TextBox控件中提供了两个事件用来验证:

上面一个发生在验证之后 下面一个是验证之时 这个验证什么意思 简单点说 也就是当文本框将要失去焦点的时候 就会触发这个事件 可以在里面写代码验证用户输入的数据是否合法:

protected override void OnValidating(CancelEventArgs e) {
    this.BackColor = this.CheckRange(this.Text) ? Color.White : Color.Yellow;
    base.OnValidating(e);
}
如果合法背景色白色否则黄色 注意这里的事件参数e有一个Cancel属性默认值是false这个属性有啥用?也就是是否取消事件的发生 如果你将这个值设置成true那么文本框将继续持有焦点 如果你把代码改成这样:
protected override void OnValidating(CancelEventArgs e) {
    this.BackColor = this.CheckRange(this.Text) ? Color.White : Color.Yellow;
    if (this.BackColor == Color.Yellow) e.Cancel = true;
    base.OnValidating(e);
}

加了一个判断 如果是验证失败的情况下那么Cantel就为true 那么会有啥效果?

我会告诉你我鼠标一直在点窗体的XX么 压根就关不到 为啥?因为Validating之后的所有事件都被取消了 事件顺序是这样的:

Enter - GotFocus - Leave - Validating - Validated - LostFocus
也就是说到Validating那里就中断了事件的继续 那么控件也不会失去焦点 所以你其他事情你也干不了 除非通过验证( - -!、、不过对于这种流氓的行为还是不要干的好)

下面是全部代码:

public class NumberTextBox : TextBox
{
    private int m_nHigh = 100;
    private int m_nLow = 0;

    private string _NumberRange = "0,100";
    [DefaultValue("0,100")]
    public string NumberRange {
        get { return _NumberRange; }
        set {
            if (string.IsNullOrEmpty(value))
                throw new ArgumentException("[NumberRange] can not be empty or null!");
            string[] v = ((string)value).Split(',');
            if (v.Length != 2)
                throw new ArgumentException("Invalid input!");
            try {
                m_nLow = int.Parse(v[0]);
                m_nHigh = int.Parse(v[1]);
            } catch (FormatException ex) {
                throw ex;
            }
            _NumberRange = value;
        }
    }

    private bool CheckRange(string strNum) {
        if (string.IsNullOrEmpty(strNum)) return false;
        try {
            int v = int.Parse(strNum);
            return v >= m_nLow && v <= m_nHigh;
        } catch (FormatException) {
            return false;
        }
    }

    protected override void OnValidating(CancelEventArgs e) {
        this.BackColor = this.CheckRange(this.Text) ? Color.White : Color.Yellow;
        base.OnValidating(e);
    }
}
这个代码没什么难度 不过多做解释 派生这个控件主要是为了后面的内容做铺垫 在后面会讲如何想把NumberRange做成像Size那样的 在属性窗口能够展开的 这里只是先做一个初步 后面的话上面的代码还得改 应为要做成像Size那样的 像上面这样是肯定没有办法的、、、


添加时间:2014-04-09 23:42:24 编辑时间:2014-04-10 00:00:21 阅读:1439 
C#自定义控件开发 C#控件开发
xinirvana - 2014-05-04 10:25:12
这个系列的文章还会更新吗?坐等。。。
隔壁王大叔 - 2014-06-27 13:55:09
这个博客很漂亮,赞一个!
  • 编写评论

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