I am making little Windows Forms Application.
I have PictureBox (parent) and Label (child) in it.
The Parent's Mouse Events are working perfectly, but Mouse events generated by child elements are not reflected on the Parent. The Cursor also changes back to its default (arrow).
Is it possible to pass events generated by child Controls, e.g., the MouseEnter event, to the Parent Control?
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
The first animation represents what I have now, the second is what I need 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 :)
You'd use code like this, maybe in the Load() event of the Form:
private void Form1_Load(object sender, EventArgs e)
{
Point pt = CardName.Parent.PointToScreen(CardName.Location);
Card.Controls.Add(CardName);
CardName.Location = Card.PointToClient(pt);
}
This keeps the label in the same position as it was, but makes the picturebox the parent.
Not sure where you're going wrong. Here's an example showing it in action. Both the PictureBox (Card) and Label (CardName) are inside a Panel (panel1). Clicking on button2 toggles the visibility of the Card. Clicking on button1 makes Card the Parent of CardName. You can see that at first, only the Card toggles visibility, but after clicking on button1 and setting the Parent, both toggle visibility together since CardName is a Child of Card (it also changes its BackColor to match that of its new Parent):
Code:
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
Here is how you can tell if the cursor has actually left the BOUNDS of the Panel, as opposed to simply enter a child control within the Panel:
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;
}
}
First of all, you should build a UserControl as a container for all your objects: it'd make everything simpler (the one I'm using here is actually a UserControl, modified to comply with your current setup).
When a Control other than the PictureBox is interacted with, you can decide whether you want to trigger a similar action on the main Control or perform a different action based on what event has been generated.
▶ When the Mouse Pointer enters you assembled Control, you want to change the default Cursor: then, when one of the Labels raises the Enter event, call the method of the main Control that handles this. An event handler is a method, you can call it.
▶ When a Label is clicked, you don't want to trigger the related action of the main Control: in this case, there's nothing to do, just handle this event and perform the required action.
▶ The Label should be child of the main Control. You're using a PictureBox, which is not a ContainerControl. You can add child controls to it anyway. You need to do this in code, since - as mentioned - the PictureBox is not designed to host Controls, thus you cannot drop one inside it: the Control you drop will be parented with the Container that hosts the PictureBox (your Form, here).
When you set the parent in code, you need to remember that the Location of the child control is relative to the old Parent, so you have to re-define it's position.
E.g: PictureBox.Bounds = (100, 100, 100, 200) / Label.Bounds = (100, 250, 100, 50)
When the PictureBox becomes Parent of your Label, the Label.Location is still (100, 250): so, now, it will be hidden, since it's outside the visible bounds of its new Parent. You have to reposition it in relation to the new host: its new Location should be (0, 150), to keep the previous relative position.
PictureBox.Control.Add(Label);
//[...]
Label.Location = new Point(Label.Left - PictureBox.Left, Label.Top - PictureBox.Top);
=> Label.Location = (100 - 100, 250 - 100) => (0, 150)
Or, centered horizontally:
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
Or, using positions relative to the Screen:
var p = Label.PointToScreen(Point.Empty); // Relative to the ClientRectangle (Top/Left = (0, 0))
PictureBox.Controls.Add(Label);
Label.Location = PictureBox.PointToClient(p);
In any case, call BringToFront() after, to ensure that the new child Control is brought on top and anchor the Control, so it will keep its position and its Width will be bound to the Parent Width:
Label.BringToFront();
Label.Anchor = AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right;
Now, assuming you want to change the Cursor to Cursors.Hand when the Mouse enters your combined Control and reset to default when it leaves it:
▶ You want the Cursor to change shape in any case.
▶ You want to generate different actions when the PictureBox is clicked and when one of the Labels is clicked.
▶ Both Labels can have distinct actions when clicked.
→ In the visual sample, the Label above the PictureBox is named lblTitle, the Label inside the PictureBox, at the bottom, is named lblFooter.
→ The PictureBox is named ImageView.
Setup the handlers:
NOTE: With a UserControl, the events handling, e.g., in relation to MouseEnter, changes in:
// 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
}
I have Windows Form named Form1 and inside I have a dynamic SplitContainer named splitcontainer.
I want to know which panel is selected when the mouse is clicked at runtime.
I tried to use mouseclick event in the splitContainer properties but I haven't succeed.
You need to bind to the MouseClick events of the panel inside the split container.
I added a container called "splitContainer1" with 2 panels, Panel1 and 2
I wired up the below events and it seems to work
private void splitContainer1_Panel1_MouseClick(object sender, MouseEventArgs e)
{
MessageBox.Show("Panel1");
}
private void splitContainer1_Panel2_MouseClick(object sender, MouseEventArgs e)
{
MessageBox.Show("Panel2");
}
After your further comments, I've edited the below to show how to manually bind the mouse click events of the 2 panels of your dynamically added container.
private void splitContainerHorizontalToolStripMenuItem_Click(object sender, EventArgs e)
{
SplitContainer spltcnt = new SplitContainer();
spltcnt.Dock = DockStyle.Left;
spltcnt.Orientation = Orientation.Horizontal;
spltcnt.SplitterWidth = 4;
spltcnt.Visible = true;
spltcnt.Size = new System.Drawing.Size(731, 615);
spltcnt.BorderStyle = BorderStyle.Fixed3D;
spltcnt.SplitterDistance = 351;
//Manually bind the mouse click events.
spltcnt.Panel1.MouseClick += Panel1OnMouseClick;
spltcnt.Panel2.MouseClick += Panel2OnMouseClick;
Controls.Add(spltcnt);
}
private void Panel1OnMouseClick(object sender, MouseEventArgs mouseEventArgs)
{
MessageBox.Show("Panel1");
}
private void Panel2OnMouseClick(object sender, MouseEventArgs mouseEventArgs)
{
MessageBox.Show("Panel2");
}
You can of course call the mouse click handler methods anything you like.
Thanks
I have a splitcontainer. In Panel 1 I have 7 toolstripbuttons that opens a User Control in Panel 2 when I click on them. The user control consists of different textboxes that the user can fill in. The code looks like this:
private void toolStripButtonBaseLayers_Click(object sender, EventArgs e)
{
splitContainer1.Panel2.Controls.Clear();
BaseLayers BL = new BaseLayers();
splitContainer1.Panel2.Controls.Add(BL);
}
private void toolStripButtonSpatialCoverage_Click(object sender, EventArgs e)
{
splitContainer1.Panel2.Controls.Clear();
SpatialCoverage SC = new SpatialCoverage();
splitContainer1.Panel2.Controls.Add(SC);
}
private void toolStripButtonFloodMaps_Click(object sender, EventArgs e)
{
splitContainer1.Panel2.Controls.Clear();
FloodMaps FM = new FloodMaps();
splitContainer1.Panel2.Controls.Add(FM);
}
Now everytime I open another user control with the click event the user control will be empty because I clear the panel and open a new User Control. This is not what I want to do!
My question is how it is possible to save everything you filled in in the textboxes? And how it is possible that all the user control values will be still filled in when switching between them? For example I fill the user control BaseLayers in, I switch to the user control Spatial Coverage and Flood Maps and when I swith back to the user control BaseLayers everything still have to be filled in.
Create all your controls and hide all of them(Visible=false). Then in Click-Events just set Visible=true for the appropriate control.
Example:
private void toolStripButtonFloodMaps_Click(object sender, EventArgs e)
{
SetChildVisibleForPanel(typeof(FloodMaps), splitContainer1.Panel2);
}
static void SetChildVisibleForPanel(Type visChildType, Panel pnl)
{
if (pnl.Controls.Count==0)
{
//Create your controls with Visible=false;
}
foreach (Control ctl in pnl.Controls)
{
ctl.Visible = (ctl.GetType() == visChildType);
}
}
I have a DevExpress grid, which is disabled on screen. When I click the control, I want it to become enabled. Right now I have a click event set up for the grid:
private void gridPSR_Click(object sender, EventArgs e)
{
gridPSR.Enabled = true;
}
This isn't working. How should I be going about this?
Disabled controls do not receive windows messages, so you will never get the click message on that control. Assuming this is Winforms, you can listen for the click on the form (or whatever control is hosting this grid) and check if the click location is in the rectangle of the disabled control and then enable the control accordingly:
void Form1_MouseClick(object sender, MouseEventArgs e)
{
if (gridPSR.ClientRectangle.Contains(e.Location))
{
gridPSR.Enabled = true;
}
}
I know it's an old post but for me bounds worked instead of ClientRectangle
private void OnPanelMouseClick(object sender, MouseEventArgs e)
{
if ((e.Button == MouseButtons.Left) &&
myControl.Bounds.Contains(e.Location) &&
!myControl.Enabled)
{
myControl.Enabled = true;
}
}
Where myControl is member variable of your control instance. OnPanelMouseClick handler should be linked with MouseClick event of form or container that holds control.
In this code I am setting an event on a disabled TextBox control called 'txtNumLabels'. I tested this code with the text box both on a Form and also with having it within a GroupBox container.
Set an event in the constructor after the 'InitializeComponent();'
this.txtNumLabels.Parent.MouseClick += new System.Windows.Forms.MouseEventHandler(this.txtNumLabels_Parent_MouseClick);
Here is the event handler -
private void txtNumLabels_Parent_MouseClick(object sender, MouseEventArgs mouseEvent)
{
// The Bounds property of a control returns a rectangle of its Location and Size within its parent control
Rectangle rect = txtNumLabels.Bounds;
// Other method that gets the same rectangle -
// Point t = txtNumLabels.Location;
// Size ts = txtNumLabels.Size;
// Rectangle rect = new Rectangle(t, ts);
if (rect.Contains(mouseEvent.Location))
{
txtNumLabels.Enabled = true;
}
}
I have a solution with several forms, each may have TextBox's/controls and a button to show the SIP (the bottom bar is hidden).
When the user clicks my SIP button, the SIP is enabled but the focus is now the button. I want the user to click the button - the SIP to display but the focus to remain on the control that had the focus before the user clicked the button. Does anyone know how to do this? Thanks.
Instead of using an standard button, you can create a custom one by deriving from the Control class and overriding the OnPaint method. A control created this way will not claim the focus by default when treating the Click event (tested on VS2008 netcf 2.0).
public partial class MyCustomButton : Control
{
public MyCustomButton()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.DrawString("Show SIP", Font, new SolidBrush(ForeColor), 0, 0);
// Calling the base class OnPaint
base.OnPaint(pe);
}
}
The solution of nathan will work also for Compact Framework or native Windows Mobile applications. In the textbox GotFocus set a global var and use this in the buttons click event to set the focus back to the last active textbox:
//global var
TextBox currentTB = null;
private void button1_Click(object sender, EventArgs e)
{
inputPanel1.Enabled = !inputPanel1.Enabled;
if(currentTB!=null)
currentTB.Focus();
}
private void textBox1_GotFocus(object sender, EventArgs e)
{
currentTB = (TextBox)sender;
}
regards
Josef
Edit: Solution with subclass of TextBox:
class TextBoxIM: TextBox{
public static TextBox tb;
protected override void OnGotFocus (EventArgs e)
{
tb=this;
base.OnGotFocus (e);
}
}
...
private void btnOK_Click (object sender, System.EventArgs e)
{
string sName="";
foreach(Control c in this.Controls){
if (c.GetType()==typeof(TextBoxIM)){
sName=c.Name;
break; //we only need one instance to get the value
}
}
MessageBox.Show("Last textbox='"+sName+"'");
}
Then, instead of placing TextBox use TextBoxIM.