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);
}
Related
I have multiple custom controls, and I noticed that all of them share the same event (custom) example : OnMoved etc
What I'm doing now is, copy & paste the same code from controls to controls.
So, are there anyway for me to write custom events that can be shared throughout all my controls in C# WPF?
An example of an event that I use for all my controls :
Point lastPosition = new Point();
Point currentPosition = new Point();
public static void OnMoved(object sender, EventArgs e)
{
currentPosition.X = Canvas.GetLeft(explorer);
currentPosition.Y = Canvas.GetTop(explorer);
// didn't moved
if (currentPosition.X == lastPosition.X || currentPosition.Y == lastPosition.Y)
{
return;
}
lastPosition.X = Canvas.GetLeft(explorer);
lastPosition.Y = Canvas.GetTop(explorer);
}
It depends on what exactly you need the event to do, but you could place the event into a shared class:
public class MyEvents
{
public static void SomeEvent(object sender, EventArgs e)
{
MessageBox.Show("hi");
}
}
And then just subscribe to it from wherever you need to:
SomeButton.Click += MyEvents.SomeEvent;
You can create a base class that has a public virtual event, and the event will appear in any classes that derive from the base class. That would keep you from having to copy and paste the same code over and over.
Yes you can! :D The only things you need to have present are:
-> Same Events (The Args of the event have to be exactly the same.
-> They are going to do the same.
The bad thing is that you cannot mix controls with events. For example, you can create a .Click event for a button so it closes your application, but if you wish this to do the same when you press the key "F8" it won't work because the Event arguments are different ~
You can try using a method that makes the same stuff in all your events. Example:
private void _Close()
{
Process.GetCurrentProcess().Close();
}
And you can close with "F5" pressed in the form or with a button click or typyng in a textbox "Close".
button.Click += Button_Close;
private void Button_Close(Object o, RoutedEventArgs e)
{
_Close();
}
this.KeyDown += This_Close;
private void This_Close(Object o, KeyEventArgs e)
{
if(e.KeyCode == Key.F5) _Close();
}
TextBox.TextChanged += Text_Close;
private void Text_Close(Object o, TextChangedEventArgs e)
{
if(TextBox.Text == "Close") _Close();
}
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.
This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
How can I invoke an event handler from a function?
I need to do something I was pretty sure should be simple, I have Form with controls and every control have event handler that sets label with correct formating etc.
What I need is to call every handlers collection for every control on specific type.
I browsed all stackoverflow and never saw result.
It is simple application so please stop comments like: "you need additional function called from event handler".
This is what I tried
foreach (Control ctrl in Controls)
{
if (ctrl is TrackBar)
{
TrackBar tb = ctrl as TrackBar;
Invoke(tb.Scroll, tb, new EventArgs());
}
}
But I see very strange error
The event 'System.Windows.Forms.TrackBar.Scroll' can only appear on the left hand side of += or -=
Any solution?
You can't raise event outside class in which event is declared (only adding and removing handlers is available). Instead of trying to raise event, which will call event handler, simply extract handler logic to separate method, and call that method:
foreach (TrackBar tb in Controls.OfType<TrackBar>())
{
DoSomething(tb.Value);
}
And handler:
void trackBar_Scroll(object sender, EventArgs e)
{
DoSomething(((TrackBar)sender).Value);
}
Error appears because tb.Scroll is event not delegate.
You could try making a delegate with the same Signature as the Event, create a common eventhandler for your controls and then you can either invoke it or respond to the event as normal.
i.e.
Something like this:
public partial class Form1 : Form
{
public delegate void trackbarscroll( object sender, EventArgs e);
trackbarscroll tbs;
public Form1()
{
InitializeComponent();
trackBar1.Scroll += new EventHandler(trackBar_Scroll);
trackBar2.Scroll += new EventHandler(trackBar_Scroll);
tbs = trackBar_Scroll;
}
void trackBar_Scroll(object sender, EventArgs e)
{
TrackBar tb = (TrackBar)sender;
}
private void button1_Click(object sender, EventArgs e)
{
foreach (Control ctrl in Controls)
{
if (ctrl is TrackBar)
{
TrackBar tb = ctrl as TrackBar;
Invoke(tbs,tb, new EventArgs());
}
}
}
}
I have a Button defined in Xaml like
<Button Name="button1" />
I'm trying to subscribe to the Click event of this Button using Reflection. More precisely I want to invoke an Action<object, EventArgs> and inside the event handler I want to access a local variable from outside of the scope like this
string testString = "Hello";
Action<object, EventArgs> clickAction = (object sender, EventArgs e) =>
{
MessageBox.Show(testString);
};
If I do this, Hello is displayed
clickAction.Invoke(null, null);
Similar, if I just subscribe to the Click event like this, Hello is displayed
button1.Click += (sender, e) =>
{
MessageBox.Show(testString);
};
But if I use reflection to subscribe to the event with AddEventHandler, I'm unable to access testString and I get a NullReferenceException in the event handler.
Type buttonType = button1.GetType();
EventInfo clickEvent = buttonType.GetEvent("Click");
Delegate clickEventHandler = Delegate.CreateDelegate(clickEvent.EventHandlerType,
null,
clickAction.Method);
clickEvent.AddEventHandler(button1, clickEventHandler);
Why does this happend?
Is there a fix I can apply to make it work the way I want?
Do I have another option?
Update - This is what the Method looks like in full
private void MyMethod()
{
string testString = "Hello";
Action<object, EventArgs> clickAction = (object sender, EventArgs e) =>
{
MessageBox.Show(testString);
};
Type buttonType = button1.GetType();
EventInfo clickEvent = buttonType.GetEvent("Click");
Delegate clickEventHandler = Delegate.CreateDelegate(clickEvent.EventHandlerType,
null,
clickAction.Method);
clickEvent.AddEventHandler(button1, clickEventHandler);
}
EDIT - rewriting my answer after the comments:
you can make this work if you bind the delegate to an instance. that instance should be a class containing all information/variable you want to use from within your event handler...
you create that instance, initialize it with the values you want and then bind it:
Delegate clickEventHandler = Delegate.CreateDelegate(clickEvent.EventHandlerType, yourinstance, clickAction.Method);
this way the delegate has a "this" which can be used to access the variables...
you could even make those variables instead of local just plain class fields and bind the delegate to this.
another option would be to put thos variables into the "sender object" and access them that way...
I've built a custom component that basically has a picture box and label in it. In the parent form, I want to be able to detect when its been clicked on. The standard .click event doesn't seem to be working, but I've never used events before so am unsure if I'm using them correctly. Heres the code I'm using (in the parent) to try and make it recognise the click:
Item aItem = new Item();
aItem.Icon = ItemImage;
aItem.Title = Title;
aItem.Click += new EventHandler(ItemClicked);
aItem.Filename = File;
and heres the method its calling:
public void ItemClicked(Object sender, EventArgs e)
{
MessageBox.Show("Item Clicked!");
}
This code never fires. Do I need to put anything into the component or am I just doing this wrong?
Cheers
Right I finally worked it out. Tejs response just confused me more so here's what I did.
In my UserControl I had the following event:
public event EventHandler Clicked;
Then I had an event for when the image was clicked (still in the UserControl) and I just called the Clicked event:
private void imgItem_Click(object sender, EventArgs e)
{
Clicked(this, e);
}
Then in my parent form, when I created the object, I had the following:
Item aItem = new Item();
aItem.Clicked += new EventHandler(ItemClicked);
void ItemClicked(object sender, EventArgs e)
{
MessageBox.Show("Clicked!");
}
You would do this by exposing an event':
Your custom component:
// A custom delegate like MyItemClickedHandler, or you could make a Func<> or Action<>
public event MyItemClickedHandler ItemClickedEvent;
public void ItemClicked(object sender, EventArgs e)
{
if(ItemClickedEvent != null)
ItemClickedEvent(); // Your delegate could pass parameters if needed
}
Then your parent form simply observes the event:
myCustomControl.ItemClickedEvent += new MyItemClickedHandler(SomeMethod);
Then, whenever the event is raised on your custom control, the parent is notified because it subscribed the event.