As with the base classes in our application framework, the built-in WPF controls also have an inheritance hierarchy, with each successive base class offering some additional functionality. Let's look at the Button class as an example. Here is the inheritance hierarchy of the Button control:
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Media.Visual System.Windows.UIElement System.Windows.FrameworkElement System.Windows.Controls.Control System.Windows.Controls.ContentControl System.Windows.Controls.Primitives.ButtonBase System.Windows.Controls.Button
As with every object in .NET Framework, we start with the Object class, which provides low-level services to all classes. These include object comparison, finalization, and the ability to output a customizable string representation of each object.
Next is the DispatcherObject class, which provides each object with thread affinity and associates them with a Dispatcher object. The Dispatcher class manages a prioritized queue of work items for individual threads. Only the thread that the associated Dispatcher object was created on can access each DispatcherObject directly and this enables derived classes to enforce thread safety.
After the DispatcherObject class, we have the DependencyObject class, which enables all derived classes to use the WPF property system and declare Dependency Properties. The GetValue and SetValue methods that we call to access and set their values are also provided by the DependencyObject class.
Next up is the Visual class, which has the primary role of providing rendering support. All elements that are displayed in the UI will extend the Visual class. In addition to rendering each object, it also calculates their bounding box and provides support for hit testing, clipping, and transformations.
Extending the Visual class is the UIElement class, which provides a number of core services to all of its derived classes. These include the event and user input systems and the ability to determine the element's layout appearance and rendering behavior.
Following on from that is the FrameworkElement class, which provides the first framework-level members, building upon the foundation of the core-level classes that it extends. It is the FrameworkElement class that enables data binding through the DataContext property and styling through the Style property.
It also provides events that relate to an object's lifetime, an upgrade of the core-level layout system to a full layout system and improved support for animations, among other things. This is typically the lowest-level class that we might want to extend if we were creating our own basic elements, as it enables derived classes to partake in the majority of the WPF UI capabilities.
The Control class extends the FrameworkElement class and is the base class for most of the WPF UI elements. It provides appearance templating through the use of its ControlTemplate functionality and a host of appearance-related properties. These include coloring properties, such as Background, Foreground, and BorderBrush, along with alignment and typeface properties.
Extending the Control class is the ContentControl class, which enables controls to have one object of any CLR type as its content. This means that we can either set data objects or UI elements as the content, although we may need to provide a DataTemplate for the data objects if they are of a custom type.
The final class in the long line of parent classes that the Button class extends is the ButtonBase class. In fact, this is the base class for all buttons in WPF and it adds useful functionality for buttons. This includes automatically converting certain keyboard events to mouse events, so that users can interact with the buttons without using a mouse.
The Button class itself adds little to its inherited members with only three related bool properties; two that specify whether a button is the default button and one that specifies whether the button is a cancel button. We'll see an example of this shortly. It has an additional two protected overridden methods that get called when the button is clicked or when an automation peer is created for it.
While WPF enables us to modify existing controls to such a degree that we rarely need to create our own, it is important to be aware of this inheritance hierarchy so that we can extend the appropriate and most lightweight base class that fulfills our requirements when we need to.
For example, if we wanted to create our own custom button, it would typically make more sense to extend the ButtonBase class, rather than the Button class, and if we wanted to create a totally unique control, we could extend the FrameworkElement class. Now that we have a good understanding of the make-up of the available controls, let's see how they are displayed by the WPF layout system.