This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Passing arguments to event handler
I'm trying to handle an event raised from a MenuItem.Click. The catch is, I need some way to pass some custom arguments with it (specifically, two integers representing row and column).
private void Window_Loaded(object sender, RoutedEventArgs e)
{
...
MenuItem froNewChild = new MenuItem();
froNewChild.Header = "Insert new box";
froNewChild.Click += new RoutedEventHandler(froNewChild_Click);
// froNewChild.Click += new RoutedEventHandler(froNewChild_Click, column, row);
FlowRectangleOptions.Items.Add(froNewChild);
...
}
private void froNewChild_Click(object sender, RoutedEventArgs e) //+column & row
{
FlowDocument.allColumns[column-1].AddFlowRectangle(column, row);
FlowDocument.ReRenderAll(canvas1);
}
This answer Passing arguments to an event handler would seem to do what I want, but doesn't work as-is because sender is already defined in this scope. Sadly I don't know enough yet to work around that problem - advice greatly appreciated!
Assumming "column" and "row" are created in your loaded method, you could just use a Lambda event handler so you can pass the variables in.
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
//Assumming "column" and "row" are in this block somewhere.
...
MenuItem froNewChild = new MenuItem();
froNewChild.Header = "Insert new box";
froNewChild.Click += (s, eArgs) =>
{
FlowDocument.allColumns[column - 1].AddFlowRectangle(column, row);
FlowDocument.ReRenderAll(canvas1);
};
FlowRectangleOptions.Items.Add(froNewChild);
...
}
The argument passed to the evant handler depends on how the raising class is designed, and they are passed as a class derived from EventArgs as a second argument. Augmenting this is not possible unless you are the designer of the class raising the event. In this particular case we are talking about a framework class and so it is not possible at all.
As a workaround you can define some state member variable in your class and use them to pass informations to the handler.
sa_ddam213 already answered you but just to clarify: the parameter names in lambda expressions carry no semantic meaning and can be whatever you want. The names sender and eventArgs were only used for clarity in the question you linked. What matters in this case is that there are two params and the compiler infers the rest by himself.
The values of column and row will be captured at the point you define the lambda and will be available when the event handler executes even if MainWindow_Loaded has long ago returned and the original variables destroyed.
For more about lambda expressions in C#: http://msdn.microsoft.com/en-us/library/bb397687.aspx
Related
I passed a variable from one page to another for a pop up menu. I want to be able to add the selected adviser to the favorites list. The issue is, when I pass the variable to one page, it only allows me to use it for that specific method. I am trying to get it passed to a Button Click event,(OkayButtonClicked), so it will be saved to the favorites list. I have tried two different attacks to this.
I have tried:
Declaring the button, using FindByName from the xaml code x:Name property.
Button okaybutton = FindByName("OkayButton") as Button;
Then I tried two different ways of using this variable to pass the string.
One way was:
okaybutton += (sender2, e2) => OkayButtonClicked(sender2, e2, selectedAdvisor);
The second way was:
okaybutton += delegate(object sender, EventArgs e)
{
OkayButtonClicked(sender, e, selectedAdvisor);
};
(selectedAdvisor is the variable that was passed from one page to the current one I am on, which is where the problem is occurring)
There is one error, each, that I get with these.
First One:
"Operator '+=' cannot be applied to operands of type "Button" and "lambda expression"
Second One:
"Operator '+=' cannot be applied to operands of type "Button" and "anonymous expression"
(The Button click event that I am trying to get the variable passed to)
private void OkayButtonClicked(object sender, EventArgs e)
{
PopupNavigation.Instance.PopAsync();
DisplayAlert("Attention", "You have successfully added Adviser to said List", "Okay");
}
you constructor for FavoriteButtonPopupView probably looks something like this
public FavoriteButtonPopupView(string selectedAdvisor)
{
...
}
the selectedAdvisor variable you pass is locally scoped - meaning it is only visible within the constructor, and other methods on that page can't access it.
What you need to do is create a class level variable that will be visible to all methods of the class.
private string SelectedAdvisor;
public FavoriteButtonPopupView(string selectedAdvisor)
{
// store the parameter in a class level variable so other methods can access it
SelectedAdvisor = selectedAdvisor;
...
}
private void OkayButtonClicked(object sender, EventArgs e)
{
// do something with SelectedAdvisor here
PopupNavigation.Instance.PopAsync();
DisplayAlert("Attention", "You have successfully added Adviser to said List", "Okay");
}
note - this is basic C# and has nothing specific to do with Xamarin
Instead of okayButton += you need to use okayButton.Click += event.
Your code should be like
okaybutton.Click += delegate(object sender, EventArgs e)
{
.....
};
You can only use += with an event or delegate in c#. You can't use += operator with a button instance
Hope this helps
This can seems like a simple question ... the crux is how to match the button delegate signature void, object, eventargs with my method or use an event delegate.
As an example, I have code for a button that changes color when it's clicked. However,
button1.Click += new EventHandler(KK.ChangeColor);
carries the EventArgs from the button to the ChangeColor(object sender, EventArgs e) method, but is meaningless to the rest of the code which use ColorEventArgs; and
button1.Click += delegate(object sender, EventArgs e){ KK.ChangeColor(sender); };
doesn't allow for later removal of the delegate later in the code.
So which is better? Adding unnecessary parameters to all my methods to match the button delegate or suffering from not being able to remove the delegate later ?
or How would I change the delegate signature of the button? It seems there must be a 'cleaner' way to do this?
Will appreciate advice.
"It seems there must be a 'cleaner' way to do this?"
In my opinion, better design would depends on what exactly ChangeColor method do. If it is doing only specific operation that closely related to event button clicked, I would just leave it as the real event handler method. That means, it should have required parameters to match Button.Click event handler signature (I don't think there is option to "change the delegate signature") :
void ChangeColor(object sender, EventArgs e)
{
MessageBox.Show("Button {0} clicked!!!", ((Button)sender).Name);
}
Otherwise, if it is doing not only specific operation related to event button clicked, I would refactor codes unrelated to button click event to another method. This way, the other method doesn't need to have unnecessary parameters. For example :
void ChangeColor(object sender, EventArgs e)
{
var buttonName = ((Button)sender).Name;
MessageBox.Show("Button {0} clicked!!! Save form data", );
//assume that form name can be determined from name of button being clicked
SaveFormToDatabase(buttonName);
}
private void SaveFormToDatabase(string formName)
{
//save form specified in parameter
}
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.
I'm slowly getting the hang of C# and this question is probably a result of bad design but here goes.
I have dynamic menus being generated thusly:
public Form1()
{
InitializeComponent();
AddContextMenu();
}
public void AddContextMenu()
{
ContextMenuStrip mnuContextMenu = new ContextMenuStrip();
mnuContextMenu.ItemClicked+=
new ToolStripItemClickedEventHandler(mnuContextMenu_ItemClicked);
this.ContextMenuStrip = mnuContextMenu;
ToolStripMenuItem mnuItemEnable = new ToolStripMenuItem("Enable");
mnuContextMenu.Items.Add(mnuItemEnable);
}
and the event handler:
private void mnuContextMenu_ItemClicked (Object sender,
ToolStripItemClickedEventArgs e)
{
//do stuff here
}
How do I change mnuContextMenu.Text (or any other property) from inside the event handler?
VS says :
mnuContextMenu does not exist in the
current context
There's a reason that all event handler methods have the exact same signature in the .NET world. You've probably noticed that the sender and e arguments are always there, no matter which event you're handling. They provide all the information you need.
In this particular case, you're looking for the sender parameter, which is a reference to the specific control that raised the event.
Of course, it's typed as an Object, so you'll have to cast it to a more derived type in order to use it like you want to. That's straight-forward enough—since you know that an ItemClicked event is only going to be raised by a ContextMenuStrip object, just cast it directly:
private void mnuContextMenu_ItemClicked (Object sender, ToolStripItemClickedEventArgs e)
{
((ContextMenuStrip)sender).Text = "Your text";
}
Or, if you want to play it safe (and you probably do), follow the standard idiom:
private void mnuContextMenu_ItemClicked (Object sender, ToolStripItemClickedEventArgs e)
{
// Try to cast the object to a ContextMenuStrip
ContextMenuStrip cmnu = sender as ContextMenuStrip;
// Verify that the cast was successful
// (if it failed, the cmnu variable will be null and this test will fail,
// preventing your code from being executed and your app from crashing)
if (cmnu != null)
{
cmnu.Text = "Your text";
}
}
There's absolutely no reason to litter your code with maintaining class-level references to these objects when there's a perfectly good, built-in way of obtaining references to exactly the ones that you want, when you want them.
mnuContextMenu only existed in the scope of AddContextMenu.
You have a couple of options:
this.ContextMenuStrip.Text = "Hello World";
or:
((ContextMenuStrip) sender).Text = "Hello World";
The first works because you stored the local mnuContextMenu in the class propery ContextMenuStrip. The second way casts the sender paramater (object raising the event) to a ContextMenuStrip.
Clearly it fails because you declare the context menu object inside the AddContextMenu method as local method variable instead of having it as private member of the containing class. the solution MegaHerz has suggested would probably work, or you keep a reference to your object as private member of the class.
This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
C#: Difference between ‘ += anEvent’ and ‘ += new EventHandler(anEvent)’
Hi all,
I have two eventhandlers.
In my constructor I attach a function to the eventhandler but do it in different ways for the two eventhandlers. One with new Eventhandler, the other by just pointing to the function. They seem to do the same?
What is the prefered way and why?
UPDATE: already answered here
public partial class MyForm : Form
{
public event EventHandler Button1Clicked;
public event EventHandler Button2Clicked;
public MyForm()
{
InitializeComponent();
simpleButton1.Click += new EventHandler(simpleButton1_Click);
simpleButton2.Click += Button2Click;
}
void simpleButton1_Click(object sender, EventArgs e)
{
if (Button1Clicked != null)
{
Button1Clicked.Invoke(sender, e);
}
}
void Button2Click(object sender, EventArgs e)
{
if (Button2Clicked != null)
{
Button2Clicked.Invoke(sender, e);
}
}
}
C# 2 introduced method group conversions which is what your second form is using. It does exactly the same thing internally - in both cases, it produces a new instance of EventHandler.
Personally I prefer the second form: it doesn't have as much cruft. It just says what you're interested in.
(I hadn't spotted the exact duplicate. Leaving this answer here despite closing the question, as it's doing no harm.)
yes they do the same.
Resharper recomemnds latter.
Both of the way of attaching event handler are same. In the last case framework (fro 2.0 onward) is able to infer type itself.