Passing control's MouseClick event to its container - c#

I'll try to be concise but comrehensible. I have a dynamic 2-dimensional array of PictureBox elements, and they're all added to the same form via:
this.Controls.Add(PictureBoxArray[i,j]);
Now I've designed an algorithm that would determine which of these PBs are clicked, but I've placed it in the ParentForm_MouseClick method. And now I've reached a paradox. The algorithm I've created returns the proper output, but the ParentForm_MouseClick method is called only when I click on the empty space in the Form, not when I click on PictureBoxes. So my question is - how can I invoke ParentForm_MouseClick method when users click anywhere in the form i.e. can I somehow override PictureBoxes' MouseClick events so that ParentForm_MouseClick is invoked instead?
EDIT: This just occured to me, could I create a custom PictureBoxClass Class that extends the .NET one and just override the MouseClick() event to invoke a method I've previously written?

It seems like you're over-complicating things. If you're going to need to call the code within an event handler from different places, it's a good idea to abstract that out to a different method. You can attach the same event handler to the PictureBoxes which then call that method.
for(int i = 0; i < PictureBoxArray.GetLength(0); i++)
{
for(int j = 0; j < PictureBoxArray.GetLength(1); j++)
{
//attach handler
PictureBoxArray[i,j].Click += pictureBox_Click;
}
}
void pictureBox_Click(object sender, EventArgs e)
{
MyMethod();
}
void parentForm_Click(object sender, EventArgs e)
{
MyMethod();
}
private void MyMethod()
{
//method to be called
}

Related

How to call a callback function on a GUI event? Delegates?

I'm very new to C# and I'm wondering if using delegates is the right way here:
I created a UserControl in Visual Studio Windows Forms Designer.
In a TableLayoutPanel I have 3 x 3 of these UserControls. Each of them gets a row and col index through the constructor.
Now in my Form that contains the TableLayoutPanel I want to call a function whenever one of the UserControls is clicked and have the row and col index in that function call.
I know how to process the Click event in the UserControl. But I don't know how I can call some kind of callback that I can register with the UserControl.
So the UserControl's Click event handler would call something like Callback(Row, Col);
But I don't know how to get method of my Form into the UserControl. In C I would use a function pointer. Do I need a delegate here?
So in my UserControl partial class I would have something like:
public delegate void DoubleClickHandler(int row, int col);
public DoubleClickHandler Callback;
public void On_Click(object sender, EventArgs e)
{
Callback(Row, Col);
}
And when creating the form I would do something like:
MyControl elem = new MyControl(1, 3);
elem.Callback = delegate (int row, int col) { Console.WriteLine("row {0}, col {1}", row, col); }
It works but I don't know if this is the right way to do it.
Winforms would probably use events for this; same idea, different keywords e.g.
For the event prop
//old
public DoubleClickHandler Callback;
//new
public event EventHandler<(int Row, int Col)> SomethingDoubleClicked; //use a past tense name if you raise after, or a "SomethingDoubleClicking" if you raise before - probably hard to raise a click event before but..
OnClick might have code like
var eh = SomethingDoubleClicked;
eh?.Invoke(this, (theRow, theCol));
And subscription might look like:
//"modern"
yourcontrol.SomethingDoubleClicked += (s, e) => Console.WriteLine("row {0}, col {1}", e.Row, e.Col);
//"classic"
yourcontrol.SomethingDoubleClicked += SomeControl_SomethingDoubleClicked;
void SomeControl_SomethingDoubleClicked(object sender, (int Row, int Col) e)
{
Console.WriteLine("row {0}, col {1}", e.Row, e.Col);
}
Note the += rather than = - an event is conceptually a list of methods that shall be invoked in an undefined order
Feel free to vary that tuple in the EventHandler to be a class or something.. Could also derive from EventArgs, but doesn't have to.
If you have no userstate to report, you can simplify to using:
public event EventHandler SomethingDoubleClicked;
//onclick
var eh = SomethingDoubleClicked;
eh?.Invoke(this, EventArgs.Empty);
ourcontrol.SomethingDoubleClicked += (s, e) => ...;
But I would perhaps avoid deriving from EventArgs to provide custom user state, and then using the EventHandler form immediately above - it'll work because when you do:
eh?.Invoke(this, new MyCustomEventArgs(...));
MyCustomEventArgs derives from EventArgs so you can pass the child class in the parent type, but it means the dev that uses it will have to cast it back:
void SomeControl_SomethingDoubleClicked(object sender, EventArgs e)
{
var mcea = e as MyCustomEventArgs;
}
and it's a bit nasty; better to use EventHandler<MyCustomEvenArgs> to make using it a TAB TAB/no casting affair

