I agree, the documentation on bubbling/tunneling on MSDN isn't great at all.
I've found this MSDN magazine article "Understanding Routed Events and Commands In WPF" much better at explaining the bubbling events.
Look for the "Event Routing" section, copy pasted below:
Event Routing
Understanding a little
about the logical and visual trees is
important because routed events get
routed based primarily on the visual
tree. Routed events support a
RoutingStrategy of Bubble, Tunnel, or
Direct.
Bubble is the most common and
means that an event will bubble
(propagate) up the visual tree from
the source element until either it has
been handled or it reaches the root
element. This allows you to handle an
event on an object further up the
element hierarchy from the source
element. For example, you could attach
a Button.Click handler on the
enclosing Grid element instead of
directly on the button itself. Bubble
events just have names that indicate
their action (for example, MouseDown).
Tunnel events go in the other
direction, starting at the root
element and traversing down the
element tree until they are handled or
reach the source element for the
event. This allows upstream elements
to intercept the event and handle it
before the event reaches the source
element. Tunnel events have their
names prefixed with Preview by
convention (such as PreviewMouseDown).
Direct events behave like normal
events in the .NET Framework. The only
potential handler for the event is a
delegate that is hooked up to the
event.
Usually if a Tunnel event is
defined for a particular event, there
is a corresponding Bubble event. In
that case, the Tunnel event fires
first, starting at the root and
working its way down to the source
element looking for a handler. Once it
has been handled or has reached the
source element, the Bubble event is
fired, working its way up from the
source element and looking for a
handler. A Bubble or Tunnel event does
not stop its routing just because an
event handler is called. If you want
to stop the bubbling or tunneling
process, you mark the event as handled
in your event handler using the event
arguments you are passed:
private void OnChildElementMouseDown(object sender, MouseButtonEventArgs e) { e.Handled = true; }
Once your handler marks an event as
handled, it will not be raised to any
more handlers. Well, that is only
partially true. In reality, event
routing continues behind the scenes,
and you can explicitly hook up event
handlers in code with an override of
the UIElement.AddHandler method that
has an additional flag to effectively
say, "Call me even if the event has
been marked as handled." You specify
that flag with a call like the
following:
m_SomeChildElement.AddHandler(UIElement.MouseDownEvent, (RoutedEventHandler)OnMouseDownCallMeAlways,true);
The first parameter to AddHandler is
the RoutedEvent you want to handle.
The second is a delegate to the
event-handling method (which will need
to have the correct signature for the
event's delegate). The third parameter
indicates whether you want to be
notified even if another handler has
marked the event as handled. The
element on which you call AddHandler
is the one that will be watching for
the event to flow by during routing.
Hope this helps.