4.为自定义控件添加事件

上一篇里面介绍了属性 这一篇来介绍一下事件

在这里强调一点 一提到事件 估计你脑子的反映就是控件的那些事件 比如鼠标点击事件 键盘事件什么的 确实 不过在C#中 事件绝对不是只有控件才有的东西 你普通的一个类也可以有事件 对于一些才开始接触C#的人可能有点犯晕了 对于C#中委托和事件 我这里找了一篇 张子阳的文章 说得很清晰了

http://www.cnblogs.com/JimmyZhang/archive/2007/09/23/903360.html(张子阳 C#中的委托和事件)

当你从Control继承的时候 已经有了一堆事件 比如Click 什么的 如果这些基本的事件能满足你的需求 你也不必多此一举的去写一个事件出来 假设现在我们要做这样一个控件 就像QQ截图的时候的那个颜色选择器一样的控件:

就是这个、、我说的是右下角的那块选择颜色的 这次我选用UserControl来做 因为方便 而且如果现在就直接绘制一个这样的控件出来 还不合适 所以还是用UserControl组合一个控件出来吧 只是为了演示 我就做了一个这样的:

- -!、、我是用的三个PictureBox修改背景色拼出来的 现在来为这个控件加上一点功能 我点击右边两个小的颜色的时候 左边的那个大的就变成我点击的那个颜色 那么我们需要为右边的两个控件添加一个点击事件 假设那个大的我取名pic_main右边的两个取名pic_red,pic_blue先加入如下代码:

private void pic_red_Click(object sender, EventArgs e) {
    pic_main.BackColor = pic_red.BackColor;
}

private void pic_blue_Click(object sender, EventArgs e) {
    pic_main.BackColor = pic_blue.BackColor;
}
这代码就不用解释了 既然是一个颜色选择控件 你觉得他应该有写啥属性和事件?、、我现在能想到的属性是当前选择的颜色是啥 和一个选择的颜色发生了变化的事件 假设分别为这个属性和事件命名 SelectedColor , ColorChange 然后在改一下代码把属性加上去 属性上一篇已经说过了怎么增加 所以直接上代码:
private Color _SelectedColor;

[Browsable(false)]//此属性不需要显示在属性窗口因为他是只读的
public Color SelectedColor {
    get { return this._SelectedColor; }
    //或者你也可以不要_SelectedColor 直接get{ return pic_main.BackColor; }
}
注意 这里只留了get 因为我们只提供了两个颜色可以给别人选择 别人是没有机会去设置当前选择的颜色的 既然没有必要去设置 所以也就没有必要在属性窗口显示了 所以上一篇讲到的Browsable就派上用场了

现在属性搞定了 可是事件了?ColorChange顾名思义 颜色改变了 所以颜色改变了的时候才发生 先暂时改一下刚才的代码:

private void pic_red_Click(object sender, EventArgs e) {
    if (pic_main.BackColor == pic_red.BackColor) return;
    this._SelectedColor = pic_main.BackColor = pic_red.BackColor;
    //怎么发生事件?
}

private void pic_blue_Click(object sender, EventArgs e) {
    if (pic_main.BackColor == pic_blue.BackColor) return;
    this._SelectedColor = pic_main.BackColor = pic_red.BackColor;
    //怎么发生事件?
}
现在的问题来了 属性我们知道怎么添加了 但是事件怎么在属性窗口也搞一个上去?
  1. 首先你得有一个委托
  2. 然后你得定义一个事件
假设你已经知道了委托和事件 那么加入如下代码:
public delegate void MyEventHandler(object sender, EventArgs e);
public event MyEventHandler ColorChange;//ColorChange会在属性窗口显示

到现在你把控件添加到窗体上已经可以看到ColorChange了

现在委托和事件都有了 现在需要的就是触发事件了 一般情况下触发事件都会单独写一个On[EventName]的虚方法就像这个样子:
public delegate void MyEventHandler(object sender, EventArgs e);
public event MyEventHandler ColorChange;//ColorChange会在属性窗口显示
protected virtual void OnColorChange(EventArgs e) {
    if (ColorChange != null)
        ColorChange(this, e);
}
然而触发这个事件一般是这个样子(至于为什么这么写 最后来讲):
private void pic_red_Click(object sender, EventArgs e) {
    if (pic_main.BackColor == pic_red.BackColor) return;
    this._SelectedColor = pic_main.BackColor = pic_blue.BackColor;
    this.OnColorChange(new EventArgs());
}