How to sync up to different events in Winforms

If I have a button which does something and also a double-click event on a data grid which I want to do the same thing, what is the best way to ensure that only one function has to be maintained?
Apart from doing the following, is there any fancy C# way to indicate that two events are to do the same thing?
void button1_Click(...) { MyFunction(); }
void dataGrid1_DoubleClick(...) { MyFunction(); }
void MyFunction() { // do stuff }
I suppose that you are talking about a DataGridView (WinForms) so the signature of the event DoubleClick in the DataGridView and the signature of Click event on a button control is the same.
(An EventHadler). In this case you can simply set the same method using the form designer or manually bind the event
dataGridView1.DoubleClick += new EventHandler(MyFunction);
button1.Click += new EventHandler(MyFunction);
Of course the MyFunction method should match the expected signature of an EventHandler
private void MyFunction(object sender, EventArgs e)
{
// do your work
}
Reviewing my answer after a few minutes I wish to add:
If you find yourself in a situation in which you need to differentiate between the controls using the sender object (like Control c = sender as Control; if (c.Name == "someName") ) I really suggest you to return to the first idea. Call a common method but keep the EventHandler separated for each control involved.
Using VS, in the form's designer view You can set the procedure You want to call to each control's each event in the control's properties window.
image
Just to add to what Steve said, you will want to bind these events to your function manually in the Load event of your form, instead of using the events under the lightning bolt in the properties window in the designer, like so:
private void Form1_Load(object sender, EventArgs e)
{
button1.Click += MyMethod;
dataGridView1.DoubleClick += MyMethod;
}
void MyMethod(object sender, EventArgs e)
{
//Do Stuff
}
Also, declaring a new instance of the EventHandler class has been redundant since Anonymous methods were introduced to C#, you can just point the event directly at the method as shown above.

Trigger control's event programmatically

Assume that I have a WinFoms project. There is just one button (e.g. button1).
The question is: is it possible to trigger the ButtonClicked event via code without really clicking it?
Button controls have a PerformClick() method that you can call.
button1.PerformClick();
The .NET framework uses a pattern where for every event X there is a method protected void OnX(EventArgs e) {} that raises event X. See this Msdn article. To raise an event from outside the declaring class you will have to derive the class and add a public wrapper method. In the case of Button it would look like this:
class MyButton : System.Windows.Forms.Button
{
public void ProgrammaticClick(EventArgs e)
{
base.OnClick(e);
}
}
You can just call the event handler function directly and specify null for the sender and EventArgs.Empty for the arguments.
void ButtonClicked(object sender, EventArgs e)
{
// do stuff
}
// Somewhere else in your code:
button1.Click += new EventHandler(ButtonClicked);
// call the event handler directly:
ButtonClicked(button1, EventArgs.Empty);
Or, rather, you'd move the logic out of the ButtonClicked event into its own function, and then your event handler and the other code you have would in turn call the new function.
void StuffThatHappensOnButtonClick()
{
// do stuff
}
void ButtonClicked(object sender, EventArgs e)
{
StuffThatHappensOnButtonClick();
}
// Somewhere else in your code:
button1.Click += new EventHandler(ButtonClicked);
// Simulate the button click:
StuffThatHappensOnButtonClick();
The latter method has the advantage of letting you separate your business and UI logic. You really should never have any business logic in your control event handlers.
Yes, just call the method the way you would call any other. For example:
private void btnSayHello_Click(object sender, EventArgs e)
{
MessageBox.Show("Hello World!");
}
private void btnTriggerHello_Click(object sender, EventArgs e)
{
btnSayHello_Click(null, null);
}
button1.PerformClick();
But if you have to do something like this maybe it's better to move the code you have under the event on a new method ?
Why don't you just put your event code into a Method. Then have the Event execute the method. This way if you need to execute the same code that the Event rises, you can, but simply just calling the "Method".
void Event_Method()
{
//Put Event code here.
MessageBox.Show("Hello!");
}
void _btnSend_Click(object sender, EventArgs e)
{
Event_Method();
}
void AnotherMethod()
{
Event_Method();
}
Make sense? Now the "Click" event AND anywhere in code you can trigger the same code as the "Click" event.
Don't trigger the event, call the method that the event calls. ;)
In most cases you would not need to do that. Simply wrap your functionality in functions related to a specific purpose (task). You call this function inside your event and anywhere else it's needed.
Overthink your approach.
I recently had this problem where I wanted to programatically click a button that had multiple event handlers assigned to it (think UserControl or derived classes).
For example:
myButton.Click += ButtonClicked1
myButton.Click += ButtonClicked2;
void ButtonClicked1(object sender, EventArgs e)
{
Console.WriteLine("ButtonClicked1");
}
void ButtonClicked2(object sender, EventArgs e)
{
Console.WriteLine("ButtonClicked1");
}
When you click the button, both functions will get called. In the instances where you want to programmatically fire an event handler for a function from a form (for example, when a user presses enter in a Text field then call the InvokeOnClick method passing through the control you. For example
this.InvokeOnClick(myButton, EventArgs.Empty);
Where this is the Form instance you are in.
use a for loop to call the button_click event
private void btnadd_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i <= 2; i++)
StuffThatHappensOnButtonClick();
}
void StuffThatHappensOnButtonClick()
{
........do stuff
}
we assume at least one time you need click the button

