C# Simplifying a click event method to include other controls [duplicate] - c#

This question already has answers here:
Identify who invokes Event Handler
(2 answers)
How to create an event handler for multiple buttons?
(3 answers)
How to write a common ClickEvent for many labels?
(4 answers)
clear mutiple textboxes by reusing an event handler
(1 answer)
Is it safe to use the same event handler for different Windows Forms Controls?
(7 answers)
Closed 5 years ago.
I was looking to simplify the following action: Click on a panel, and the panel changes its color to green, and if it is green, change it back to gray. I was wondering how you would write the controls into the method as an argument so that it would work for any control without having to duplicate the event method.
Normally: Panel Click event
panel_Click ()
//some if statement
panel1.BackColor = green
panel1.BackColor = gray
then repeat per control. Instead of this, I would create a central method that all of the controls are subscribed to, that read the name of the panel like:
genericpanel_Click(){
ChangeColor(thisPanelname);
}
and then that would make use of the argument/parameter:
public void ChangeColor(panel? Mypanel) {
//some if...
Mypanel.BackColor = Green
Mypanel.BackColor = Gray
}
What's the accurate equivalent of this pseudo code into a working one?
Edit: Okay, yes, I can see now that it's a duplicate, no need to downvote further. I just didn't know what to search for specifically. Anyways, I've found the answer at this point.

All events of a control provide its source as the first argument. You just have to cast it to the right type:
void panel_Click(object sender, EventArgs e) {
Panel myPanel = (Panel)sender;
ChangeColor(myPanel);
}
All definitions for event handler delegates provide the sender, for example:
public delegate void EventHandler(object sender, EventArgs e);
public delegate void EventHandler<T>(object sender, T e);
In case you define your own events, just use those delegate types and pass the source of the event as the first argument.

You can subscribe all panels to this method:
private void panel_Click(object sender, EventArgs e)
{
Panel clickedPanel = sender as Panel;
if ( clickedPanel != null )
{
if ( clickedPanel.BackColor == Color.Blue )
{
clickedPanel.BackColor = Color.Red;
}
else
{
clickedPanel.BackColor = Color.Blue;
}
}
}
If you only subscribe panels to this even the first check wouldn't be needed, but to be safe its there.
This method takes the sender (the panel that activated the event) and check's its background color. If color A set to Color B -> else Color A

Related

Winform: How to know if CheckBox has been checked from code or UI [duplicate]

This question already has answers here:
Differentiate between a user changing the Checkbox.Checked value, or it programmatically changing
(4 answers)
Closed 2 years ago.
I'm developing a winform c# application. In this application, I'm using Checkbox (JCS.ToggleSwitch to be precise).
I have added a CheckedChanged event to execute some code when the Checkbox has been checked.
I would like to be able to change the state of the checkbox in the code without triggering the code in this CheckedChanged event. In order to do that, I would like to know if it's possible to know if the checkbox state has been changed because of code or because the user clicked on the checkbox.
I tried to detect that with the sender DummyCheckbox_CheckedChanged(object sender, EventArgs e), although, the sender is the same when the state has been changed from a piece of code or by the UI.
Do you know if it's possible to only execute my code when the checkbox has been updated by the user?
private JCS.ToggleSwitch DummyCheckbox;
this.DummyCheckbox= new JCS.ToggleSwitch();
this.DummyCheckbox.CheckedChanged += new JCS.ToggleSwitch.CheckedChangedDelegate(this.DummyCheckbox_CheckedChanged);
private void DummyCheckbox_CheckedChanged(object sender, EventArgs e)
{
if(???) //CheckState has been changed from UI
{
if(DummyCheckbox.Checked)
{
//Code 1
}
else
{
//Code 2
}
}
else //CheckState has been changed because of code "DummyCheckbox.Checked = true"
{
//Code 3
}
}
No this is not possible in the way you are thinking of.
This is because changing the state will result in the UI element to fire the event.
What you could do, is to remove the even handler before you change the state, change it and add the event handler again. As such:
this.DummyCheckbox.CheckedChanged -= new JCS.ToggleSwitch.CheckedChangedDelegate(this.DummyCheckbox_CheckedChanged);
this.DummyCheckbox.Checked = true; //Or false if needed
this.DummyCheckbox.CheckedChanged += new JCS.ToggleSwitch.CheckedChangedDelegate(this.DummyCheckbox_CheckedChanged);

