struggling with events

M

mp

I'm trying to digest the basic event syntax(and not succeeding very
well<g>)

************************************************
according to page: http://www.codeproject.com/KB/cs/event_fundamentals.aspx

one should, if possible, use the generic framework supplied objects
System.EventArgs
"You can encapsulate all event arguments as properties
of a class that derives from System.EventArgs."
"The first alternative listed above is strongly encouraged,
and support for it is built into the .NET Framework
through the System.EventArgs class."

--and--

"While you can create your own event handlers (and sometimes you might need
to),
you should use one of the EventHandler delegates provided by the .NET
Framework
in cases where one of the Framework's event handlers would work
with your particular event implementation. "
************************************************

i'm trying here to create the absolute simplest format so i can begin
understanding
where am I going wrong in the following simple form
button_click wants to raise an event that carries no data (so
System.EventArgs.Empty)

namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

public delegate void EventHandler(object sender, EventArgs e);
public event EventHandler MyEvent;

public class MyEventArgs : System.EventArgs
{ }

public void RaiseTheEvent(MyEventArgs eventArgs)
{
try
{
EventHandler handler = MyEvent;
if (handler != null)
{
handler(this, eventArgs);
}
}
catch
{
// Handle exceptions here
}
}

private void button1_Click(object sender, EventArgs e)
{
RaiseTheEvent(MyEventArgs.Empty);
}
}
}


....useless whining follows...:)
vb6 took one line to define an event, one line to raise it, and one line to
catch it!?!
c# makes me feel sooooo dumb!!!
:-/
mark
 
K

kndg

I'm trying to digest the basic event syntax(and not succeeding very
well<g>)
[...]

I didn't read the site you mentioned... but when I first learn about
event, I found that the MSDN documentation is clear enough.

Basically, declaring and subscribing event is very simple. I'm not good
at explaining things but I will try...
First, declare the publisher that expose the event.

public class Publisher
{
public event EventHandler<EventArgs> MyEvent;

public void NotifyToSubscriber()
{
if (MyEvent != null) MyEvent(this, EventArgs.Empty);
}
}

Then, declare the subscriber that want to subscribe to the publisher's event

public class Subscriber
{
public string Message { get; private set; }

public Subscriber(string message)
{
Message = message;
}

public void ConveyMessage(object o, EventArgs e)
{
Console.WriteLine(Message);
}
}

Example usage:

public class MyClass
{
public static void Main(string[] args)
{
Publisher publisher = new Publisher();
Subscriber subscriber1 = new Subscriber("Hello");
Subscriber subscriber2 = new Subscriber("Hi");

publisher.MyEvent += subscriber1.ConveyMessage;
publisher.MyEvent += subscriber2.ConveyMessage;

publisher.NotifyToSubscriber();
}
}

Below example is windows forms application that simulate file
downloading. See if there is anything that you didn't understand.

using System;
using System.Threading;
using System.Windows.Forms;

namespace MyNamespace
{
public class DownloaderEventArgs : EventArgs
{
public string FileName { get; private set; }

public DownloaderEventArgs(string filename)
{
FileName = filename;
}
}

public class Downloader
{
private readonly string[] filenames;

public event EventHandler<DownloaderEventArgs> Downloaded;
public event EventHandler<EventArgs> Completed;

public Downloader(string[] filenames)
{
this.filenames = filenames;
}

private void InvokeDownloaded(DownloaderEventArgs e)
{
var handler = Downloaded;
if (handler != null) Downloaded(this, e);
}

private void InvokeCompleted(EventArgs e)
{
var handler = Completed;
if (handler != null) Completed(this, e);
}

public void StartDownload()
{
Random random = new Random();

foreach (var f in filenames)
{
Thread.Sleep(random.Next(3000) + 1000); // simulate downloading
InvokeDownloaded(new DownloaderEventArgs(f));
}

InvokeCompleted(EventArgs.Empty);
}
}

public class MyForm : Form
{
private readonly Button button;
private readonly string[] filenames = { "abc.zip", "def.zip",
"ghi.zip", "123.zip" };

public MyForm()
{
button = new Button();
button.Text = "Start";
button.Click += StartDownload;
Controls.Add(button);
}

private void StartDownload(object sender, EventArgs e)
{
var Downloader = new Downloader(filenames);
Downloader.Downloaded += Notify;
Downloader.Completed += (o, args) => { MessageBox.Show("Download
completed"); };
Downloader.StartDownload();
}

private void Notify(object sender, DownloaderEventArgs e)
{
MessageBox.Show(e.FileName);
}

[STAThread]
public static void Main(string[] args)
{
Application.Run(new MyForm());
}
}
}
 
J

Jeff Johnson

I'm trying to digest the basic event syntax(and not succeeding very
well<g>)

It took me a while to wrap my head around it, too. One thing I still don't
understand very well is the pre-Generics way of declaring events. (And by
"understand" I really mean "can do it off the top of my head.") I much
prefer the EventHandler<T> method.

Anyways, down to specifics:
public class MyEventArgs : System.EventArgs
{ }

No. No no no. There is absolutely no reason to derive from a class unless
you're going to EXTEND it. You're adding nothing to the EventArgs class so
you should just USE the EventArgs class.
public delegate void EventHandler(object sender, EventArgs e);
public event EventHandler MyEvent;

Ewww. Old style. Do this instead:

public event EventHandler<EventArgs> MyEvent;

Here's the magic: using "EventHandler<T> xxx" creates a (or uses an
existing) delegate behind the scenes that looks like this:

