How to use "this" properly to access the form controls? - c#

In my case I have panels,but to make it clear I will use buttons in my example.
I have 5(or more) buttons and I set an event,for example - OnMouseHover, to all of the five buttons.How do I get ,which one has the mouse hovered if all the events link to one method
To capture the button where the mouse is hovered,I tried using "this",but it links to the form itself.
private void buttonX_MouseHover(object sender, EventArgs e)
{
this.Text = "Test";
}
I expected the text on the specified button where the mouse is hovered to change its text to "Test",but it happened on the form.Text only.
In my real program,I have 60 panels and I again use one method for all OnMouseHover events on any of them.How do I get the panel(or button in the example above) where the mouse is hovered?

this always refers to the class instance (the form). You need to cast the sender instead:
((Control)sender).Text = "Test";
The sender is (generally) the instance raising the event - useful when using the same handler from multiple controls.

This always is the class, in this case the form itself. The object "sender" is the magic word. If it is a button cast it back into a button. Since the object is only a reference you can make changes to the sending object.

Related

C#: KeyDown Event does not work in controls

I am trying to finish my final school project. I am creating a c# winform game to be specific. We can not use anything else.
I will not be posting here code because I got it pretty messed up and I guess u can answer me just with this info.
Setup:
I got my program set up like this. The is main form and two user controls. I switch those controls within the main form during the game. The first one is MENU and the second one is GUI with picturebox acting as a gamescene.
Problem:
Setup quite not important I guess. But what I need to do is to do some action when I press key Down on the first Control (while it is active in the form). Sounds easy I thought at first but the onKeyDown event in the menu.cs(1st usercontrol) is doing nothing when i press the key(The event method is not blank). I tried this.previewKey = true; in the menu load method but it did not even recognize it.
So my question is: Is there any way to use onKeyDown in usercontrols code?
I did it this way becouse I use the same keys in the second controls and i didnt want it to get messy (which obviously did the oposite huh)
TLDR: Need to use onKeyDown event in userControls (keyPreview might be the key)
BONUS
I also need to somehow link variables from Controls 1 to Form and Controls 2.
I looked it up and found out it would be easy in situacion like "Form to Form" but since it is userControls I cant figure it out and I feel like I am just a tiny bit from finishing it.(feels terrible sitting here 9 hours xD please help)
On the keypress event make sure it is for the selected control an not the main form. If you are capturing for the form to determine which key was pressed then use the keypress event for that. You can use a messagebox to verify that you have the right control. Every key has an integer value and you can access and use those by using the properties of e.
Bonus. Depending on how you implemented your code you will have to use either global varibles to pass the data across the forms or use delegates to actively access and set controls on another form
You have to register the event in the Form.Designer.cs :
private void InitializeComponent()
{
// Your form properties here
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);
}
You're KeyDown event can be used like this in the Form.cs code :
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
// To know if your event is working and the value of the key who's pressed
MessageBox.Show("Key Pressed = "
+ e.KeyCode.ToString()
+ ", Value = "
+ e.KeyValue.ToString());
// Example - add some actions bellow
if (e.KeyValue == 13)
MessageBox.Show("Return Key Pressed");
}

How determine where a mouse is wherever it's clicked

