I have a control (button) with a messagebox containing the text "Test A" and added a delegate with another message "Test B". The code below first shows the messge "Test A`" and then "Test B":
c.Click += delegate(object sender, EventArgs e)
{
MessageBox.Show("Test B");
};
I need something similar to this code I did that is not working properly:
EventHandler handler;
c.click -= handler;
handler = delegate(Object sender, EventArgs e)
{
MessageBox.Show("Test B");
};
c.Click += handler;
I need to change the sequence, that is, show the message "Test B", then "Test A". Is it possible?
You can unregister and register it again:
button1.Click -= button1_Click; // unregister main event
button1.Click += delegate(object sender, EventArgs e) // reigster new one
{
MessageBox.Show("Test B");
};
button1.Click += button1_Click; // register again old
Button click Event:
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("Test A");
}
Now it will call Call Test B first and secondly Test A
You mentioned that your program contains a compiler of some sort, and that the user is able to specify whether their code should run before existing code or after. It seems to me that you can simply handle the users code in an event handler of your own, which runs both your and the users code, in the desired order (as Nikola already mentioned).
In other words, don't expose these events to your users code directly, but 'wrap' them with an event handler of your own. Nothing changes from the users point of view - they still think they've got direct access to the event. But you control how you process your users code, so you're free to insert a layer in-between to guarantee a correct execution order:
c.Click += (sender, e) =>
{
if (hasUserEventHandler && runUserEventHandlerFirst)
UserEventHandler(sender, e);
StandardEventHandler(sender, e);
if (hasUserEventHandler && !runUserEventHandlerFirst)
UserEventHandler(sender, e);
};
I mean, you could do something like this:
public void SwapDelegateOrder(object obj, string eventName)
{
// Fetch the underlying event handler field (let's *assume* it has the same name):
var delegateField = obj.GetType().GetField(eventName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var mcDelegate = delegateField.GetValue(obj) as MulticastDelegate;
if (mcDelegate == null)
return;
// Get the add and remove methods:
var evt = obj.GetType().GetEvent(eventName);
var add = evt.GetAddMethod(true);
var remove = evt.GetRemoveMethod(true);
// Remove all invocations...
var invocations = mcDelegate.GetInvocationList();
foreach (var invocation in invocations)
remove.Invoke(obj, new object[] { invocation });
// ...and add them back in, in reverse order (*assuming* that invocations are called in the order they're added):
foreach (var invocation in invocations.Reverse())
add.Invoke(obj, new object[] { invocation });
}
But that's based on assumptions about compiler and runtime implementation (event field name and execution order). What if some of these details are changed in a compiler update or in the next .NET framework version? You don't have control over that, but you do have control over your own code.
Related
Basically what I am trying to do is, creating a new method that will have an event handler button click event condition within it.enter code here
//when this event is active the only should then should run what inside the {}
if(buttonEvents_Click(object sender, EventArgs e))
{enter code here`
//Happening something
}
what I already tried withing(buttonEvents.click) get the error "the event control.click can only appear on the left side =+ or -+".
You should have your event handler method like you always do:
buttonEvents_Click(object sender, EventArgs e)
{
if(condition){ //you need your condition checker inside the method
//... coder here
}
}
now for any control that you want to add this handler you can sompliy do:
myButton.Click += buttonEvents_Click;
Alternatively you can do this like:
myButton.Click += (s, e) =>
{
//which s is the sender and e is the EventArgs
//code here
};
This works in adding an event handler in C# WPF
CheckBox ifPrint = new CheckBox();
ifPrint.AddHandler(CheckBox.ClickEvent, new RoutedEventHandler(
(sender, e) => //toggle check box event
{
//do stuff
}));
but it looks messy when the method body gets long, so I want to define the method elsewhere like this
ifPrint.AddHandler(CheckBox.ClickEvent, delegate(object sender, RoutedEventArgs e){
checkBoxClick(sender, e);
});
private void checkBoxClick(object sender, RoutedEventArgs e)
{
//do stuff
}
but this doesn't even compile with the error: Cannot convert anonymous type to type 'System.Delegate' because it is not a delegate type
Sorry, I am new to this and have no idea how it's supposed to be done. Is this even close? Thanks!
You can subscribe to a separate method like this, as long as the signature of checkBoxClick is correct:
ifPrint.Click += checkBoxClick;
You can also subscribe to an event inline like this:
ifPrint.Click += (s, e) => SomeMethod();
Which then allows you to name your method something more reasonable and not require it to accept parameters:
private void SomeMethod()
{
//do stuff
}
Just to explain it a little further, in the above code, s and e take the place of the parameters in your checkBoxClick event method, so it's basically equivalent to this:
ifPrint.Click += checkBoxClick;
private void checkBoxClick(object sender, RoutedEventArgs e)
{
SomeMethod();
}
Edit, in regards to your comment.
Given this is much simpler, when, if ever, should one use this? ifPrint.AddHandler(CheckBox.ClickEvent, new RoutedEventHandler( (sender, e) => { //do stuff }));
I honestly don't think I've ever used that syntax.
It seems that in most cases it does the same thing. According to the MSDN docs, there's a handledEventsToo parameter on the AddHandler() method, which I think could be significant.
Imagine you subscribed to an event multiple times, like this:
ifPrint.Click += checkBoxClick;
ifPrint.Click += checkBoxClick;
ifPrint.Click += checkBoxClick;
And inside your event, you set e.Handled = true. If you didn't have that line, you'd see the message box displayed 3 times. But with that line, you only get the message box once, because the first time the event fires, it marks the event "handled".
private void checkBoxClick(object sender, RoutedEventArgs e)
{
MessageBox.Show("Clicked!");
e.Handled = true;
}
By passing in true for the last parameter (it's false by default), you actually tell it to fire that event, even if other events already "handled" the event.
ifPrint.AddHandler(CheckBox.ClickEvent,
new RoutedEventHandler((s, e) => { /* do stuff */ }), true);
try this logic to attach click event handler for your checkbox.
CheckBox ifPrint = new CheckBox();
ifPrint.Click+=checkBoxClick;
private void checkBoxClick(object sender, RoutedEventArgs e)
{
//do stuff
}
I am trying to create an extension method Clone() for a RichTextBox (RTB).
I want to set the TextChanged event handler of the new RTB to the TextChanged event handler of the old RTB. For example:
newRTB.TextChanged += oldRTB.TextChanged;
However, the following error is given:
"The event 'System.Windows.Forms.Control.TextChanged' can only appear on the left hand side of += or -=."
One possible solution is to add the event handler as a parameter to the clone method and just recreate the event, but I need to do this for multiple events and that would get cumbersome. Any ideas?
The "=" sign also does not seem to work.
We could copy the events via reflection. Now i myself would be wary of doing this, so please test exhaustively and with all versions (2.0, 3.0, 4.0). I tried many ways but the following was the only way, i got it to work. A Smoke test was run on .NET 4.0.
Create an extension method on the Form Class
public static class FormExtension
{
public static void CopyEvent(this Form form, Control src, string fieldName, string eventName, Control dest)
{
EventHandlerList events = (EventHandlerList)typeof(Control)
.GetProperty("Events", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(src, null);
object key = typeof(Control).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
EventInfo evInfo = typeof(Control).GetEvent(eventName, BindingFlags.Public | BindingFlags.Instance);
Delegate del = events[key];
if (del != null)
{
Delegate d = Delegate.CreateDelegate(evInfo.EventHandlerType, form, del.Method);
MethodInfo addHandler = evInfo.GetAddMethod();
Object[] addHandlerArgs = { d };
addHandler.Invoke(dest, addHandlerArgs);
}
}
}
Now use it like this
Here i show an example of copying the click and the text changed event.
this.CopyEvent(richTextBox1, "EventText", "TextChanged", richTextBox2);
this.CopyEvent(richTextBox1, "EventClick", "Click", richTextBox2);
How to use it for other events
You would have to open the Control class via Reflector and get the field and the eventnames.
So in the case of Text Changed it was something like:
public event EventHandler TextChanged <-----The Event name for the "CopyEvent" function
{
add
{
base.Events.AddHandler(EventText, value);
}
remove
{
base.Events.RemoveHandler(EventText, value);
}
}
where EventText is
private static readonly object EventText = new object(); <-------The Field name
Try this:
newRTB.TextChanged += new TextChangedEventHandler(oldRTB_textChanged);
Create a method like this:
void oldRTB_textChanged(object sender, EventArgs e)
{
// do something
}
If you precreate the TextChanged event, you can do this, in a form put two TextBox and a Button, this will make your controls share the event.
private void button1_Click(object sender, EventArgs e)
{
textBox2.TextChanged += new EventHandler(textbox1_TextChanged);
}
private void textbox1_TextChanged(object sender, EventArgs e)
{
MessageBox.Show("this");
}
UPDATE
in a different file, create the method you want, you can take the code of the current event and copy in this method, then you just assign it to the new controls
void MyCloned_TextChanged(object sender, EventArgs e)
{
// Shared method OldRTB to share with NewRTB
}
In the form you will use it like this
public Form1()
{
InitializeComponent();
textBox1.TextChanged += new EventHandler(MyCloned_TextChanged);
textBox2.TextChanged += new EventHandler(MyCloned_TextChanged);
}
I have been tinkering with Events to gain a better understanding of their use in very general situations. I'm surprised to find the following, so I'm probably heading in the wrong direction...the essence of what I'm doing is changing a button to a random color when it is clicked:
Windows Form
public Form1()
{
ColorChanges KK = new ColorChanges();
KK.ColorEventHandler += handle_ColorChanges;
button1.Click += delegate { KK.ChangeColor(button1); };
}
Event Class
class ColorChanges
{
*... properties & constructor*
public void ChangeColor(object sender)
{
*... randomly assign color to ColorEventArgs*
}
protected virtual void onColorEvent(object sender, ColorEventArgs e)
{
EventHandler<ColorEventArgs> ceh = ColorEventHandler;
{
if (ceh != null)
{
ceh(sender, e)
}
}
}
public event EventHandler<ColorEventArgs> ColorEventHandler;
}
Custom Event Args
public class ColorEventArgs : EventArgs
{
public Color xColor { get; set; }
}
Event Handler
public void handle_ColorChanges(object sender, ColorEventArgs e)
{
if (sender is Button)
{
var ButtonSender = (Button)sender;
ButtonSender.BackColor = e.xColor;
}
}
So the edited questions are:
Is use of the EventHandler(TEventArgs) Delegate useful? MS documentation indicates that syntax like
button1.Click += new EventHandler<AutoRndColorEventArgs>(handle_ColorChanges);
is correct, but that will not reach my code to randomly select a color and an error
"No overload for 'handle_ColorChanges' matches delegate >'System.EventHandler' "
so something like
button1.Click += new EventHandler<AutoRndColorEventArgs>(KK.ChangeColor(button1));
or
button1.Click += new EventHandler(KK.ChangeColor(button1));
Error says that a method is required and if I use
"No overload for 'handle_ColorChanges' matches delegate
'System.EventHandler'"
Lambda expressions help thanks for the supporting answers
button1.Click += (sender,args) => KK.ChangeColor(s);
But that doesn't allow un-assignment and that will be required later...
An anonymous delegate has the same problem
button1.Click += delegate(object sender, EventArgs e)
{ KK.ChangeColor(sender); };
The crux of the problem is that my color methods or their delegates do not match the button delegate signature (object, event). I don't care about the button args and want to use my own HOW?
Is the use of the delegate correct?
Yep, what you are doing is assigning an anonymous delegate as your event handler. This is perfectly valid, however, the catch here is you can't unassign the event handler because you have no reference to it. You could keep a reference to it and do it that way (if required)
var clickHandler = delegate { ... };
button1.Click += clickHandler;
...
button1.Click -= clickHandler
If you need access to the parameters of the event handler you will need to add those into the signature e.g.
button1.Click += delegate (object sender, EventArgs args) { ... }
The new EventHandler(SomeHandlerMethod) construct is the long way of doing things, it's synonymous to += SomeHandlerMethod. Your code currently doesn't work because you are trying to actually call the handler inside the constructor when the constructor expects a reference to the method
+= new EventHandler<ColorEventArgs>(KK.ChangeColor);
Is there a better structure for this?
Yeah, you can do it using even less code
button1.Click += (s, args) => KK.ChangeColor(button1);
This is incorrect:
button1.Click += new EventHandler<AutoRndColorEventArgs>(KK.ChangeColor(button1));
Instead of KK.ChangeColor(button1), you just need to specify the event handler method name as you did in here:
KK.ColorEventHandler += handle_ColorChanges;
The event handler method signature should match with the EventHandler delegate.If you want to just call a method in event handler, you can use lambda statement like this:
button1.Click += (s,e) => KK.ChangeColor(s);
Or:
button1.Click += delegate(object s, EventArgs e) { KK.ChangeColor(s); };
By doing this you are creating an anonymous method and attach it to your Click event.
I have a silverlight mvvm application that loads main view with 2 user controls loaded into 2 ContentControls, one with listbox showing items and other with edit button. When i click edit button, 2 new user controls load into the ContentControls, one showing data to edit (EditData) and other having Save and Cancel button (EditAction).
When i click save button, it raises an event that is defined in seperate GlobalEvents.cs class like:
public event EventHandler OnSaveButtonClicked;
public void RaiseSaveButtonClicked()
{
this.OnSaveButtonClicked(this, EventArgs.Empty);
}
and i subscribe to it in the other user control EditData, because i need to transfer that edited data via custom EventArgs, so i have put in the constructor of it's ViewModel:
this.globalEvents.OnSaveButtonClicked += (s, e) => SaveData();
and in the Save data:
public void SaveData()
{
globalEvents.RaiseSaveData(EditedGuy);
}
which raises another event that loads previous user controls into their ControlContent and shows edited data in list box. Thats all fine, but whenever i click on edit and then save again, it raises the event twice, and again 3 times, then 4 and so on. How can i make it to be raised only ONE time? I thought it could be because every time i click edit, a new instance of the user control is loaded and i dont know, maybe the subscription to the event stays, so i have tried to paste
this.globalEvents.OnSaveButtonClicked -= (s, e) => SaveData();
to the Dispose() method but without success. How can i make this work?
You can't use lambdas when you want to unregister from events.
this.globalEvents.OnSaveButtonClicked += (s, e) => SaveData();
This will create one instance - let's call it instance A - of type EventHandler and add it as a handler.
this.globalEvents.OnSaveButtonClicked -= (s, e) => SaveData();
This will not remove instance A from the event but create a new instance - instance B - and tries to remove it from the event.
To fix this problem, either create a little method or save that anonymous method in a field:
class ViewModel
{
private EventHandler _saveButtonClickedHandler;
// ...
public ViewModel()
{
_saveButtonClickedHandler = (s, e) => SaveData();
this.globalEvents.OnSaveButtonClicked += _saveButtonClickedHandler;
// ...
}
public void Dispose()
{
this.globalEvents.OnSaveButtonClicked -= _saveButtonClickedHandler;
// ...
}
// ...
}
this.globalEvents.OnSaveButtonClicked += (s, e) => SaveData();
This line is being called multiple times so you are adding a new event handler every time.
You need to either move that line to somewhere where it's only called once or change the event handler to:
this.globalEvents.OnSaveButtonClicked += SaveData;
public void SaveData(object sender, EventArgs e)
{
globalEvents.RaiseSaveData(EditedGuy);
this.globalEvents.OnSaveButtonClicked -= SaveData();
}
So you remove the event handler after dealing with it. This assumes that the handler will be added back next time you go into edit mode.
You could define a private eventhandler delegate variable in your class and assign it in your constructor:
private SaveButtonClickedHandler _handler;
Assign the handler in your constructor:
_handler = (s,e) => SaveData();
this.globalEvents.OnSaveButtonClicked += _handler;
Dispose:
this.globalEvents.OnSaveButtonClicked -= _handler;
"SaveButtonClickedHandler" is pseudo-code/placeholder for whatever the name of the delegate should be.
Hasanain
You'll have to put in a proper event handler method that calls SaveData() and register/unregister that. Otherwise you try to unregister another "new" anonymous method instead of the original one you've registered, which you, because it is anonymous, cannot actually access anymore.
public void SaveButtonClicked(object sender, EventArgs e)
{
SaveData();
}
this.globalEvents.OnSaveButtonClicked += SaveButtonClicked;
this.globalEvents.OnSaveButtonClicked -= SaveButtonClicked;