How do I make buttons do the same thing?

I just started programming, and I want to use WinForms to make multiple buttons that you can click on to change from white to lime-green and back to white. I have done this for one button:
private void button1_Click(object sender, EventArgs e)
{
if (button1.BackColor != Color.Lime)
{
button1.BackColor = Color.Lime;
}
else
{
button1.BackColor = Color.White;
}
}
Now I could copy and paste that for all of the buttons, but I know that is inefficient; and if I use winforms to reference button1 on button2, it will just change the color of button1 (obviously).
So, do I need to use a helper method, new class, or something else? What would that look like?
There are a couple of approaches. One might be to create a common function which the different buttons call:
private void button1_Click(object sender, EventArgs e)
{
ChangeColor(button1);
}
private void ChangeColor(Button button)
{
if (button.BackColor != Color.Lime)
button.BackColor = Color.Lime;
else
button.BackColor = Color.White;
}
Then each button handler can use that same function call.
Or, if all of these buttons will always ever do exactly the same thing, then you can use one click handler function for all of them. In this case what you'd need to do is determine which button invoked the handler (whereas you're currently referencing button1 directly) so that you know which one to change. The sender object passed into the handler function is actually a reference to the form element which invoked the handler. All you need to do is cast it:
private void button_Click(object sender, EventArgs e)
{
var button = (Button)sender;
if (button.BackColor != Color.Lime)
button.BackColor = Color.Lime;
else
button.BackColor = Color.White;
}
So first the handler grabs a reference to the button which invoked it, then runs the logic on that button. Note also how I made the name of the handler function slightly more generic. Now you'd go to the form designer and set button_Click as the click handler for all of the buttons which should invoke this.
You do this the exact same way you'd do it for any C# class. You derive your own class and customize the base class behavior. Every event has a corresponding OnXxxx() method that you can override.
Add a new class to your project and paste this code:
using System;
using System.Windows.Forms;
class MyButton : Button {
protected override void OnClick(EventArgs e) {
// Your code here
//...
base.OnClick(e);
}
}
Change the code in OnClick() to do what you want to do. Compile. You'll now have your own button control on the top of the toolbox. And can drop as many copies of it as you want on a form. They'll all behave the same without having to add any code in the form.
Probably the easiest way would be to have each button invoke the same click handler. Then inside of your handler use the Sender instead of hard coding Button1.
private void buttons_Click(object sender, EventArgs e)
{
var theButton = (Button) sender;
if (theButton.BackColor != Color.Lime)
{
theButton.BackColor = Color.Lime;
}
else
{
theButton.BackColor = Color.White;
}
}
You can get the button that raised the Click event by casting sender to Button.
You can then add the same handler to every button.
I'm a VB guy.... in VB.Net you can add multiple handlers for events and connect multiple events to the same handler.
This sub hooks all clicks to color the buttons.
Private Sub ColorButtons(sender As System.Object, e As System.EventArgs) _
Handles Button1.Click, Button2.Click, ..
I do this all the time accidentally because I drag/copy a control to make a new one and the new button gets added to the original's events.
Other Subs can handle the same events to do other work - both will execute.
No idea how to do this in C#.
The proper way to do this really is to associate each button's click event to the function you have coded for that purpose (you want the function to run when the button is clicked, right?), so add the following (or similar) to an appropriate section of your code:
MyButton1.Click += new RoutedEventHandler(buttons_Click);
MyButton2.Click += new RoutedEventHandler(buttons_Click);
etc...
You can associate as many controls to the event handler as you like.
What I usually do before is this:
private void button2_Click(object sender, EventArgs e)
{
button1.PerformClick();
}
This code will just simply run the codes under button1_Click.
But try not to practice as such and just simply put it in a function/method just like what David suggested.

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.

C# WinForms showing a control as a dropdown

