将父鼠标事件应用于子元素

Apply parent mouse events to child elements

我正在制作小 Windows 表格申请。
我有 PictureBox(父级)和 Label(子级)。

父级的鼠标事件运行良好,但子元素生成的鼠标事件未反映在父级上。光标也变回默认值(箭头)。

是否可以将子控件生成的事件(例如MouseEnter事件)传递给父控件?

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        Card.MouseEnter += new EventHandler(Card_MouseEnter);
        Card.MouseLeave += new EventHandler(Card_MouseLeave);
        Card.MouseDown += new MouseEventHandler(this.Card_MouseDown);
        Card.MouseUp += new MouseEventHandler(this.Card_MouseUp);
    }

    void Card_MouseLeave(object sender, EventArgs e)
    {
        this.Card.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.card_bg));
        this.Rename("Running!");
    }

    void Card_MouseEnter(object sender, EventArgs e)
    {
        this.Card.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.card_hover_bg));
    }

    private void Card_MouseDown(object sender, EventArgs e)
    {
        this.Card.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.card_click_bg));
    }

    private void Card_MouseUp(object sender, EventArgs e)
    {
        this.Card.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.card_hover_bg));
        this.Rename("Please Wait...");
    }

    private void CardName_MouseDown(object sender, MouseEventArgs e)
    {
        
    }

    void Rename(string args)
    {
        this.CardName.Text = args;
    }

    private void CardName_Click(object sender, EventArgs e)
    {

    }
}

      

            This is what I have                       This is what I want to achieve
    

第一个动画代表我现在拥有的,第二个是我需要实现的:)

When I'm making pictureBox1.Controls.Add(label1) label1 is disappearing and I tried bring to front and change color but couldn't do it. Please if you will have any idea show me in provided code by me to be understandable for me. Thank you all again and again :)

您会使用这样的代码,也许在表单的 Load() 事件中:

private void Form1_Load(object sender, EventArgs e)
{
    Point pt = CardName.Parent.PointToScreen(CardName.Location);
    Card.Controls.Add(CardName);
    CardName.Location = Card.PointToClient(pt);
}

这使标签保持在原来的位置,但使图片框成为父级。

不确定你哪里错了。下面是一个展示它的实际例子。 PictureBox (Card) 和 Label (CardName) 都位于 Panel (panel1) 内。单击按钮 2 切换卡片的可见性。单击 button1 使 Card 成为 CardName 的父级。您可以看到,一开始只有 Card 切换可见性,但是在单击 button1 并设置 Parent 之后,两者一起切换可见性,因为 CardName 是 Card 的子级(它还更改了其 BackColor 以匹配其新父级的 BackColor):

代码:

public partial class Form1 : Form
{

    private void button1_Click(object sender, EventArgs e)
    {
        Point pt = CardName.Parent.PointToScreen(CardName.Location);
        Card.Controls.Add(CardName);
        CardName.Location = Card.PointToClient(pt);
    }

    private void button2_Click(object sender, EventArgs e)
    {
        Card.Visible = !Card.Visible;
    }

}

When I move mouse over label, panel thinks mouse left it and rises MouseLeave event

这里是判断光标是否真的离开面板边界的方法,而不是简单地在面板中输入子控件:

private void panel1_MouseEnter(object sender, EventArgs e)
{
    panel1.BackColor = Color.Red;
}

private void panel1_MouseLeave(object sender, EventArgs e)
{
    Point pt = panel1.PointToClient(Cursor.Position);
    if (!panel1.ClientRectangle.Contains(pt))
    {
        // we only get in here when the cursor leaves the BOUNDS of panel1
        panel1.BackColor = Control.DefaultBackColor;
    }            
}

首先,您应该构建一个 UserControl 作为所有对象的容器:它会使一切变得更简单(我在这里使用的实际上是一个 UserControl,经过修改以符合您当前的设置)。

当与 PictureBox 以外的 Control 交互时,您可以决定是要在主 Control 上触发类似的操作,还是根据生成的事件执行不同的操作。

▶ 当 Mouse Pointer 进入你 assembled 控件时,你想更改默认的 Cursor:然后,当其中一个 Label 引发 Enter 事件时,调用处理此问题的主控件的方法。事件处理程序是一种方法,您可以调用它。

▶ 当一个Label被点击时,你不希望触发主控件的相关动作:在这种情况下,没有什么可做的,只是处理这个事件并执行所需的操作。