How to perform automatic button click on Windows Form based on a Parameter? C#

I created a Windows Application with a form and few buttons on it. I need to fire the button click event automatically some times based on the parameter value passed to the application.
static class SensexPrediction
{
static void Main(string[] Args) <---- Modified this so accepting arguments.
{
Application.Run(new Sensex_Prediction_Form(Args)); <--- Passing Args to Form.
}
}
Below is the Code for Sensex_Prediction_Form method.
public Sensex_Prediction_Form(String[] Args)
{
InitializeComponent();
if (Args.Length != 0) //There is atleast one argument.
{
this.Invoker = Args[0]; <-----Invoker is the name of the data member of the class.
}
}
Now on form load if Invoker == "X" i need to perform button1_Click event code. For that i wrote the following...
private void Sensex_Prediction_Form_Load(object sender, EventArgs e)
{
if(Inovker == "x")
{
predict_butn.performclick();
}
}
But click is not happening automatically even though argument passed is X.
What i couldn't understand is the button name in the solution explorer is predict_butn but when i click on the button the event code is in a function named button1_click. Is this the reason?
Please help. Thanks.
After the suggestions i separated the event code and actual logic in a separate method named prediction.
private void Sensex_Prediction_Form_Load(object sender, EventArgs e)
{
if (Invoker == "Scheduler")
{
prediction();
}
}
And i initialized the data variable of the class with Scheduler as below..
public string Invoker = "Scheduler";
Even then when i load the form the method is not being invoked.
As suggested i corrected the connection between button name and even method name etc.
thank q
Suprisingly..(for me :-))
if (Invoker == "Scheduler")
{
MessageBox.Show("Testing");
prediction();
MessageBox.Show("OK");
}
Testing message is getting executed but after that it just displays the form. so what could be the reason?
Understood the issue..I am getting an exception object reference not set..because i have a line that says
ActiveForm.Text = "Sensex Prediction System ";
At this point form has not been loaded so it can't set the text.
------> Now the issue is how to call a method automatically after loading the form? because the method will have code that will modify the form while executing.
Got it...using the "shown" event for the form able to do what i wanted. Thanks.
Promote the code within button1_click to a method. Then once the form is loaded and if the parameter match your filter, call the method.
private void Button1_Click(object sender, EventArgs e)
{
DoSomething();
}
private void Sensex_Prediction_Form_Load(object sender, EventArgs e)
{
if(Inovker == "X")
{
DoSomething();
}
}
private void DoSomething()
{
...
}
Extract the code out of the button event into it's own method that is called in the button click. Now you just need to call that new extracted method instead of trying to invoke a ui button click.
First you can use Environment.GetCommandLineArgs() instead of passing the args to the constructor of the form.
Second you may assign the wrong event handler to the click. reassign the predict_butn.Click to its correct event handler instead of the current one which is button1_click
Double click button to generate event handler(for example button1_Click(object sender, System.EventArgs e)), and then you want to trigger that method just call button1_Click(null, null)
Of course better solution is to create one function with your logic and then call that function in button click event and when Invoker == "X".
First of all, you're not checking whether Invoker == 'X', but Invoker == 'x'. This may be part of the problem. Comparing strings with == is not good practice, however. You should use
if (Invoker.Equals('X', StringComparison.InvariantCulture))
Is the action performed if you click the button with your mouse? If so, the problem is not the event handler.
If it is not performed, the event handler and the button's event are not connected. You can check in the button's properties whether the event handler is attached to the button's click event.
What happens if you double-click the button in the designer? Is a new event handler created? If so, move the code from button1_click to the new event handler and delete the button1_click event handler.
Event handlers and control events are not automatically associated. You need to do this in the control's properties.