Gretings
I need to have a custom control for my application. Basically its an expression editing GUI. You have, say, expression:
If variable_x is greater than variable_y
And you can click on "greater than" and change it to other comparator (like, equal to or less than).
The control thus must look like a label, but when you click it, it must show a dropdown (like combobox does) that has a listview inside (or maybe some other control) so that user can choose something. In a sense, i need a combobox without the box itself, replaced by something else (in this case, a label).
I know how to make custom controls, i understand i must somehow DropDown on mouse click or enter keypress, and hook events so that when whatever i dropped has closed, the choice is made, and also somehow track if user clicked elsewhere so i can close this dropdowned control. But i dont know if this is easy to do (some built-in method exists) or i have to do it all myself? Dont want to redevelop the wheel....
Please tell me if there are easy ways to do this.
Thanks!
You can extend the ComboBox control to update the DropDownStyle on Enter and LostFocus events.
public partial class MyComboBox : ComboBox
{
public MyComboBox()
{
InitializeComponent();
this.Dock = DockStyle.Fill;
this.SelectionChangeCommitted += this.OnComboBoxSelectionChangeCommitted;
this.Enter += this.OnControlEnter;
this.LostFocus += this.OnComboBoxLostFocus;
}
private void OnControlEnter(object sender, EventArgs e)
{
this.DropDownStyle = ComboBoxStyle.DropDownList;
}
private void OnComboBoxLostFocus(object sender, EventArgs e)
{
this.DropDownStyle = ComboBoxStyle.Simple;
}
private void OnComboBoxSelectionChangeCommitted(object sender, EventArgs e)
{
// Notify to update other controls that depend on the combo box value
}
}

Hooking up generic event handlers to multiple controls of the same type

I have a WinForms app that contains many NumericUpDown controls. In a nutshell, if my users enter a value into the control and then delete the text, I want to restore it (the text) when the control loses focus. So I decided that I'd check .Text when the control loses focus and if it's empty, I set .Text = .Value.ToString().
I'm doing this in the Leave event handler and it works just fine. But as I said, I have many of these controls (18, to be exact). I don't like creating 18 Leave event handlers that all do the same thing so I created a generic one like this:
private void numericUpDown_GenericLeave(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(((NumericUpDown)sender).Text))
((NumericUpDown)sender).Text = ((NumericUpDown)sender).Value.ToString();
}
I started to hook up all of the controls to this generic event handler but I quickly got tired of doing this:
numericUpDown1.Leave += numericUpDown_GenericLeave;
numericUpDown2.Leave += numericUpDown_GenericLeave;
numericUpDown3.Leave += numericUpDown_GenericLeave;
...
numericUpDown18.Leave += numericUpDown_GenericLeave;
So I thought I'd create a function that would return a list of all the controls of a specified type and then loop through that list and hookup the event handlers. That function looks like this:
public static List<Control> GetControlsOfSpecificType(Control container, Type type)
{
var controls = new List<Control>();
foreach (Control ctrl in container.Controls)
{
if (ctrl.GetType() == type)
controls.Add(ctrl);
controls.AddRange(GetControlsOfSpecificType(ctrl, type));
}
return controls;
}
I call the function like this:
var listOfControls = GetControlsOfSpecificType(this, typeof(NumericUpDown));
foreach (var numericUpDownControl in listOfControls)
{
numericUpDownControl.Leave += numericUpDown_GenericLeave;
}
When I run my app, however, I don't see the expected behavior that occurs when I manually hookup each control to the generic event handler. This code is currently in the constructor of my form and I've tried calling it before as well as after the call to InitializeComponent() but neither one seems to be working. I get no error of any kind, I just don't see the behavior that I was expecting. I have a breakpoint set inside the generic event handler but the debugger never breaks so it seems like the event handler isn't being hooked up correctly. Does anyone know why this might be or how I can troubleshoot it further? Thanks!
EDIT
I just realized that the call to:
var listOfControls = GetControlsOfSpecificType(this, typeof(NumericUpDown));
was happening before the call to InitializeComponent() so of course the list of controls being returned was empty. DOH! Thanks for all the replys. I apologize for wasting everyones time. :-(
You're passing this to your method, which is presumably a reference to your form. Your method will only catch the controls that are placed directly on your form. Any NumericUpDown controls that are not directly on the form (i.e. they're sitting on a panel or something) will be missed.
Why not create a user control that has a NumericUpDown control in it.
Then handle this is in the user control events.
This worked for me:
private decimal _previous = 0;
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
if (((NumericUpDown)sender).Text.Length > 0)
{
_previous = this.numericUpDown1.Value;
}
}
private void UserControl1_Leave(object sender, EventArgs e)
{
if (this.numericUpDown1.Text == "")
{
this.numericUpDown1.Value = _previous;
this.numericUpDown1.Text = System.Convert.ToString(_previous);
}
}
Just note that the Leave event is on the user control not on the updown control itself.
Question answered. See Edit above. Thanks to bsegraves for pointing me in the right direction.

Categories