▶ 标签应该是主控件的子控件。您使用的是 PictureBox,它不是 ContainerControl。无论如何,您都可以向其添加子控件。您需要在代码中执行此操作,因为 - 如前所述 - PictureBox 并非设计用于托管控件,因此您不能将一个 放入 它:您放置的控件将以容器为父级托管 PictureBox(您的表单,此处)。
当你在代码中设置父级时,你需要记住子控件的位置是相对于旧父级的,所以你必须重新定义它的位置。

例如:PictureBox.Bounds = (100, 100, 100, 200) / Label.Bounds = (100, 250, 100, 50)

当 PictureBox 成为标签的父级时,Label.Location 仍然是 (100, 250):因此,现在它将 隐藏,因为它在外面其新父级的可见边界。您必须相对于新主机重新定位它:它的新位置应该是 (0, 150),以保持之前的相对位置。

PictureBox.Control.Add(Label);
//[...]
Label.Location = new Point(Label.Left - PictureBox.Left, Label.Top - PictureBox.Top);
=> Label.Location = (100 - 100, 250 - 100) => (0, 150)

或者,水平居中:

Label.Location = new Point((PictureBox.Width - Label.Width) / 2, Label.Top - PictureBox.Top);
=> Label.Location = ((100 - 100) / 2, 250 - 100) => (0, 150) // <- Since both have the same Width

或者,使用相对于屏幕的位置:

var p = Label.PointToScreen(Point.Empty);  // Relative to the ClientRectangle (Top/Left = (0, 0))
PictureBox.Controls.Add(Label);
Label.Location = PictureBox.PointToClient(p);

无论如何,之后调用 BringToFront(),以确保新的子控件位于顶部并锚定控件,因此它将保持其位置并且其宽度将绑定到父宽度:

Label.BringToFront();
Label.Anchor = AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right;

现在,假设您想在鼠标进入您的组合控件时将光标更改为Cursors.Hand,并在离开时重置为默认值:

▶ 您希望 Cursor 在任何情况下都能改变形状。
▶ 您希望在单击 PictureBox 和单击其中一个 Label 时生成不同的动作。
▶ 两个标签在点击时可以有不同的动作。

可视化示例中,PictureBox上方的Label名为lblTitle里面的Label 底部的 PictureBox 名为 lblFooter.
PictureBox 被命名为 ImageView.

设置处理程序:

注意:对于 UserControl,事件处理(例如,与 MouseEnter 相关的事件)发生以下变化:

// The Parent's MouseEnter calls OnMouseEnter
protected override void OnMouseEnter(EventArgs e)
{
    base.OnMouseEnter(e);
    this.Cursor = Cursors.Hand;
}

// Child Controls just call the same method
private void Labels_MouseEnter(object sender, EventArgs e) => OnMouseEnter(e);

public Form1()
{
    InitializeComponent();

        Point p = lblFooter.PointToScreen(Point.Empty); 
        ImageView.Controls.Add(lblFooter);
        lblFooter.Location = ImageView.PointToClient(p);

        ImageView_MouseEnter += ImageView_MouseEnter;
        ImageView_MouseLeave += ImageView_MouseLeave;
        // Not added in the code here, do whatever is needed with this handler
        ImageView_Click += ImageView_Click;

        lblFooter.MouseEnter += Labels_MouseEnter;
        lblFooter.MouseLeave += Labels_MouseLeave;
        lblFooter.MouseClick += lblFooter_MouseClick;

        lblTitle.MouseEnter += Labels_MouseEnter;
        lblTitle.MouseLeave += Labels_MouseLeave;
        lblTitle.MouseDown += lblTitle_MouseDown;
        lblTitle.MouseUp += lblTitle_MouseUp;
}

private void ImageView_MouseEnter(object sender, EventArgs e) => this.Cursor = Cursors.Hand;

private void ImageView_MouseLeave(object sender, EventArgs e) => this.Cursor = Cursors.Default;

private void Labels_MouseEnter(object sender, EventArgs e)
{
    ImageView_MouseEnter(ImageView, e);
    // [...]
    // Do stuff related to the Labels Enter event
}

private void Labels_MouseLeave(object sender, EventArgs e) {
    ImageView_MouseLeave(ImageView, e);
}

private void lblTitle_MouseDown(object sender, MouseEventArgs e) {
    // Perform actions when the Mouse button is held down lblTitle
}

private void lblTitle_MouseUp(object sender, MouseEventArgs e) {
    // Perform actions when the Mouse button is released
}

private void lblFooter_MouseClick(object sender, MouseEventArgs e) {
    // Perform actions on a Mouse Click event on lblFooter
}