Index: ooxcb/ooxcb/eventsys.py
===================================================================
--- ooxcb/ooxcb/eventsys.py	(revision 7383ace43e330fde3f86c2a2b69c8a1468b88dce)
+++ ooxcb/ooxcb/eventsys.py	(revision 91546cff3cf17814c900b8eaead1a8ff33160ce3)
@@ -28,12 +28,12 @@
 # Copyright (c) 2006-2008 Alex Holkner
 # All rights reserved.
-# 
+#
 # Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions 
+# modification, are permitted provided that the following conditions
 # are met:
 #
 #  * Redistributions of source code must retain the above copyright
 #    notice, this list of conditions and the following disclaimer.
-#  * Redistributions in binary form must reproduce the above copyright 
+#  * Redistributions in binary form must reproduce the above copyright
 #    notice, this list of conditions and the following disclaimer in
 #    the documentation and/or other materials provided with the
@@ -167,5 +167,7 @@
 import inspect
 
-EVENT_HANDLED = True 
+from .util import cached_classproperty
+
+EVENT_HANDLED = True
 EVENT_UNHANDLED = None
 
@@ -175,4 +177,27 @@
     pass
 
+class EventDispatcherMeta(type):
+    """
+        just a meta class making sure that each EventDispatcher subclass
+        has its own `event_types` descriptor.
+    """
+    def __new__(mcs, name, bases, dct):
+        @cached_classproperty
+        def event_types(cls):
+            """
+                get the event types of me and all of my bases!
+            """
+            types = []
+            for base in cls.__bases__:
+                if hasattr(base, 'event_types'):
+                    types.extend(base.event_types)
+            if hasattr(cls, 'local_event_types'):
+                types.extend(cls.local_event_types)
+            return types
+
+        dct['event_types'] = event_types
+        return type.__new__(mcs, name, bases, dct)
+
+
 class EventDispatcher(object):
     '''Generic event dispatcher interface.
@@ -180,4 +205,7 @@
     See the module docstring for usage.
     '''
+
+    __metaclass__ = EventDispatcherMeta
+
     # Placeholder empty stack; real stack is created only if needed
     _event_stack = ()
@@ -196,8 +224,9 @@
 
         '''
-        if not hasattr(cls, 'event_types'):
-            cls.event_types = []
-        cls.event_types.append(name)
+        if not hasattr(cls, 'local_event_types'):
+            cls.local_event_types = []
+        cls.local_event_types.append(name)
         return name
+
 
     def push_handlers(self, *args, **kwargs):
@@ -243,5 +272,5 @@
         '''Attach one or more event handlers to the top level of the handler
         stack.
-        
+
         See `push_handlers` for the accepted argument types.
         '''
@@ -284,5 +313,5 @@
         frame, or if no stack frame contains any of the given handlers.
 
-        If the stack frame is empty after removing the handlers, it is 
+        If the stack frame is empty after removing the handlers, it is
         removed from the stack.  Note that this interferes with the expected
         symmetry of `push_handlers` and `pop_handlers`.
@@ -343,5 +372,5 @@
     def dispatch_event(self, event_type, *args):
         '''Dispatch a single event to the attached handlers.
-        
+
         The event is propogated to all handlers from from the top of the stack
         until one returns `EVENT_HANDLED`.  This method should be used only by
@@ -361,5 +390,5 @@
 
         :rtype: bool or None
-        :return: (Since pyglet 1.2) `EVENT_HANDLED` if an event handler 
+        :return: (Since pyglet 1.2) `EVENT_HANDLED` if an event handler
             returned `EVENT_HANDLED`; `EVENT_UNHANDLED` if one or more event
             handlers were invoked but returned only `EVENT_UNHANDLED`;
@@ -403,5 +432,5 @@
         # arguments in an event handler.  This is caught as a TypeError in
         # dispatch_event but the error message is obfuscated.
-        # 
+        #
         # Here we check if there is indeed a mismatch in argument count,
         # and construct a more useful exception message if so.  If this method
@@ -425,5 +454,5 @@
 
         # Allow default values to overspecify arguments
-        if (n_handler_args > n_args and 
+        if (n_handler_args > n_args and
             handler_defaults and
             n_handler_args - len(handler_defaults) <= n_args):
@@ -438,8 +467,8 @@
             else:
                 descr = repr(handler)
-            
+
             raise TypeError(
                 '%s event was dispatched with %d arguments, but '
-                'handler %s has an incompatible function signature' % 
+                'handler %s has an incompatible function signature' %
                 (event_type, len(args), descr))
         else:
@@ -447,6 +476,6 @@
 
     def event(self, *args):
-        '''Function decorator for an event handler.  
-        
+        '''Function decorator for an event handler.
+
         Usage::
 
Index: ooxcb/ooxcb/util.py
===================================================================
--- ooxcb/ooxcb/util.py	(revision 4a478dca50342baa2d9df687bff730290691d0cc)
+++ ooxcb/ooxcb/util.py	(revision 91546cff3cf17814c900b8eaead1a8ff33160ce3)
@@ -46,4 +46,19 @@
         result = self.func(obj)
         setattr(obj, self.name, result)
+        return result
+
+class cached_classproperty(object):
+    """
+        a modified version of :class:`cached_property` that allows to define
+        cached properties on classes.
+    """
+    def __init__(self, func):
+        self.func = func
+        self.name = func.__name__
+
+    def __get__(self, obj, type):
+        # equal if we call it bound to an instance or not.
+        result = self.func(type)
+        setattr(type, self.name, result)
         return result
 
