C# Events - Raising, Subscribing from two different projects in one solution - c#

My goal is to create an event, raise the event in Project #1, have Project #2 subscribe to the event so that I can hit some code in Project #2. I was advised to do it this way because I cannot reference the code in Project #2.
I'm not familiar with events and have been researching the last hour. Is there anyone that can provide example code on how to do this? What I have now is not working.
// Creating the event
public event EventHandler UpdateUIEvent;
// Raising the event in Project #1
protected override void ResetProperties()
{
this.filePath = string.Empty;
EventArgs e = new EventArgs();
UpdateUIEvent(this, e); // this kills my program
}
// Subscribing to the event in Project #2
protected override void ClearAll()
{
boEncrypt.UpdateUIEvent += new EventHandler ?? // Not sure how to subscribe here
tbFile.text = string.Empty;
}

You're close... Please read the docs as usual for a better understanding of what needs to be where.
For your code, here's what we'll do:
In project 1, incorporate Michael HB's good recommendation to always check if there's any subscribers.
public class ProjectOneClass
{
public event EventHandler UpdateUIEvent;
// other stuff
protected override void ResetProperties()
{
this.filePath = string.Empty;
EventArgs e = new EventArgs();
var handler = UpdateUIEvent;
if (handler != null)
UpdateUIEvent(this, e);
}
}
Then in project 2, you have to have a reference to the event, which means you also need a reference to the class that contains it. You define a method in the class in project 2 that has the same signature as the event in project 1. In your case (in many cases) this signature is void (object sender, EventArgs e). EventHandler is a pre-defined delegate with this signature.
"Subscribing" to the event is adding your new method to the handler, like pOne.UpdateUIEvent += SomeMethodWithTheSameSignature; You're basically saying "whenever UpdateUIEvent is called, call this other method as well".
So now that we know that we can have a method called when the event fires, we need to define that event. If you want to call ClearAll() when the event fires, that's what goes in the method body.
public class ProjectTwoClass
{
public ProjectOneClass pOne;
// other stuff
public ProjectTwoClass()
{
pOne = new ProjectOneClass();
pOne.UpdateUIEvent += POneOnUpdateUIEvent;
}
public void POneOnUpdateUIEvent(object sender, EventArgs eventArgs)
{
ClearAll();
}
private void ClearAll()
{
tbFile.Text = string.Empty; // could probably just call tbFile.Clear();
}
}

