how to add event to class

M

mp

i have the following function in a class
private string GetCommentFromLine(string Origline,ref string CleanedLine)
{
int pos = Origline.IndexOf( ";", 0);

//....here i want to raise an event reporting the pos variable


}



how to create and raise the event in c#?

i tried to create the event

public event CommentPosition{int pos};

but get invalid token { error

i also tried with () but no good

mark
 
M

mp

Peter Duniho said:

i guess i'm just help disabled
i enter "create event in class" in my help search box and get 500 hits, the
first 20 of which don't show anything about how to create an event in a
class(like the page you linked)
maybe if i looked through the next 480 i might have come across that page...
i always check help first but often don't find what i'm looking for...
:-(
thanks for the link
mark
 
M

mp

Peter Duniho said:
It does take practice.

When using search engines, it's useful to consider what they are actually
doing and how they work. Note that your search text didn't include "C#"
at all, even though your question is definitely C#-specific.

i'm in the c# express ide help
I have c# selected as the language to show
i thought that would filter on c# answers
Also, it's important to keep in mind that certain very common words are
often discarded for the purpose of the search. Words like "in", "as",
"the", etc. That means your search probably was processed as "create
event class", which explains the very first hits that showed up.

now i enter just "event" and the first link is useful...go figure
:)
That said, more useful links weren't really that far down. Some times all
that's needed is a little more persistence. :)

Pete

that said,
i have tried to incorporate the examples into my code and i'm not getting
something right
man, this is way harder to declare,raise,and consume an event than in vb6
but oh well....

i'm trying to follow the example in
How to: Publish Events that Conform to .NET Framework Guidelines (C#
Programming Guide)
http://msdn.microsoft.com/en-us/library/w369ty8x.aspx


having trouble getting all the parts adapted to my class that raises the
event, and the form that consumes the event...
but i'll keep trying
thanks for the links
mark
 
J

Jeff Johnson

i tried to create the event

public event CommentPosition{int pos};

Events require a "type". Specifically, they need to be of some type of
delegate. The old 1.x syntax for events was incredibly ugly, in my opinion.
However, once 2.0 introduced generics it became much easier to declare
events. Basically, you do this:

public event EventHandler<xxx> CommentPosition;

The xxx represents a type that is either EventArgs or is derived from
EventArgs. Notice that there are NO parameters to the event. That's VB
thinking; get away from it. If you need extra data associated with your
event you derive your own class from EventArgs and give it properties to
hold this data. (There are some pre-existing classes in the Framework which
may do what you want, but I usually find myself creating my own.)

So you might do this:

public class PositionEventArgs : EventArgs
{
private int _position;
public int Position
{
get { return _position; }
set { _position = value; }
}

public PositionEventArgs(int position)
{
_position = position;
}
}

I highly recommend you follow the convention of putting "EventArgs" at the
end of the class name as I did above.

Then to raise the event you simply call the event name itself as if it were
a method (because it basically is). To do this it's best to set up a common
method to do the work, and the naming convention is "On<YourEventName>":

// I'm declaring this as void but it doesn't have to be. If this
// is one of those events that receives data from the subscriber,
// (like a cancelable event) you might want this handler to return
// that data.
private void OnCommentPosition(int position)
{
EventHandle<PositionEventArgs> threadSafeCopy = CommentPosition;

if (threadSafeCopy != null)
{
// I'm creating a new PositionEventArgs object on the fly,
// but if the subscriber were going to modify its properties,
// I'd want to create the object in a variable, pass that in
// to the call below, and then examine the properties after
// the call.
threadSafeCopy(this, new PositionEventArgs(position));
}
}

And then when you need to raise the event you simply do:

OnCommentPosition(theCurrentPosition);

There's some voodoo happening behind the scenes, and I could explain some of
it, but if all you care about is actually setting up and raising events,
this is all you need.

One more thing: I must say I don't care for the name of your event. Events
should have a verb in them, as they represent something happening. Change.
Click. Render. CommentPosition sounds like a couple of nouns. By the name of
the event alone I, as "some other programmer," have no idea what sort of
action it represents.
 
J

Jeff Johnson

private void OnCommentPosition(int position)
{
EventHandle<PositionEventArgs> threadSafeCopy = CommentPosition;

Typo: that should be "EventHandler<PositionEventArgs>". I dropped a letter.
 
M

mp

Thanks Jeff for the input, see comments inline (hope this didn't get too
long)
amazingly I finally got an event working after several tries to follow the
tutorial on this page:
http://msdn.microsoft.com/en-us/library/w369ty8x.aspx

Jeff Johnson said:
Events require a "type". Specifically, they need to be of some type of
delegate.
snip


So you might do this:

do you put this (EventArg class) in it's own cs file? or create it as a
separate class inside the cs file holding the 'event publishing' class? I
ended up using a separate class file, not sure if that matters, but the
class raising the event(cLispFile) and the class subscribing the event
(window.xaml.cs)- both need to see this class.
"//Declare your class at a scope visible to both your publisher and
subscriber classes, "
public class PositionEventArgs : EventArgs
{
snip
}
I highly recommend you follow the convention of putting "EventArgs" at the
end of the class name as I did above.

yep, that tutorial also had that convention
Then to raise the event you simply call the event name itself as if it
were a method (because it basically is). To do this it's best to set up a
common method to do the work, and the naming convention is
"On<YourEventName>":

// I'm declaring this as void but it doesn't have to be. If this
// is one of those events that receives data from the subscriber,
// (like a cancelable event) you might want this handler to return
// that data.
private void OnCommentPosition(int position)
{
EventHandle<PositionEventArgs> threadSafeCopy = CommentPosition;

if (threadSafeCopy != null)
{
// I'm creating a new PositionEventArgs object on the fly,
// but if the subscriber were going to modify its properties,
// I'd want to create the object in a variable, pass that in
// to the call below, and then examine the properties after
// the call.
threadSafeCopy(this, new PositionEventArgs(position));
}
}

that's a little different from what i came up with per the tutorial...i'll
have to study this a bit to understand the pros/cons of each way....
here's what i ended up with
//from example next step
// Wrap event invocations inside a protected virtual method
// to allow derived classes to override the event invocation
behavior
protected virtual void OnRaiseCustomEvent(CommentEventArgs e)
{
// Make a temporary copy of the event to avoid possibility of
// a race condition if the last subscriber unsubscribes
// immediately after the null check and before the event is
raised.
EventHandler<CommentEventArgs> handler = RaiseCustomEvent;

// Event will be null if there are no subscribers
if (handler != null)
{

// Use the () operator to raise the event.
handler(this, e);
}
}
And then when you need to raise the event you simply do:

OnCommentPosition(theCurrentPosition);

in my case it ended up;
this happens in a loop reading through the file line by line, each pass
calls this with the current data for that line
(i'll be adding two more string args, but for now this to get the structure
working)
OnRaiseCustomEvent(new CommentEventArgs(ThisComment, pos));

so i see i can either pass the args(your example) or the EventArgs object
(my sample) at this point
that makes sense.
if all you care about is actually setting up and raising events, this is
all you need.

actually the second half of all this is subscribing to the event...
in my case i put (in the window.xaml.cs) the following...
(seems to work, don't know if it's 'right' so to speak:)

class Subscriber
{
private System.Windows.Controls.ListBox lb;
public Subscriber(cLispFile cf, System.Windows.Controls.ListBox
LB)
{
lb = LB;
// Subscribe to the event using C# 2.0 syntax
cf.RaiseCustomEvent += HandleCustomEvent;
}
// Define what actions to take when the event is raised.
void HandleCustomEvent(object sender, CommentEventArgs e)
{
lb.Items.Add(e.Position);
}
}

then to create the class that raises the event and the one that subscribes
to it
private void button1_Click(object sender, RoutedEventArgs e)
{
cLispFile cf = new cLispFile("path to file");
Subscriber sub1 = new Subscriber(cf, this.listBox1);
cf.ReadFile();
One more thing: I must say I don't care for the name of your event. Events
should have a verb in them, as they represent something happening. Change.
Click. Render. CommentPosition sounds like a couple of nouns. By the name
of the event alone I, as "some other programmer," have no idea what sort
of action it represents.

that makes sense, i could change it to DetectComment or ReportComment or
something like that.

I'm probably using this event in a goofy way (not being a 'real' programmer)
i'm parsing lisp code text files, detecting and removing comments to create
a "clean" version
just as a way to watch what's going on and see if i'm correctly trapping all
the comments, i created a form with 3 listboxes to show the original lines,
the comments found, and the resulting "cleaned" version.
to see if my string.substring(int,int) was using the correct numbers i added
a listbox to show what position the comment was found in.

originally i was populating 4 stringbuilders with the content for the 4
listboxes, then after the loop through the file populate the listboxes from
the stringbuilders

then the idea of using an event inside the loop instead of filling
stringbuilders and later reading them back occurred to me and that's what
started my excursion into learning how to do events.

vb6 was so easy i didn't realize how convoluted it would become with dotnet.

anyway thanks again for the input and i appreciate any comments you have on
what i've done compared to what you showed as well, especially the
subscriber part as you hadn't addressed that

thanks
mark
 
M

mp

Peter Duniho said:
[...]
do you put this (EventArg class) in it's own cs file? or create it as a
separate class inside the cs file holding the 'event publishing' class?

You can do whatever seems to make the most sense for you.
snip

I think that using an event to allow reporting of something that happens
during the normal processing is a pretty typical use of events, actually.

Pete

Thank you very much for all that info.
I really appreciate it.
Mark
 
M

mp

Peter Duniho said:
On 11/2/10 1:11 PM, mp wrote: snip



I gather from the code example that the event's name is
"RaiseCustomEvent". The event's name itself should just be "CustomEvent".

IMHO, the event name should describe what happened. So, something like
"CommentDetected" or "CommentFound" might be appropriate.

ok, I changed the event name to "CommentDetected"
As far as the method name goes, I will recommend _either_ "OnCustomEvent"
or "RaiseCustomEvent".

ok, I changed the name to "RaiseCommentDetectedEvent"
At least based on the usage you showed, I don't think you really need the
"Subscriber" class. Instead, this would work just as well:

private void button1_Click(object sender, RoutedEventArgs e)
{
cLispFile cf = new cLispFile("path to file");

cf.RaiseCustomEvent += (sender, e) =>
{
listBox1.Items.Add(e.Position);
};

cf.ReadFile();
}

I must have done something wrong
I tried the above and now get the following errors:

at this line;
cf.RaiseCommentDetectedEvent += (sender, e) =>

I get the errors:

Error 1
'LispViewer.cLispFile.RaiseCommentDetectedEvent(LispViewer.CommentDetectedEventArgs)'
is inaccessible due to its protection level

Error 2 A local variable named 'sender' cannot be declared in this scope
because it would give a different meaning to 'sender', which is already used
in a 'parent or current' scope to denote something else

Error 3 A local variable named 'e' cannot be declared in this scope because
it would give a different meaning to 'e', which is already used in a 'parent
or current' scope to denote something else

//revised names/definitions:
class cLispFile
{
public delegate void CommentDetectedEventHandler(object sender,
CommentDetectedEventArgs a);
public event EventHandler<CommentDetectedEventArgs>
CommentDetectedEvent;
protected virtual void
RaiseCommentDetectedEvent(CommentDetectedEventArgs e)
{
EventHandler<CommentDetectedEventArgs> handler =
CommentDetectedEvent;
if (handler != null)
{
handler(this, e);
}
}
//also tried void RaiseCommentDetectedEvent(CommentDetectedEventArgs e)
//to get rid of protected virtual but that didn't help either

what did i do wrong?
thanks
marlk
 
M

mp

Peter Duniho said:
[...]
At least based on the usage you showed, I don't think you really need
the
"Subscriber" class. Instead, this would work just as well:

private void button1_Click(object sender, RoutedEventArgs e)
{
cLispFile cf = new cLispFile("path to file");

cf.RaiseCustomEvent += (sender, e) =>
{
listBox1.Items.Add(e.Position);
};

cf.ReadFile();
}

I must have done something wrong
I tried the above and now get the following errors:

at this line;
cf.RaiseCommentDetectedEvent += (sender, e) =>

Should be "cf.CommentDetected", not "cf.RaiseCommentDetectedEvent".

As for the other errors, that's my fault. I blindly followed my usual
pattern for lambda methods, but didn't take into account the method's own
parameter names.

Change to:

cf.CommentDetected += (sender1, e1) =>

I had tried capitalizing sender and e thinking that would make them
different, but didn't seem to work
now renaming them does work
Or something like that (you just have to use different names for the
lambda method's parameters.they can actually be whatever you want, as long
as they are different than those found in the button1_Click() method).

Pete

thanks again for your tireless assistance to me and so many others
when do you find time to get your own work done?
:)
mark
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top