private void pic_blue_Click(object sender, EventArgs e) {
    if (pic_main.BackColor == pic_blue.BackColor) return;
    this._SelectedColor = pic_main.BackColor = pic_blue.BackColor;
    this.OnColorChange(new EventArgs());
}

现在 你已经可以找到控件的ColoChange事件双击看看效果了:


然后现在解释上面的代码 首先对于一个事件的委托而言一般都是这个格式:

public delegate void XxxEventHandler(object sender,XxxEventArgs e)
没有返回值的 你去看看 你平时写的那些代码的那些事件双击出来的代码是不是基本都长这个样子?那个sender是啥?sender一般是事件的触发者 谁来触发的 在上面我的OnColorChange中可以看到 这个值是传的this对于这个程序而已 你sender和e 你完完全全可以都不要 只是一个规范而已 既然是规范那还是遵守 一般一个事件的委托都是没有返回值的 而且都有一个sender和e 那么e又是干嘛的?在接下来的例子中我们就会用到e e一般表示事件参数 也就是事件产生的时候保存的一些当时的信息 到这里 既然知道sender是啥 那么我们上面的pic_red和pic_blue的代码可以改一下了 这样写:
public ColorBox() {
    InitializeComponent();
    this.pic_red.Click +=new EventHandler(pic_Click);
    this.pic_blue.Click +=new EventHandler(pic_Click);
}

private void pic_Click(object sender, EventArgs e) {
    PictureBox pic = sender as PictureBox;
    if (pic_main.BackColor == pic.BackColor) return;
    this._SelectedColor = pic_main.BackColor = pic.BackColor;
    this.OnColorChange(new EventArgs());
}
我们没有必要为pic_red,pic_blue都去写一个几乎一样的代码 将他们绑定到同一个事件函数上去就行了 通过sender可以知道是那个pic点击的就取响应pic的属性判断就行了 注意这里既然是手动绑定的事件 那么原来双击绑定的事件就可以删除了 不过不把原来的事件函数删了的话 估计在Designer.cs文件里面会报错 报错的那两句删了就行了 还有 如果以后你的事件的委托我和上面差不多是这个样子的:
public delegate void XxxEventHandler(object sender,EventArgs e)//这里不是XxxEventArgs了
那你没有必要去写一个XxxEventHandler 因为C#本来提供一个和他一模一样的委托的签名 名字就叫EventHandler 用来满足那些只有基本需要的 所以上面的代码 又可以改了:
public delegate void MyEventHandler(object sender, EventArgs e);
public event MyEventHandler ColorChange;
改成:
public event EventHandler ColorChange;
下面再来看一个需求 虽然刚才的ColorChange中可以通过SelectedColor来获取到改变后的颜色 但是有时候事件会产生出一些参数出来 而事件中可能你需要用到这些参数 而这个参数只有在事件产生的时候才会有 所以说没有办法通过控件的属性来获取到 所以这个时候就通过e来传递这个参数 比如很常见的e.Graphics 看看刚才的ColorChange的代码在里面我是通过控件的属性去获取当前的颜色的 我现在希望能直接通过e.Color来得到如果这样那我们就得这样该代码了 这次是最终版本:
[DefaultEvent("ColorChange")]
public partial class ColorBox : UserControl
{
    public ColorBox() {
        InitializeComponent();
        this.pic_red.Click +=new EventHandler(pic_Click);
        this.pic_blue.Click +=new EventHandler(pic_Click);
    }

    private Color _SelectedColor;

    [Browsable(false)]
    public Color SelectedColor {
        get { return this._SelectedColor; }
    }

    public delegate void MyEventHandler(object sender, MyEventArges e);
    [Description("和属性一样这个信息会在属性窗口看到")]
    public event MyEventHandler ColorChange;//ColorChange会在属性窗口显示

