This is a C# question. I have a user control A. A contains another user control B. B has an event called BEvent. I want to expose this event in A so anyone who uses control A can subscribe BEvent. How can I write code to implement this design? Thanks.
Inside your user control A you could expose the event of control B like this...
public event EventHandler EventA
{
add { _control.EventB += value; }
remove { _control.EventB -= value; }
}
You should look at the delegate which event B is using, and ensure that event A matches. In this example i just selected EventHandler because that is quite common when developing User Controls
public delegate void EventHandler(object sender, EventArgs e);
Related
I am writing several UserControls for other programmers here.
Some Exposed EventHandlers with same name as the base are accepted
(UserControl CodesCombo -> SelectedValueChanged)
some aren't
(UserControl TextBox -> TextChanged)
I'm writing a UserControl which contains a TextBox. I need to expose the TextChanged event to the potential consumers of this control.
I have a similar control based on a ComboBox, and in that I used
public event EventHandler SelectedValueChanged
{
add { cbMain.SelectedValueChanged += value; }
remove { cbMain.SelectedValueChanged -= value; }
}
to expose the "Change" event "SelectedValueChanged", and it works without without any problems.
However, when I try to use this technique in my TextBox based control in a similar way
public event EventHandler TextChanged
{
add { tbMain.TextChanged += value; }
remove { tbMain.TextChanged -= value; }
}
I get a warning message:
'MyTextBox.TextChanged' hides inherited member 'UserControl.TextChanged'. Use new keyword if hiding was intentional.
I'm not entirely certain what the message means, other than the obvious, but what I do know is that I don't -think- I want to hide anything. I have internal SelectedValueChanged and TextChanged functions (cbMain_SelectedValueChanged, tbMain_TextChanged) which do a few things I need, but I want to allow the consumer to get an Event call on text change as well, just like they do in the ComboBox based one.
Also, I get no "change" event at all in the available list of event in the test program.
I've gotten around this for now by exposing the event as "TextChange"
new public event EventHandler TextChange
{
add { tbMain.TextChanged += value; }
remove { tbMain.TextChanged -= value; }
}
which gives me an event in the list and seems to work well enough, but I'd prefer to have a general solution to this as we're making several more controls along these lines for our package and I don't think I can get away with having names that are just "off".
Any idea what this message is really telling me, and how I can get my event internally, and also still get the user theirs?
Thanks!
Update: Asked for more specific code:
namespace NCFLSToolbox
{
public partial class NCFLSCodesCombo : UserControl
{
//Listed in the Events for System.Windows.Forms.ComboBox
private void cbMain_SelectedValueChanged(object sender, EventArgs e)
{
ControlRequiredColoring();
}
//Exposed Event for user
public event EventHandler SelectedValueChanged
{
add { cbMain.SelectedValueChanged += value; }
remove { cbMain.SelectedValueChanged -= value; }
}
}
public partial class NCFLSTextbox : UserControl
{
//Listed in the Events for System.Windows.Forms.TextBox
private void tbMain_TextChanged(object sender, EventArgs e)
{
ControlRequiredColoring();
}
//Couldn't expose "TextChanged" by name...
////public event EventHandler TextChanged
//// {
//// add { tbMain.TextChanged += value; }
//// remove { tbMain.TextChanged -= value; }
//// }
//...so I exposed "TextChange" instead.
public event EventHandler TextChange
{
add { tbMain.TextChanged += value; }
remove { tbMain.TextChanged -= value; }
}
}
}
Any idea what this message is really telling me?
First off, understand what it means to hide. Forget about the fact that it's an event for a moment. If we have:
class B { public void M() { Console.WriteLine("B.M()"); } }
class D : B { public void M() { Console.WriteLine("D.M()"); } }
then we have two methods both called M. If you say:
D d = new D();
B b = d;
d.M(); // D.M();
b.M(); // B.M();
The compiler is warning you that you have two methods, not one, and which one you get depends on the compile-time type of the receiver, NOT the run-time type of the receiver.
Why is that probably not what you want?
Three reasons.
First, many C# programmers come to C# from Java, where methods are automatically overridden. In C# methods have to be virtual to be overridden. The compiler is telling you that you are not getting the semantics you might expect.
Second, this is to mitigate the brittle base class problem. Suppose the author of B gives the D team a DLL assembly containing B without M, and D derives from it, adding method D.M. Then the B team realizes that they could implement M, so they add it to B and give the D team a new assembly. Now the D team should be warned that B.M exists, so that they can decide whether to delete D.M or not.
Third, why are you doing this in the first place? If the base class already has an event that you want, just use it! Don't make a new one that uses the old one; just use the old one. The compiler is telling you that you're definitely doing something strange and probably wrong.
If it is your intention to have two members on two different classes where one hides the other, you tell the compiler "I have thought about this and this is intentional" by putting new on the member. That is "this is a new member, and I meant to make it a new member, not an override of an old member".
The problem is that TextBox events do not propagate to the parent user control. If you wish them to do so, there are two ways to accomplish this:
1) Have the event on the user control ferry all additionals/removals to the event handler to the UserControl's event handler. This is roughly what your question is trying to do. However, I don't recommend it.
2) Have the event on the TextBox trigger events on the parent user control.
So, just run this code in your constructor, below InitializeComponent():
tbMain.TextChanged += (sender,e)=>OnTextChanged(e);
With this approach, a TextChanged event in the TextBox will call OnTextChanged, which raises a TextChanged event. You cannot invoke base class events directly (hence why the OnTextChanged method is provided).
Edit: tbMain.TextChanged += (sender,e)=>OnTextChanged(e); is semantically equivalent to the below code:
tbMain.TextChanged += OnTbMainTextChanged;
...
} //End of Constructor
private void OnTbMainTextChanged(object sender, EventArgs e)
{
OnTextChanged(e);
}
The benefit of using a lambda function is that it is more self-contained. Among other benefits, using a self-contained lambda function makes it obvious to future code maintainers that the sender is not being propagated, without requiring the maintainer to navigate to the named method. This is what you want: from the perspective of subscribers to the user control, your use of a TextBox to implement your control is an implementation detail.
Using this code, i am adding an eventhandler to the RootFrame.Obscured.
(Application.Current as App).RootFrame.Obscured += onObScured;
Since the RootFrame can be accessed from every class in the App, what happens if i add different eventhandlers from different classes?
Example:
class A{
(Application.Current as App).RootFrame.Obscured += onObScuredA;
private void onObScuredA(object sender, ObscuredEventArgs e) {
//Some code here
}
}
class B{
(Application.Current as App).RootFrame.Obscured += onObScuredB;
private void onObScuredB(object sender, ObscuredEventArgs e) {
//Some other code here
}
}
When the event is triggered, will both onObScuredA() and onObScuredB() be triggered if there has been created an instance of both A and B?
Is the correct way, to add the eventhandlers, and their respective methods on the App.xaml.cs class so i can be certain which eventhandlers are added?
Thanks.
You can add as many event handlers onto an event as you want, they will all be invoked. That is the nature of events, when they fire, all event handlers handle this event.
So, the answer is "they both will be triggered". Now, this may or may not be what you want, but adding new event handler does not replace previous event handlers.
Read more on MSDN: Handling and Raising Events.
I am working on a UserControl for selecting special files, in this control there is a TreeView which gets populated with nodes when user selects some file. Also user can remove the files from this treeview!
I am using this control in a wizard form. In this wizard form there is a button named buttonNext and this button is disabled by default.
How can I create an event for the treeview in the usercontrol that when it gets populated it notify the next button in wizard form to get enabled and if user removes all files from that treeview it notify the button to get disabled again.
P.S: Selecting files (browser dialog and stuff like that) are all done within this usercontrol, so in my wizard form I have no access to the things that is going on in this component, but only I set the TreeView itself as public so I can read its nodes in my wizard form.
I know how to subscribe to events but never created any event myself :(
Declare events on your CustomControl:
public event EventHandler DataPopulated;
public event EventHandler DataRemoved;
Common practice is creating protected virtual methods (for possible overriding them in descendant classes), named On<EventName> which will verify that event has attached handlers and raise event, passing required arguments:
protected virtual void OnDataPopulated()
{
if (DataPopulated != null)
DataPopulated(this, EventArgs.Empty);
}
NOTE: If you need to pass some data to event handlers, then use generic EventHandler<DataPopulatedEventArgs> delegate as event type, where DataPopulatedEventArgs is a class, inherited from EventArgs.
Then just call this method just after your data was populated:
treeView.Nodes = GetNodes();
OnDataPopulated();
Then just subscribe to this event and enable your next button:
private void CustomControl_DataPopulated(object sender, EventArgs e)
{
buttonNext.Enabled = true;
}
Who is the one populating the TreeView? The one loading the data on it could enable the Next button when it has finished the loading. Am I missing something?
By the way, you create an event like this:
public event EventHandler<EventArgs> YouEventName;
And you call it like a method:
this.YourEventName(this,EventArgs.Emtpy);
Best practices say that you should create a method to call it like this:
protected virtual void OnYourEventName()
{
if (this.YourEventName != null)
{
this.YourEventName(this, EventArgs.Empty);
}
}
Check out this MSDN article for a complete tutorial on how to create and fire events.
http://msdn.microsoft.com/en-us/library/aa645739(v=vs.71).aspx
You can just propogate the event of the Treeview.
You can add this to your custom control, and it will have a SelectedNodeChanged event.
public event EventHandler SelectedNodeChanged
{
add { tree.SelectedNodeChanged += value; }
remove { tree.SelectedNodeChanged-= value; }
}
Creating a new event
public event EventHandler<EventArgs> myEvent;
You then invoke it from some method
this.myEvent(sender, e);
The actual event would look something like this:
protected void MyEvent(object sender, EventArgs e)
{
//Your code here
}
Your code can be like this:
public delegate void ChangedEventHandler(object sender, EventArgs e);
class TreeViewEx : TreeView
{
...
public event ChangedEventHandler Changed;
protected virtual void OnChanged(EventArgs e)
{
if (Changed != null)
Changed(this, e);
}
}
and it usage
TreeViewEx tree = ...
tree.Changed += new EventHandler(TreeChanged);
// This will be called whenever the tree changes:
private void TreeChanged(object sender, EventArgs e)
{
Console.WriteLine("This is called when the event fires.");
}
If I have an event like this:
public delegate void MyEventHandler(object sender, EventArgs e);
public event MyEventHandler MyEvent;
And adds an eventhandler like this:
MyEvent += MyEventHandlerMethod;
... is it then possible to register this somehow? In other words - is it possible to have something like:
MyEvent.OnSubscribe += MySubscriptionHandler;
Similar to auto-implemented properties, events are auto-implemented by default as well.
You can expand the declaration of an event as follows:
public event MyEventHandler MyEvent
{
add
{
...
}
remove
{
...
}
}
See, for example, How to: Use a Dictionary to Store Event Instances (C# Programming Guide)
See Events get a little overhaul in C# 4, Part I: Locks for how auto-implemented events differ between C# 3 and C# 4.
It is possible to declare the event accessors specifically, i.e., the add and remove accessors.
Doing so makes it possible to do custom logic when new event handlers are added.
When you define your events, you can actually use the longer format to execute more code when people attach or remove themselves from your events.
Check out the info on the add and remove keywords.
I guess, you are looking for event accessors. Way to customizing the references to the subscribers. Here is how you can do it
public class TestClass
{
private event EventHandler UnderlyingEvent;
public event EventHandler TestEvent
{
add
{
UnderlyingEvent += value;
}
remove
{
UnderlyingEvent -= value;
}
}
}
For more information, please visit this article
http://msdn.microsoft.com/en-us/magazine/cc163533.aspx
It's possible if you declare your custom event, like this pseudocode:
class MyClass
{
public event EventHandler MyEvent
{
add { //someone subscribed to this event ! }
remove { //someone unsubscribed from this event ! }
}
...
}
I have a UserControl, and I need to notify the parent page that a button in the UserControl was clicked. How do I raise an event in the UserControl and catch it on the Main page? I tried using static, and many suggested me to go for events.
Check out Event Bubbling -- http://msdn.microsoft.com/en-us/library/aa719644%28vs.71%29.aspx
Example:
User Control
public event EventHandler StatusUpdated;
private void FunctionThatRaisesEvent()
{
//Null check makes sure the main page is attached to the event
if (this.StatusUpdated != null)
this.StatusUpdated(this, new EventArgs());
}
Main Page/Form
public void MyApp()
{
//USERCONTROL = your control with the StatusUpdated event
this.USERCONTROL.StatusUpdated += new EventHandler(MyEventHandlerFunction_StatusUpdated);
}
public void MyEventHandlerFunction_StatusUpdated(object sender, EventArgs e)
{
//your code here
}
Just add an event in your control:
public event EventHandler SomethingHappened;
and raise it when you want to notify the parent:
if(SomethingHappened != null) SomethingHappened(this, new EventArgs);
If you need custom EventArgs try EventHandler<T> instead with T beeing a type derived from EventArgs.
Or if you are looking for a more decoupled solution you can use a messenger publisher / subscriber model such as MVVM Light Messenger here