Events and Buttons

I want to get the text of the button whenever I click on it.
The algorithm that I made is where i have a function that is a loop that creates a number of buttons and assigns numbers:
void ListAllPage()
{
if (pageMax < 50)
{
//if page max less than 50
for (int i = 0; i < pageMax; i++)
{
Button newBtn = new Button();
newBtn.Text = i.ToString();
newBtn.Width = 50;
newBtn.Click += page_Clicked;
pageCell.Controls.Add(newBtn);
}
}
}
Now buttons will appear on the screen, their events will be triggered and the function page_Click; will be executed:
public void page_Clicked(object sender, EventArgs e)
{
//inside this function I want to obtain the button number that was clicked by the user. How do I do that?
}
Take note, I must all the functions that I described here,...
My thinking is to feed all the buttons that i created inside the loop to a dictionary..
Dictionary.. it will take variables like this btndic.Add(Button b=new Button,b.text);
But the issue is how to retrieve the buttons,,,
if there is a better way, i would like to hear about it...
instead of using the Click Event -> Use the Command Event: http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.button.oncommand.aspx then you can distinguish which button has been clicked
You just need to cast the sender object to a Button, or more generally, a Control:
public void page_Clicked(object sender, EventArgs e)
{
Control c = sender as Control;
MessageBox.Show("Clicked on " + c.Text);
}
Also, it might be more appropriate to use the Tag property to store your custom information (number). In that case, Text property can be anything you like.
Try this way
public void page_Clicked(object sender, EventArgs e)
{
Button btn=(Button)sender;
}
in your ListAllPage method assign Tag to each button:
newBtn.Tag = i;
In your handler you can obtain button instance from sender:
var clickedButton = (Button)sender;
int pageIndex = (int)clickedButton.Tag;

Categories