    protected virtual void OnColorChange(MyEventArges e) {
        if (ColorChange != null)
            ColorChange(this, e);
    }

    private void pic_Click(object sender, EventArgs e) {
        PictureBox pic = sender as PictureBox;
        if (pic_main.BackColor == pic.BackColor) return;
        this._SelectedColor = pic_main.BackColor = pic.BackColor;
        this.OnColorChange(new MyEventArges(this._SelectedColor));
    }
    public class MyEventArges : EventArgs {
        private Color _Color;

        public Color Color {
            get { return _Color; }
            //set { _Color = value; }
        }

        public MyEventArges(Color clr) {
            this._Color = clr;
        }
    }
}
自定义了一个EventArges里面定义了一个Color属性然后自定义的委托里面的第二个参数是MyEventArges 我想 代码已经不用做过多的解释了 然后 在窗体上 你就可以这样用了:
private void colorBox1_ColorChange(object sender, ColorBox.MyEventArges e) {
        MessageBox.Show(e.Color.ToString());
}

你可以看到参数列表中 第二个参数是ColorBox.MyEventArges那是因为我MyEventArges是写在ColorBox里面的一个内部类 你也可以单独一个文件来写这个类 至于这个问题 我想不用解释吧 从上面终极版本的代码可以看到 事件也可以像属性那样添加描述信息 而且还可以看到 我在最上面控件的类上面加上了DefaultEvent 加上那句之后使得ColorChange成为控件的默认事件 什么意思?也就是我们把控件添加到窗体上的时候直接双击控件的时候代表的事件 仔细想一下 双击Form的时候是不是默认出来的是Load事件?双击Button的时候是Click?双击TextBox的时候出来的是TextChanged?

最后添加事件最后请认准这一格式

public delegate void XxxEventHandler(object sender,XxxEventArges e);
public event XxxEventHandler Xxx;
protected virtual void OnXxx(XxxEventArges e){
    if(Xxx != null) Xxx(this,e);
}

如果你的委托没有特别的需求的话 可以直接用EventHandler 而且要知道.NET中有很多现成的EventHandle 有时候没有必要自己去完全写一个

然后来说说为什么 前面两句不做过多的解释 来解释一些触发事件的那个OnXxx方法、、、或许 你会问 为什么要特地写一个方法出来去触发事件 直接这样不就完了么?

private void pic_Click(object sender, EventArgs e) {
    PictureBox pic = sender as PictureBox;
    if (pic_main.BackColor == pic.BackColor) return;
    this._SelectedColor = pic_main.BackColor = pic.BackColor;
    //this.OnColorChange(new MyEventArges(this._SelectedColor));
    if (ColorChange != null) ColorChange(this, new MyEventArges(this._SelectedColor));
}
这样的确说的过去 假设你有很多地方需要触发这个事件 你不嫌弃每个触发事件的地方都写上这样的代码的话那也罢 可是回头去想一下 前几篇文章里面的MyControl是怎么在控件上绘制Hello World的? 是不是重写的Paint事件?然后在OnPaint里面加入了我们的代码?override void OnPaint?:


看看代码提示是不是有很多OnXxx而那个Xxx是不是就是事件的名字?里面还包含了一个e 先不说规范问题 你就说说如果你直接像上面的代码直接在要触发事件的地方写死了 如果有一天别人想重写你的ColorBox控件做二次封装的时候 要用到里面的ColorChange事件 想在里面加上一点自己的代码怎么办?所以我们一般把触发事件的行为写到一个 OnXxx的虚方法中 而方法包含一个事件参数e 以方便别人重写的时候可以得到一些数据 而触发事件则调用OnXxx方法来实现 别人重写完了也调用base.OnXxx(e)来调用父类的OnXxx 我们的控件作为父类而言 OnXxx就是去调用用户绑定的ColorChange事件 如果说你本来就不打算你的事件被别人重写 - -!、、那还是尽量写个OnXxx在那里吧、、用private修饰、、或者不写成虚方法


添加时间:2014-03-22 06:14:09 编辑时间:2016-11-04 13:30:21 阅读:2286 
C#自定义控件开发 C#控件开发
还没有人留言 要不你来抢一个沙发?
  • 编写评论

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