public delegate void xxx(object sender, T e);

So there's one of your one-liner VB6 equivalents!
public void RaiseTheEvent(MyEventArgs eventArgs)
{
try
{
EventHandler handler = MyEvent;
if (handler != null)
{
handler(this, eventArgs);
}
}
catch
{
// Handle exceptions here
}
}

Let's talk about raising the event. The first thing I recommend you do is to
drop another familiar VB term and strike the word "raise" from your
vocabulary, at least when it comes to NAMING methods. You do talk about
"raising events" in C# but that's all it is: talk. When it comes to the
recommended naming convention, instead of RaiseClick or RaiseChange you use
"On" instead, and you always use the full event name after On. OnClick.
OnChange. OnBeforeAdd. Etc. Let's rewrite your method like this:

public void OnMyEvent()
{
EventHandler<EventArgs> threadSafeCopy = MyEvent;
if (threadSafeCopy != null)
{
threadSafeCopy(this, EventArgs.Empty);
}
}

Here's what I changed:

1. The name, clearly. Yours was RaiseTheEvent. Clearly I changed Raise to
On, but that's not all. You don't have an event named TheEvent, you have
MyEvent. So the raiser method becomes OnMyEvent.

2. I removed the argument to the method. Because this event uses the
can't-get-any-simpler EventArgs class, there's no need to pass one in. You
just create one when you need it. (See below.)

3. In fact, you don't create an EventArgs at all! You use the static
EventArgs.Empty field (which is kind of a goofy name, since there's nothing
to "fill" in an EventArgs class). Could you have used new EventArgs()
instead? I suppose, although MSDN says that "This constructor is only called
by the common language runtime," so I'd recommend using EventArgs.Empty.

4. I changed the declaration of the temporary variable (which I always call
threadSafeCopy because I saw it that way in a code sample and thought it was
a perfect description of why the temp variable is being created in the first
place) to use the generic declaration.

5. I took out the try/catch block based on Pete's advice.

Let's talk about these raiser methods a little further. Suppose you have an
event that actually contains data. Let's say you're making a progress event
and you want to include an integer which contains the progress value. First
you'd make a custom event args class like this:

public class ProgressEventArgs : EventArgs
{
private int _progressValue;
public int ProgressValue
{
get { return _progressValue; }
set { _progressValue = value; } // skipping validation for brevity
}

public ProgressEventArgs() { }

public ProgressEventArgs(int value)
{
ProgressValue = value;
}
}

public event EventHandler<ProgressEventArgs> Progress;

Then you'd create a raiser method. My personal style is NOT to pass a
constructed event args object to this method but rather to pass the VALUES
that this object will contain and let the method construct the object. So I
would do this:

public void OnProgress(int value)
{
EventHandler<ProgressEventArgs> threadSafeCopy = Progress;
if (threadSafeCopy != null)
{
threadSafeCopy(this, new ProgressEventArgs(value));
}
}
...useless whining follows...:)
vb6 took one line to define an event, one line to raise it, and one line
to catch it!?!

Well, I don't know what you mean by "one line to catch it," since you had to
write an entire procedure in VB to handle an event just like in C#, and I
gave you a one-liner for declaring the event, so now the only thing you can
really complain about is "one line to raise it." Like Pete said, you get a
little more control over event raising in C#, so it's a trade-off.

When you get to the point where you want to RETURN information from the
subscriber, start a new thread and we'll talk about it!
 
J

Jeff Johnson

I'm going to disagree with a bit of Jeff's advice here (but not all.most
of what he wrote matches my own thoughts). First, if you _do_ follow the
.NET naming convention and use "On." as the method name, you should follow
the rest of the .NET convention that goes with it:

protected virtual void OnMyEvent()
{ . }

If you want the method to be public, name it "Perform." for strict
compliance with .NET's conventions.

I simply wasn't thinking when I wrote "public." Looking at my own code I
normally use private unless I plan on deriving from that class in which case
I use protected virtual. Thanks for pointing it out.

I've never heard of the "Perform" convention before. Interesting.
 

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

Similar Threads

Delegates and Events 8
How can these lines be rewritten 15
Use of event handling 6
Delegates/events 8
events used in .NET framework 7
Raising events 9
Object garbage collection of events 8
Anout raise events 2

Top