Before raising an event, you should always check if it is not null.
protected override void ResetProperties()
{
this.filePath = string.Empty;
EventArgs e = new EventArgs();
var handler = UpdateUIEvent;
if(handler != null)
UpdateUIEvent(this, e);
}
Even when you are in different projects, you should only matter about the "public" keyword (internal or private won't work).
In project #2, you'll have
protected overeride void ClearAll()
{
boEncrypt.UpdateUIEvent += yourFunctionToTriggerWhenTheEventIsRaised;
tbFile.text = string.Empty;
}

Related

Is it a bad idea to use the invocation list of an inherited event?

I have a class that inherits from ObservableCollection<T>. In that class I have a method that changes the collection internally and for which I'd like to suppress CollectionChanged events.
public class ContentBlockList : ObservableCollection<int> {
public void SomeMethod() {
var handlers = CollectionChanged.GetInvocationList();
foreach (NotifyCollectionChangedEventHandler handler in handlers) {
CollectionChanged -= handler;
}
// do stuff here
foreach (NotifyCollectionChangedEventHandler handler in handlers) {
CollectionChanged += handler;
}
}
}
Intuitively it seems like this should work since I'm accessing the event from within its containing object. Unfortunately, the compiler says
The event 'ObservableCollection.CollectionChanged' can only
appear on the left hand side of += or -=
I can get the code to work if I override both CollectionChanged and OnCollectionChanged(), essentially replacing the .NET versions with copies of my own. However, having to do something like that makes me suspicious that I'm ignoring some reason why doing this is a bad idea in the first place. Thanks for any thoughts on this.
As unsubscribing and re-subscribing to an event is a relatively (not really painful but I don't know how many subscribers there is likely to be) slow process I would recommend that you look into overriding both the the OnCollectionChanged and OnPropertyChanged methods for the base ObservableCollection.
So have something that resembles:
public class ContentBlockList : ObservableCollection<int>
{
private bool internallyUpdating;
public void SomeMethod()
{
this.internallyUpdating = true;
// Do Stuff (Add to base collection)
this.internallyUpdating = false;
this.OnPropertyChanged(new PropertyChangedEventArgs(#"Count");
this.OnPropertyChanged(new PropertyChangedEventArgs(#"Item[]");
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if(this.internallyUpdating)
{
return;
}
base.OnCollectionChanged(e);
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
if(this.internallyUpdating)
{
return;
}
base.OnPropertyChanged(e);
}
}
What this allows for is the ability to suppress the events being raised while you update internally but doing this in a way that means you do not have to unsubscribe and resubscribe to events.
When adding to this collection normally (i.e. with contentBlockList.Add(1)) you'll fall straight through to calling the base event. But when you are trying to update internally you'll suppress these events until you have finished.
I'd say this is both more efficient in terms of performance but also much neater code than what you were looking at.
On a last note I'd also say that the NotifyCollectionChangedEventAction that you provide is Reset. You've probably done quite a big change to the collection and to handle it you'll want any subscriber to have to refresh their look on the collection, be it a control in a WPF view or even another class that uses the collection.
better use this :
public class ContentBlockList : ObservableCollection<int>
{
ContentBlockList()
{
this.CollectionChanged += ContentBlockList_CollectionChanged;
}
void ContentBlockList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
}
}
if you maintain your code try this
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ContentBlockList pp = new ContentBlockList();
pp.CollectionChanged += pp_CollectionChanged;
pp.CollectionChanged += pp_CollectionChanged1;
pp.Add(11112);
pp.SomeMethod();
}
void pp_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
}
void pp_CollectionChanged1(object sender, NotifyCollectionChangedEventArgs e)
{
}
}
public class ContentBlockList : ObservableCollection<int>
{
public void SomeMethod()
{
var handlers = CollectionChanged.GetInvocationList();
foreach (NotifyCollectionChangedEventHandler handler in handlers)
{
CollectionChanged -= handler;
}
// do stuff here
foreach (NotifyCollectionChangedEventHandler handler in handlers)
{
CollectionChanged += handler;
}
}
public override event System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged;
}
As far as I understood you need to interrupt firing of CollectionChanged to do some work silently. So you can create boolean field like __FireCollectionChanged and then override OnCollectionChanged() to do:
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (__FireCollectionChanged)
base.OnCollectionChanged(e);
}
Then you can control whether event is fired by that boolean field.
And answering the actual question: you can't use the invocation list directly, because the event is not the delegate type field. It's basically just two methods add and remove for subscribe/unsubscribe behavior. Underlying delegate field is created behind the scenes and you typically don't want to use it.

How to implement an event class

This question is in reference to another question, mine is similar but I am asking for help beyond what I have read in this answer:
Raise an event whenever a property's value changed?
EDIT: What I am trying to accomplish is a global message "service" within the application such that I can write to the message variable from different places within the application and have the User Interface (winform) pick up on the fact that there was some change to that variable and based upon the event, I can read the message variable and display its content to the user. I hope this makes more sense now.
First, I am new to the world of C# and while I understand the code written as the most accepted answer, where I fail, is to understand the final implementation. If I place this code in a .cs file and I use the namespace in winform file how do I finalize the implementation? In my case, I would want to implement the class in the winform file so I can watch for an event to occur. Once the event occurred I would write some information to the user via the winform interface. I think I would need to use the "get" of the string...but not sure how the implementation would go? I apologize in advance if this doesn't make sense, I am trying to piece this all together. Thanks for any help on this!
For reference, I have start with the answer provide and altered it for my purposes:
public class Messaging : INotifyPropertyChanged
{
private string dLMessage;
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, e);
}
protected void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
public string DLMessage
{
get { return dLMessage; }
set
{
if (value != dLMessage)
{
dLMessage = value;
OnPropertyChanged("DLMessage");
OnDLMessageChanged(EventArgs.Empty);
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnDLMessageChanged(EventArgs e)
{
EventHandler handler = DLMessageChanged;
if (handler != null)
handler(this, e);
}
public event EventHandler DLMessageChanged;
}
Edit
According to your edited question, there are many different ways. One of those is making DLMesaage property and its change event static:
public class Messaging
{
private static string dLMessage;
public static string DLMessage
{
get { return dLMessage; }
set
{
if (value != dLMessage)
{
dLMessage = value;
OnDLMessageChanged(EventArgs.Empty);
}
}
}
protected static void OnDLMessageChanged(EventArgs e)
{
EventHandler handler = DLMessageChanged;
if (handler != null)
handler(null, e);
}
public static event EventHandler DLMessageChanged;
}
and then subscribe for event this way in all your different forms (for example in form load event)
Messaging.DLMessageChanged += msg_DLMessageChanged;
having this function in that form:
void msg_DLMessageChanged(object sender, EventArgs e)
{
MessageBox.Show("at last changed!");
}
You can unsubscribe for event this way:
Messaging.DLMessageChanged -= msg_DLMessageChanged;
For example if you subscribed for event in some forms, you can put unsubscribe code in Dispose override:
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
Messaging.DLMessageChanged -= msg_DLMessageChanged;
components.Dispose();
}
base.Dispose(disposing);
}
This way if you close the form, the event never be handled in that form.
Please note that I am keeping things simple in order to get your job done with minimum changes.
Original
Put this some where you instantiate Messageing instance for example in your form's constructor or load event handler:
Messaging msg = new Messaging();
msg.DLMessageChanged += msg_DLMessageChanged;
Add this to the form:
void msg_DLMessageChanged(object sender, EventArgs e)
{
MessageBox.Show("at last changed!");
//You can access the new value using Messaging.DLMessage
}
And also it seems you don't need to implement INotifyPropertyChanged if you only want DLMessageChanged. Now you are raising both events.
Or in case you want to use PropertyChanged event, put this some where you instantiate Messageing instance for example in your form's constructor or load event handler:
Messaging msg = new Messaging();
msg.PropertyChanged+= msg_PropertyChanged;
Add this to the form:
void msg_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "DLMessage")
MessageBox.Show("at last changed!");
}