I'm making a chess game in C#, and I'm using 64 pictureBoxs to display the board. Now, I'm trying to figure out how to determine where the user clicks to allow the user to move pieces by clicking different tiles/pictureBoxes. I'm new to C#, but I'm trying to do it by detecting where the mouse is whenever it's clicked. I've been using:
void MainFormClick(object sender, EventArgs e){
Point mousePosition = pictureBox1.PointToClient(Cursor.Position); //pictureBox1 is in upper-left
int indexClicked = (mousePosition.X / tileSize) + (8 * (mousePosition.Y / tileSize));
if (indexClicked >= 0 && indexClicked <= 63){
//do code things?
}
this.Text = "Chess - " + indexClicked;
}
I assume this isn't working because the user isn't actually clicking on the form, but on the pictureBoxes. I'd rather not make 64 pictureBox*Click methods, so is there some other way I should be doing this or method I should be using?
EDIT: I just looked at How to get control under mouse cursor. My code needs to be changed a little to better determine which pictureBox the mouse is over when it's clicked, but my real problem is that it doesn't appear that clicking on the pictureBoxes runs the above method. I'm looking for a way to run a block of code whenever the mouse is clicked, regardless of what control or part of the form it's clicked on.
I would probably just use the normal Click event. Pictureboxes have a Click event that you can bind to. I'm not sure how you're adding them, but for 64 of them I'd probably add them dynamically in a loop. As you create each one, bind the Click event to a common handler. Name each control in a way that makes it easy to figure out where it is located. Then in the handler you get an object reference to the picturebox that was clicked and can figure out what to do from there.
What you can do is :
Create 64 pictureBoxes and name them something like squareA1, squareF4 and so on
Create Click event handler
Attach this event handler to all of your pictureboxes in designer
Use object sender to find out in what picturebox event was invoked
The code might look like:
private void square_Click(object sender, EventArgs e)
{
PictureBox target = sender as PictureBox;
MessageBox.Show(target.Name); // displays pictureBox name
}
Code that is added automatically by designer to each picturebox :
this.squareA2.Click += new System.EventHandler(this.square_Click);
Alternatively you can create all 64 pictureboxes in a loop, calculate their positions, and attach this event handler to each of them...
You are already calculating where the mouse cursor is, so you should really just change to use 1 large picture box for the entire board and wire the click event to your posted method.

Windows Forms - detecting button click from a panel inside the form

I have Windows Form named - Form1 and inside Form1 I have a panel named panel1. I use this panel only to add buttons in him. For now there are exactly 9 buttons but I intend to change their number dynamicly if this has something to do with my current problem. What I need is way to detect a when a button from this panel is clicked (I have other buttons too but, they are in Form1 outside the panel) and also to know exactly which button was clicked.
I tried this:
private void panel1_Click(object sender, EventArgs e)
{
MessageBox.Show("HI" + sender);
}
As you can see, it's not much, but was enough to see that I can't do that using pnael1's_click event. Using this code I get the message box when I click anywhere in the panel except the buttons. So how can I do that. Is it possible to do it from inside panel1 or I should group those buttons using another approach but it's important to be able to keep the difference between those buttons which are now in panel1 and the other buttons I may (and in in fact I do have)?
When creating the dynamic buttons, you register that button instance's Click event and attach to an event handler (a single handler can handle all buttons' click event):
var dynamicButton1 = new Button();
dynamicButton1.Click += MyButtonClickHandler;
As long as MyButtonClickHandler has a signature that's suitable for a Click event (that's any method returning void and taking an object and an EventArgs, the handler should respond to a dynamic button's click event for as long as the button instance exists.
As long as you aren't adding controls dynamically over time, and the number of buttons is fixed as soon as the form is initialized, you can use this to add a click event handler to all buttons within a panel:
foreach (var button in panel.Controls.OfType<Button>())
{
button.Click += HandleClick;
}

Is there any way to detect a mouseclick outside a user control?

