I hope I'm understanding your question correctly. I think we're talking about two things.
First - how events work in C#
Second - how a WinForms application written in C# knows when you've clicked a button.
Events in C# are their own distinct thing. You can write a console app, create your own event, listen to it, fire it, respond to it, etc... and it all just works. You subscribe to an event by called Add() and you unsubscribe by calling Remove(). The event itself keeps track of what methods are listening to it and when it's raised, calls all of those methods.
Jon Skeet explains it much better:
How do C# Events work behind the scenes?
But these events are just C# code. Related to, but distinct from the Win32 messages you're mentioning. In a Winforms application, when the user clicks a button, how does the application know about it? We can look using the debugger (turn off the 'My Code' https://msdn.microsoft.com/en-us/library/dn457346.aspx option) and set a breakpoint in the click event, you'll be able to see what is going on.

So in Windows.Forms.Controls.ControlNativeWindow there is a WndProc method that takes in a System.Windows.Forms.Message m.
Right before that is a 'debuggableCallback' method. That mirrors what you'd expect from a Win32API app.
Source here:
http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/NativeWindow.cs,ad40308c5b6490dd
/// <include file='doc\NativeWindow.uex' path='docs/doc[@for="NativeWindow.DebuggableCallback"]/*' />
/// <devdoc>
/// Window message callback method. Control arrives here when a window
/// message is sent to this Window. This method packages the window message
/// in a Message object and invokes the wndProc() method. A WM_NCDESTROY
/// message automatically causes the releaseHandle() method to be called.
/// </devdoc>
/// <internalonly/>
private IntPtr DebuggableCallback(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) {
// Note: if you change this code be sure to change the
// corresponding code in Callback above!
Message m = Message.Create(hWnd, msg, wparam, lparam);
try {
if (weakThisPtr.IsAlive && weakThisPtr.Target != null) {
WndProc(ref m);
}
else {
DefWndProc(ref m);
}
}
finally {
if (msg == NativeMethods.WM_NCDESTROY) ReleaseHandle(false);
if (msg == NativeMethods.WM_UIUNSUBCLASS) ReleaseHandle(true);
}
return m.Result;
}
So, ultimately, if you're running on Windows, it's driven by the same Win32 API messages you'd expect. It's just that the System.Windows.Forms classes are written to encapsulate most of it from us.