Creating an event in a dll and handling the event in a Form

I have created a DLL using the following code. I have compiled this code as a DLL.
namespace DllEventTrigger
{
public class Trigger
{
public delegate void AlertEventHandler(Object sender, AlertEventArgs e);
public Trigger()
{
}
public void isRinging()
{
AlertEventArgs alertEventArgs = new AlertEventArgs();
alertEventArgs.uuiData = "Hello Damn World!!!";
CallAlert(new object(), alertEventArgs);
}
public event AlertEventHandler CallAlert;
}
public class AlertEventArgs : EventArgs
{
#region AlertEventArgs Properties
private string _uui = null;
#endregion
#region Get/Set Properties
public string uuiData
{
get { return _uui; }
set { _uui = value; }
}
#endregion
}
}
Now I'm trying to handle the event triggered by this dll in a forms application with this code.
namespace DLLTriggerReciever
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Trigger trigger = new Trigger();
trigger.isRinging();
trigger.CallAlert += new Trigger.AlertEventHandler(trigger_CallAlert);
}
void trigger_CallAlert(object sender, AlertEventArgs e)
{
label1.Text = e.uuiData;
}
}
}
My problem i'm not sure where i went wrong. Please suggest.
You need to assign your event handler before the event is actually fired, otherwise the code will throw a NullReferenceException.
trigger.CallAlert += new Trigger.AlertEventHandler(trigger_CallAlert);
trigger.isRinging();
Additionally, it's a recommended practice to check first, whether there are handlers assigned:
var handler = CallAlert; // local variable prevents a race condition to occur
if (handler != null)
{
handler(this, alertEventArgs);
}
as #Gene said, you need to register the event before raising it.
anyway, it's a good practice to check if someone is register to the event you're about to raise by checking for null.
like this:
if (this.CallAlert != null)
this.CallAlert(new object(), alertEventArgs);

Custom event handler not getting initialized