I'm creating a custom dropdown box, and I want to register when the mouse is clicked outside the dropdown box, in order to hide it. Is it possible to detect a click outside a control? or should I make some mechanism on the containing form and check for mouseclick when any dropdownbox is open?
So I finally understand that you only want it to close when the user clicks outside of it. In that case, the Leave event should work just fine... For some reason, I got the impression you wanted it to close whenever they moved the mouse outside of your custom dropdown. The Leave event is raised whenever your control loses the focus, and if the user clicks on something else, it will certainly lose focus as the thing they clicked on gains the focus.
The documentation also says that this event cascades up and down the control chain as necessary:
The Enter and Leave events are hierarchical and will cascade up and down the parent chain until the appropriate control is reached. For example, assume you have a Form with two GroupBox controls, and each GroupBox control has one TextBox control. When the caret is moved from one TextBox to the other, the Leave event is raised for the TextBox and GroupBox, and the Enter event is raised for the other GroupBox and TextBox.
Overriding your UserControl's OnLeave method is the best way to handle this:
protected override void OnLeave(EventArgs e)
{
// Call the base class
base.OnLeave(e);
// When this control loses the focus, close it
this.Hide();
}
And then for testing purposes, I created a form that shows the drop-down UserControl on command:
public partial class Form1 : Form
{
private UserControl1 customDropDown;
public Form1()
{
InitializeComponent();
// Create the user control
customDropDown = new UserControl1();
// Add it to the form's Controls collection
Controls.Add(customDropDown);
customDropDown.Hide();
}
private void button1_Click(object sender, EventArgs e)
{
// Display the user control
customDropDown.Show();
customDropDown.BringToFront(); // display in front of other controls
customDropDown.Select(); // make sure it gets the focus
}
}
Everything works perfectly with the above code, except for one thing: if the user clicks on a blank area of the form, the UserControl doesn't close. Hmm, why not? Well, because the form itself doesn't want the focus. Only controls can get the focus, and we didn't click on a control. And because nothing else stole the focus, the Leave event never got raised, meaning that the UserControl didn't know it was supposed to close itself.
If you need the UserControl to close itself when the user clicks on a blank area in the form, you need some special case handling for that. Since you say that you're only concerned about clicks, you can just handle the Click event for the form, and set the focus to a different control:
protected override void OnClick(EventArgs e)
{
// Call the base class
base.OnClick(e);
// See if our custom drop-down is visible
if (customDropDown.Visible)
{
// Set the focus to a different control on the form,
// which will force the drop-down to close
this.SelectNextControl(customDropDown, true, true, true, true);
}
}
Yes, this last part feels like a hack. The better solution, as others have mentioned, is to use the SetCapture function to instruct Windows to capture the mouse over your UserControl's window. The control's Capture property provides an even simpler way to do the same thing.
Technically, you'll need to p/invoke SetCapture() in order to receive click events that happen outside of your control.
But in your case, handling the Leave event, as #Martin suggests, should be sufficient.
EDIT: While looking for an usage example for SetCapture(), I came across the Control.Capture property, of which I was not aware. Using that property means you won't have to p/invoke anything, which is always a good thing in my book.
So, you'll have to set Capture to true when showing the dropdown, then determine if the mouse pointer lies inside the control in your click event handler and, if it doesn't, set Capture to false and close the dropdown.
UPDATE:
You can also use the Control.Focused property to determine if the control has got or lost focus when using a keyboard or mouse instead of using the Capture with the same example provided in the MSDN Capture page.
Handle the Form's MouseDown event, or override the Form's OnMouseDown
method:
enter code here
And then:
protected override void OnMouseDown(MouseEventArgs e)
{
if (!theListBox.Bounds.Contains(e.Location))
{
theListBox.Visible = false;
}
}
The Contains method old System.Drawing.Rectangle can be used to indicate if
a point is contained inside a rectangle. The Bounds property of a Control is
the outer Rectangle defined by the edges of the Control. The Location
property of the MouseEventArgs is the Point relative to the Control which
received the MouseDown event. The Bounds property of a Control in a Form is
relative to the Form.
You are probably looking for the leave event:
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.leave.aspx
Leave occurs when the input focus leaves the control.
I just wanted to share this. It is probably not a good way of doing it that way, but looks like it works for drop down panel that closes on fake "MouseLeave", I tried to hide it on Panel MouseLeave but it does not work because moving from panel to button leaves the panel because the button is not the panel itself. Probably there is better way of doing this but I am sharing this because I used about 7 hours figuring out how to get it to work. Thanks to #FTheGodfather
But it works only if the mouse moves on the form. If there is a panel this will not work.
private void click_to_show_Panel_button_MouseDown(object sender, MouseEventArgs e)
{
item_panel1.Visible = true; //Menu Panel
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (!item_panel1.Bounds.Contains(e.Location))
{
item_panel1.Visible = false; // Menu panel
}
}
I've done this myself, and this is how I did it.
When the drop down is opened, register a click event on the control's parent form:
this.Form.Click += new EventHandler(CloseDropDown);
But this only takes you half the way. You probably want your drop down to close also when the current window gets deactivated. The most reliable way of detecting this has for me been through a timer that checks which window is currently active:
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
and
var timer = new Timer();
timer.Interval = 100;
timer.Tick += (sender, args) =>
{
IntPtr f = GetForegroundWindow();
if (this.Form == null || f != this.Form.Handle)
{
CloseDropDown();
}
};
You should of course only let the timer run when the drop down is visible. Also, there's probably a few other events on the parent form you'd want to register when the drop down is opened:
this.Form.LocationChanged += new EventHandler(CloseDropDown);
this.Form.SizeChanged += new EventHandler(CloseDropDown);
Just don't forget to unregister all these events in the CloseDropDown method :)
EDIT:
I forgot, you should also register the Leave event on you control to see if another control gets activated/clicked:
this.Leave += new EventHandler(CloseDropDown);
I think I've got it now, this should cover all bases. Let me know if I'm missing something.
If you have Form, you can simply use Deactivate event just like this :
protected override void OnDeactivate(EventArgs e)
{
this.Dispose();
}

