C#自定义控件的鼠标事件问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#自定义控件的鼠标事件问题相关的知识,希望对你有一定的参考价值。

我用C#写了个自定义的控件,上面有个label和picturebox。将整个控件拉在窗体上然后写它的mousemove,mouseEnter的那几个事件,发觉如果鼠标是移到label和picturebox时那几个事件是不会响应的,只有鼠标移到空白地方才响应。难道我自定义控件上的label和picturebox截取了鼠标事件,我想当鼠标在整个控件里移动都能触发,怎么改啊

好像没有太好的办法,我这里提示一个思路使用帮助函数在窗体的子控件添加时绑定子控件的鼠标事件,当子控件移除时取消对子子控件鼠标事件的绑定,在窗体的ControlAdded及ControlRemoved中处理。这样起码不用自定义控件了。


参考技术A 重写一下label和picturebox的mousemove,mouseEnter事件,在里面加上你的自定义控件要实现的功能,也可以调用控件的mousemove,mouseEnter事件 参考技术B 这些事件是针对窗体的吧,感觉描述得不太清楚 上图

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

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

原文请看:http://clzf.co/blog.php?id=4

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

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

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

到这里 我不会告诉你 我写了一大堆来来描述 委托和事件 后来果断删除了 因为我总是一个不小心就扯到到其他地方去了、、所以我还是决定放弃了 本来我就是半灌水的 扯了n多东西出来也就算了 估计到时候越看越糊涂都说不定 所以还是建议去看张子阳的博客吧 他说的已经很清晰了 所以这里直接就开始 为自定义控件添加事件 不过我还是要强调一点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顾名思义 颜色改变了 所以颜色改变了的时候才发生 先暂时改一下刚才的代码:

<pre name="code" class="csharp"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 xxEventHandler(object sender,EventArgs 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文件里面 不过不把原来的事件函数删了的话 会报错 报错的那两句删了就行了还有 如果以后你的事件的委托我和上面差不多是这个样子的:
public delegate void xxEventHandler(object sender,EventArgs e)
那你没有必要去写一个xxEventHandler 因为C#本来提供一个和他一模一样的委托的签名 名字就叫EventHandler 用来满足那些只有基本需要的 所以上面的代码 又可以改了:
public delegate void MyEventHandler(object sender, EventArgs e);
public event MyEventHandler ColorChange;
改成:
public event EventHandler ColorChange;
下面再来看一个需求 如果平时用过事件参数e的人就知道 我接下来想要说什么 看看刚才的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

然后来说说为什么 前面两句不做过多的解释 来解释一些触发事件的那个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修饰、、

以上是关于C#自定义控件的鼠标事件问题的主要内容,如果未能解决你的问题,请参考以下文章

C# WinForms 用鼠标拖动控件

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

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

C#自定义控件内多个控件的单击事件

C# WinForm 用户控件的自定义事件问题

c# winform 制作自定义控件