Following is the Code i am using
class ImpersonatedTab : System.Windows.Forms.TabPage
{
Credentials _cred = new Credentials();
public delegate void Savesetting(TabData Tb);
public event Savesetting TriggerSaveSetting;
public ImpersonatedTab(TabData tb)
{
........
}
private void SaveData()
{
TriggerSaveSetting(_tabdata);
}
private Onclick()
{
SaveData();
}
}
When i call Onclick function within ImpersonatedTab class it returns error saying TriggerSaveSetting is null
I initialize this call like
ImpersonatedTab Tab = new ImpersonatedTab(tb);
Tab.TriggerSaveSetting += new ImpersonatedTab.Savesetting(Tab_TriggerSaveSetting);
i have created events earlier, but am not able to figure out whats wrong with this one.. i am sure should be some silly mistake.
One possible case where this could happen is if you try to call the event from within the constructor of the ImpersonatedTab class. There where you put those .... Also it is a good practice to check if the event handler has been initialized before calling it.
Change your code to this:
public delegate void Savesetting(TabData Tb);
private Savesetting saveSettingDlg;
public event Savesetting TriggerSaveSetting {
add { saveSettingDlg += value; }
remove { saveSettingDlg -= value; }
}
private void SaveData() {
var handler = saveSettingDlg;
if (handler != null) handler(_tabdata);
}
You can now set a breakpoint on the add accessor and SaveData() and verify that event subscription is working correctly. If you do see the add accessor getting called but still get a null for handler then there's a problem with the object reference.
You are trying to invoke the TriggerSaveSetting event handlers before any handler was attached to it. Make sure, to check, the event has been some handlers attached:
private void OnTriggerSaveSetting(_tabdata)
{
if (TriggerSaveSetting != null)
TriggerSaveSetting(_tabdata);
}

Question about custom events

I'm making custom events for C# and sometimes it isn't working.
This is how I'm making the event happen:
private bool isDoorOpen;
public bool IsDoorOpen {
get { return isDoorOpen;}
private set { isDoorOpen = value; DoorsChangeState(this, null);}
}
And these are the event declarations:
//events
public delegate void ChangedEventHandler(Elevator sender, EventArgs e);
public event ChangedEventHandler PositionChanged;
public event ChangedEventHandler DirectionChanged;
public event ChangedEventHandler BreaksChangeState;
public event ChangedEventHandler DoorsChangeState;
This works as long as there are methods attached to the events, but if there isn't, it throws a null ref exception. What am I doing wrong?
The recommended way to call an event is
var handler = this.DoorsChangeState;
if (handler != null)
handler(this, null);
The reason for copying the handler locally is incase the event handler changes on another thread while you're checking for null.
EDIT: Found the article talking about race conditions.
http://blogs.msdn.com/ericlippert/archive/2009/04/29/events-and-races.aspx
I know this question has been discussed (and answered) several times here on SO.
Also somewhere here i got the following extension methods to make this pattern more easy to use:
public static class EventHandlerExtensions
{
public static void FireEvent<T>(this EventHandler<T> handler, object sender, T args) where T : EventArgs
{
var temp = handler;
if (temp != null)
{
temp(sender, args);
}
}
public static void FireEvent(this EventHandler handler, object sender)
{
var temp = handler;
if (temp != null)
{
temp(sender, EventArgs.Empty);
}
}
}
So in your code you can say:
public bool IsDoorOpen
{
get { return isDoorOpen;}
private set
{
isDoorOpen = value;
DoorsChangeState.FireEvent(this);
}
}
If a event isn't subscribed to when it fires, a NullReferenceException will be thrown. This is correct behaviour, not something you've done wrong.
You should check:
if(DoorsChangeState != null)
{
DoorsChangeState(this, null); // Only fire if subscribed to
}
Before invoking an event you must check if the event is null:
if (DoorsChangeState != null)
DoorsChangeState(this, null);
When DoorsChangeState is null that means there are no listeners on that event.
You need to check to see if the event has been subscribed to.
I use this standard form for throwing all of my events.
var temp = EventName;
if(EventName!= null)
temp(this, null);

Categories

Resources