I'm working on an form custom control.the control is a MonthCalendar like Visual studio(C#) MonthCalendar control and I want to define an event for my control.
How can define a new event for this form custom control?
If your event should not provide any additional info (Foo is name of your event):
public event EventHandler Foo;
And raise it this way:
protected virtual void OnFoo()
{
if (Foo != null)
Foo(this, EventArgs.Empty);
}
If you need to pass some additional info to event handlers, then create custom arguments class by inheriting from EvenArgs class
public class FooEventArgs : EventArgs
{
public string Message { get; private set; }
public FooEventArgs(string message)
{
Message = message;
}
}
Declare event this way:
public event EventHandler<FooEventArgs> Foo;
And raise it this way:
protected virtual void OnFoo(string message)
{
if (Foo != null)
Foo(this, new FooEventArgs(message));
}
Its good practice to create protected methods for raising events by descendants of class where event is declared. Also good practice to use event naming convention:
add suffix -ing to event name for events which raised before
something happened (often you could cancel such events) (e.g. Validating)
add suffix -ed to event name for events which raised after something happened (e.g. Clicked)
As Thorsten stated, good practice to create virtual methods for raising events. Its not only allows to raise events from descendants, but also disable event raising, or adding some behavior prior/after event raising.
public event EventHandler<EventArgs> YourEvent;
Related
In my class I registered an Event from an external DLL, that will be raised when there are changes on variables from the external code.
public class Model
{
....
public void Connect
{
....
client.OnNotification += (s, e) =>
{
this.OnNotification(s,e);
}
}
}
And I have a ViewModel in which I want get notified when this event is raised in class Model.
public class ViewModel
{
...
// call method when Event in class Model is raised
public void DoSomething()
{
}
}
Any Ideas for a clean and easy way to do that?
Thank you.
Solution 1:
Pass in the client to the viewmodel's constructor and let the viewmodel subscribe to OnNotification() itself (pass in an interface if available)
Solution 2:
Make also the model implement INotifyPropertyChanged if you're using MVVM; pass in the interface into the viewmodel's constructor and subscribe to PropertyChanged.
If you're not using MVVM, you can use the same methodology by adding a custom ClientNotification event to the model, pass in the entire model into the viewmodels constructor, and subscribe to the event.
Solution 3:
Use a messaging system (aka message bus) such as Prism's Event Aggregator class or MVVM Light's Messenger class, or write your own.
EDIT: Here's an example using MVVM Light: (note: coding from memory, not tested)
Add a using reference to GalaSoft.MvvmLight.Messaging;
Create a small message class containing the properties you need. You can inherit from MVVM Light's MessageBase class if you want but its not necessary.
public class ClientNotificationMessage : MessageBase
{
public string SomeProperty { get; set;}
public int AnotherProperty { get; set;}
}
In you model's event handler, you send a message by:
client.OnNotification += (s, e) =>
{
var msg = new ClientNotificationMessage() { ... };
Messenger.Default.Send<ClientNotificationMessage>(msg);
}
In the viewmodel constructor, register to receive messages by:
Messenger.Default.Register<ClientNotificationMessage>(this, msg =>
{
// handle incoming ClientNotificationMessage
// if (msg.SomeProperty != ) ...
});
I'm sure there are other additional solutions that other ppl can add.
The solution is basic OOP design and it is not related to MVVM.
In C# you just don't pass events. You subscribe to events. Whenever something interesting happens in Model, fire en event. You can than subscribe to that event in ViewModel for example.
public class Model
{
public event EventHandler SomethingHappened; // e.g. you notification
}
public class ViewModel
{
public ViewModel(Model model)
{
model.SomethingHappend += SomethingHappend;
}
void Model_SomethingHappend(object sender, EventArgs e)
{
DoSomething();
}
void CleanUp()
{
/*
In order to prevent memoryleak:
If you subscribe to event of an object you have not created in this class
(Model.SomethingHappend in this case), you should also unsubscribe.
Otherwise model instance will keep reference to ViewModel instance.
*/
model.SomethingHappend -= SomethingHappend;
}
}
In your case, the event could be named NotificationRecieved instead of SomethingHappend
You should subscribe to the event directly in the object you want to "react" to the event.
The viewModel in this case. Define an EventHandler there and subscribe
Here is an example...
public class CodeGenerator
{
public delegate void GeneratorCalculatorEventHandler(decimal Fond);
public event GeneratorCalculatorEventHandler eventName;
public CodeGenerator()
{
eventName?.Invoke(0);
}
}
How could I catch the event? If I do this:
CodeGenerator CodeGen = new CodeGenerator();
CodeGen.eventName += CodeGen_eventName;
The event is fired when the handler is not subscribed to it yet. Is it possible to subscribe to an event on initialiazation?
As the comments said, this is a bad code smell. It sounds like you're doing something very unusual here and you should consider trying to find a different way to solve your problem.
One of the reasons it smells bad is: if the calling code knows to pass the handler to the constructor, then that code already knows that the constructor is being invoked. The caller could simply invoke the handler itself with the constructed instance! Events are typically for situations where something happens that the handler could not predict or control, like the user clicking a button.
I would not pass the handler to the constructor, were I in your situation. I would use a static event.
You should pass the handler to the constructor and attach it to the event, something like:
public class CodeGenerator
{
public delegate void GeneratorCalculatorEventHandler(decimal Fond);
public event GeneratorCalculatorEventHandler eventName;
public CodeGenerator(GeneratorCalculatorEventHandler listener)
{
eventName += listener;
eventName?.Invoke(0);
}
}
public class Test
{
public Test()
{
CodeGenerator gen = new CodeGenerator((sen) => { return; });
}
}
In my project, I need to implement an event that fires when a popup or something similar is pulled up so that I can close anything that needs to hide behind it for whatever reason.
For context, I have 3 files in play here, MainShell which fires the event, IShell which is an interface that MainShell implements and defines the event, and Reports which listens for the event. I could have put the event in MainShell and made everything simpler, however the project references would become circular if I did that. That's just what I have to work with. I can, however, refer to the IShell interface that defines the functions MainShell uses. Unfortunately, it seems attempting to use an event from a derived class/interface causes the implementation to become very complicated and picky for some reason.
In my interface file:
public class ModuleShownEventArgs : EventArgs { }
public delegate void ModuleShownEventHandler(object sender, ModuleShownEventArgs e);
public interface IShell {
event ModuleShownEventHandler ModuleShown;
... }
In my listening class:
public Reports() {
...
Container.Shell.ModuleShown += Shell_ModuleShown;
... }
private void Shell_ModuleShown(object sender, ModuleShownEventArgs e) {}
In my event firing class:
event ModuleShownEventHandler IShell.ModuleShown
{
add
{
((IShell)this).ModuleShown += value;
}
remove
{
((IShell)this).ModuleShown -= value;
}
}
public void OnModuleShown()
{
ModuleShownEventHandler handler = ((IShell)this).ModuleShown;
if (handler != null)
handler(this, new ModuleShownEventArgs());
}
I've managed to stop most of the compiler's complaints, but I'm down to one problem: there's an error where I assign handler = ModuleShown,
the event 'IShell.ModuleShown' can only appear on the left hand side
of += or -=
This prevents me from easily comparing my event to null for checking, and prevents me from firing my event at all.
Questions
How can I get this to work? Why can't I fire my event? Why does defining events change so drastically when they come from a base class/interface? Keep in mind that this project is quite large and I've only started working on it recently, so I can't make sweeping structural changes to it.
I am using Visual Studio 2013, and my project's .NET Framework version is 4.0.
The issue is that you are using explicit interface implementation. You should be using implicit instead.
public class Shell : IShell
{
public event ModuleShownEventHandler ModuleShown;
public void OnModuleShown()
{
ModuleShownEventHandler handler = ModuleShown;
if (handler != null)
{
handler(this, new ModuleShownEventArgs());
}
}
}
https://msdn.microsoft.com/en-us/library/ms173157.aspx
Ideally you will be passing around the instance that implements IShell as the interface if your concern was to hide the event from the Shell implementation. Typically, you use explicit interface implementations when you do not want your class to publicly expose an interface specific member.
I hope this helps.
It should look something like this instead.
public void OnModuleShown()
{
if (((IShell)this).ModuleShown != null);
(((IShell)this).ModuleShown)(this, new ModuleShownEventArgs());
}
I have a User Control containing a bunch of controls. I want to set the default Event of this User Control to the Click event of one of my buttons.
I know for setting default event to one of the UserControl's events I should add the attribute:
[DefaultEvent("Click")]
public partial class ucPersonSearch : UserControl
...
I'm wondering if it's possible to do something like:
[DefaultEvent("btn1_Click")]
public partial class ucPersonSearch : UserControl
...
I want to fire some methods in the form hosting this User Control at the time btn1 is clikced.
This is really a knit in my project, and you're answer will be valueable.
You can't expose events of your class members to the outside of the class. How can others subscribe to the Click event of a Button inside your UserControl? Did you try it? It's not possible unless you make the button accessible from the outside, which is not good (everybody can change all the properties).
You have to define a new event, and fire your new event when your desired event (clicking on the button) happens:
[DefaultEvent("MyClick")]
public partial class UCPersonSearch : UserControl
{
Button btnSearch;
public event EventHandler MyClick;
public UCPersonSearch()
{
btnSearch = new Button();
//...
btnSearch.Click += new EventHandler(btnSearch_Click);
}
void btnSearch_Click(object sender, EventArgs e)
{
OnMyClick();
}
protected virtual void OnMyClick()
{
var h = MyClick;
if (h != null)
h(this, EventArgs.Empty);
}
}
I have a base-class (let it be SomeBaseClass) containing a public event (SomeEvent) and I have a derived-class in which I want to raise this event but I can't(!!) VS 2010 says me (in derived-class in line: base.SomeEvent != null) "The event 'SomeBaseClass.SomeEvent' can only appear on the left hand side of += or -=". If I replace base on this It is make no sense.
No, it's absolutely right - the event is only an event (with subscription and unsubscription) as far as a derived class is concerned. If your base class wants to let derived classes raise the event, it should include a protected method to do so (typically a virtual OnFoo(EventHandler) for an event called Foo with the EventHandler type, for example). Note that if you write a field-like event in C# like this:
public event EventHandler Foo;
That's actually declaring a private field called Foo (which that class and any nested classes have access to) and a public event (which consists only of subscribe/unsubscribe). You could declare your own "custom" event like this:
protected EventHandler foo;
// Note: not thread-safe. Only present for demonstration purposes.
public event EventHandler Foo
{
add { foo += value; }
remove { foo -= value; }
}
and then derived classes would have access to the field... but I wouldn't recommend that. (I rarely declare non-private fields, other than for constants.)
You need to do it the right way (i.e., the idiomatic way in C#)
public class Base {
public event EventHandler<EventArgs> SomeEvent;
protected virtual void OnSomeEvent(EventArgs e) {
EventHandler<EventArgs> handler = SomeEvent;
if (handler != null) {
handler(this, e);
}
}
}
public class Derived {
protected virtual void OnSomeEvent(EventArgs e) {
// derived event handling here
// then invoke the base handler
base.OnSomeEvent(e);
}
}
The reason that you do it like this is because events can only be invoked from within the defining class.