How to force control to fire leave event in winforms using c#?

I am pasting text in textbox1.text and I need textbox1 should fire its leave event by itself.
For now I am using this following code. but i will appreciate if anyone can suggest me an elegant or better way:-
private void event()
{
textbox1.Text = SearchedText;
textbox1.Focus();
textbox2.Focus();
}
First I am pasting text, then setting up Focus on the control, then set up focus again on second control. It is firing leave event of textbox1, but any thing better?
Just call the code directly, no need to wait for an event:
private void textBox1_Leave(object sender, EventArgs e) {
mumble();
}
private void someEvent() {
textBox1.Text = SearchedText;
mumble();
}
void mumble() {
// etc...
}
Just calling textBox1_Leave(this, EventArgs.Empty) works fine too.
You should handle the TextChanged or Validated events instead of the Leave event.
To FORCE Leave, Validating and so on Events, no matter what, I've found ONE working solution.
First i tried:
ProcessTabStop(true);
ProcessTabStop(false);
instead of:
textbox1.Focus();
textbox2.Focus();
Problem with the TextBox 1 and 2 Focus() is that its only Active Component that needs Leave, Validating and so on fired, not other Controls, and besides, what if the form is dynamic, you as a programmer not necessarily have any idea what Control you are trying to Leave, that's why i changed Control.Focus() method to ProcessTabStop method above. The problem is then, if only ONE Control has TabStop true, there is no control to go to and back from. So Events are NOT Fired.
Next problem is that i not necessarily Close the Form with the mouse so Focus doesn't change, I use a Key (Ctrl+Enter) to Accept the Form, and then Leave, validating and so on are NOT fired, when i Send Form Close, as Form Close registers weather there are changes or not. But Values are set in Leave on TextBoxes, so I had to find a solution that worked no matter what i did to it. I almost gave up, actually i had a problem report all filled out, when I thought, what if i set ActiveControl to Null and then to the Control it came from. It worked, but had som "Flicker" due to color change on Parent Panel depending on Active or Inactive.
The "Workaround" that works in all cases is:
Control Old = ActiveControl;
// ActiveControl.SuspendLayout();
// ActiveControl.FindForm().SuspendLayout();
ActiveControl = null;
ActiveControl = Old;
// ActiveControl.FindForm().ResumeLayout();
// ActiveControl.ResumeLayout();
That seems to fire Leave, Validating and so on Events, no matter number of Form Controls and TabStopped Controls. You MAY need to SuspendLayout on either ActiveControl, or Form. My Control (Parent Panel) changes color when Active/Inactive, if I do not Suspend Layout on Form, parent panel gets an unwanted "flicker" effect.
Looking at the solution, it is very obvious, now I've found it, but took me half a day to try different things that solved one or another problem, but not all.
I know this a VERY old thread, but one of very few articles I've found on the subject of Forcing Leave Event to be Fired.

Categories

Resources