WPF defines a large number of types, forming an extensive class hierarchy. This appendix describes the roles of the most important types. It does not provide a detailed description of each type—the MSDN documentation already does that. Instead, this is a high-level guide offering a broad view of how the various types fit together. Figure D-1 shows the inheritance relationships among the types described in this appendix.
System.Object System.Windows.Threading.DispatcherObject
DispatcherObject
is the base
class of any type associated with a Dispatcher
. The dispatcher mechanism is WPF’s
message processing system. It underpins critical services including
input handling, and certain aspects of layout and data binding. It is
also at the heart of the WPF threading model. Threading and the
dispatcher system are described in Appendix C.
All of the base types examined in this appendix derive from
DispatcherObject
, reflecting the
dispatcher’s central role. Although DispatcherObject
is the most common base class
in WPF, few classes derive from it directly. Most derive indirectly via
DependencyObject
.
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject
Classes derive from DependencyObject
to employ WPF’s dependency property (DP) system. The majority
of WPF element types implement their properties using the GetValue
and SetValue
helper functions provided by DependencyObject
. Chapter 18 illustrates this technique.
By handing control of a property to the dependency property system, we can take advantage of data binding, styling, animation, property value inheritance, default values, per-type metadata, and property change notifications.
It is fairly unusual to derive directly from DependencyObject
—your classes will normally
derive from one of the other classes described in this appendix,
inheriting from DependencyObject
indirectly. However, one scenario where deriving directly from this
class can be useful is if you are writing a class whose only job is to
act as a data binding source, with values that change on a regular
basis. Although WPF supports ordinary CLR properties and can use the
.NET Framework class library’s INotifyPropertyChange
interface to discover
when properties change, it needs to use reflection to read ordinary
properties. However, when binding to a dependency property, WPF provides
the property implementation, so it does not need to use reflection to
read properties, nor does it need to rely on property change events.
This makes binding to a dependency property slightly more efficient than
binding to an ordinary CLR property. However, the difference is slight,
so you would typically use this approach only if you had identified
performance problems in this area.
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Media.Visual
The Visual
class is the
abstract base class of all 2D elements in the visual tree. It provides
rendering services, as well as transformation and hit testing
support.
This class is not an extensibility point—you cannot usefully
derive your own classes from it, because all the rendering services it
provides are defined by members marked as internal
. You must inherit from UIElement
if you wish to exploit the rendering
services. However, it is important to be aware of Visual
because it crops up in various APIs.
For example, the VisualBrush
, which
we describe in Chapter 13, lets you create a brush that
can paint using a copy of the appearance of any object derived from
Visual
.
Most types derived from Visual
derive from UIElement
, which we
describe later. One useful exception is the DrawingVisual
class. This provides a
lightweight way of hosting a drawing. We describe drawings in Chapter 13.
WPF provides a class called VisualTreeHelper
. This provides methods for
navigating the visual tree. Surprisingly, these are not defined in terms
of the Visual
class. This is because
3D elements have an independent branch of the class hierarchy, but are
still considered part of the visual tree. VisualTreeHelper
therefore works with both
Visual
and Visual3D
objects. We describe the VisualTreeHelper
class in Chapter 9.
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Media.Media3D.Visual3D
The abstract Visual3D
class
fulfills the same role for 3D content as Visual
does in 2D. All 3D elements in the
visual tree derive from Visual3D
,
which provides transformation and hit testing services.
In the current version of WPF, only one type is derived from
Visual3D
: ModelVisual3D
. As with the 2D Visual
class, you cannot usefully derive
directly from Visual3D
because the
mechanisms by which content is rendered are internal. Nonetheless, you
need to be aware of the class because it crops up in certain APIs,
including the 3D hit testing APIs. We describe 3D hit testing in Chapter 17, along with the Visual3D
and ModelVisual3D
types.
System.Objec System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Media.Visual System.Windows.UIElement
UIElement
provides access to
the rendering services implemented by Visual
by offering protected wrappers around
the relevant internal features of Visual
. UIElement
also implements input handling and
WPF’s routed event system. It provides the low-level aspects of the
layout system, although this goes only as far as basic sizing and
positioning—FrameworkElement
extends
this to provide the full layout system we describe in Chapter 3. Finally, UIElement
provides basic animation support,
although FrameworkElement
builds on
this to provide the full set of services we describe in Chapter 16.
UIElement
may seem like a
strange halfway house, providing an incomplete set of services to be
finished off by FrameworkElement
.
This structure arises because WPF is built in layers—there is a split
between the so-called core and
framework parts. The core classes
are defined in PresentationCore.dll
and the framework classes are defined in PresentationFramework.dll. This split is
designed to enable developers to use the low-level rendering and
animation services of WPF without having to use the full framework. For
example, it would technically be possible to write an HTML engine that
uses the WPF core for rendering (although no such thing has been built
at the time of this writing). That particular example would have no use
for framework-level services—HTML defines its own layout rules; the API
HTML presents to script is very different from WPF’s, so it would be
hard to map features of one to the other.
Most applications will not write classes that derive directly from
UIElement
, because it makes sense to
do so only if you wish to use your own UI framework in place of WPF’s.
This would be an unusual and radical choice, because a great deal of
what WPF has to offer (and the bulk of what this book is about) is
provided at the framework level, including layout, data binding,
styling, templates, and most of the animation system. Most ordinary
applications will therefore use UIElement
via FrameworkElement
.
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Media.Visual System.Windows.UIElement System.Windows.FrameworkElement
FrameworkElement
is the base
class of the majority of visible elements in WPF. It derives from
UIElement
, so types that derive from
FrameworkElement
are able to render
their own appearance and respond to user input. FrameworkElement
adds data binding, styling,
and resource handling, and it builds the full set of layout and
animation services on top of the primitive services provided by UIElement
.
FrameworkElement
also provides
a great deal of the infrastructure for data templates and control
templates, although you must use more specialized classes to exploit
these features. Only a Control
(or a
type derived from Control
) can have a
control template. You can use data templates either from a ContentPresenter
or from certain control
types.[130]
FrameworkElement
is arguably
WPF’s nearest equivalent to the Windows Forms Control
class. WPF has a Control
class too (which we describe later),
but its role is more specialized.
Types that derive directly from FrameworkElement
are typically all-code
affairs. Although you could create a XAML file for a FrameworkElement
with a corresponding
code-behind file, you can’t use this to construct the appearance of
the element. This is because adding child elements at this level
requires code—you need to override the GetVisualChild
method and the VisualChildrenCount
properties so that WPF
can discover and render these elements. XAML can only set properties.
However, WPF offers various derived types such as Control
, Decorator
, and Panel
, which provide the necessary code to
let you define their content in markup.
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Media.Visual System.Windows.UIElement System.Windows.FrameworkElement System.Windows.Controls.Decorator
Decorator
is the base class for
elements that contain a single child, and which either apply some kind
of effect or offer a service. For example, Border
adds an outline and optional padding
around an element. ViewBox
scales its
child to fit the space available. InkPresenter
enables ink rendering on its
child.
Despite being defined in the System.Windows.Controls
namespace, a
Decorator
is not technically a
control, as it does not derive from the Control
class. Indeed, many of the types in
the System.Windows.Controls
namespace are not controls. This namespace contains many utility types
typically used in conjunction with controls in order to build a user
interface.
Decorator
is a very simple
class—it just defines a public Child
element and performs the necessary work to ensure that the child is
added to the logical and visual trees. Its layout implementation simply
defers to the child element. You should use either this or ContentControl
when writing a custom element
that wraps a single child. See the ContentControl
section, later in this appendix, for a discussion on how to choose
between these two types.
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Media.Visual System.Windows.UIElement System.Windows.FrameworkElement System.Windows.Controls.Panel
Panel
is the abstract base
class of elements that contain and arrange multiple child elements. Each
derived type implements a particular layout strategy. For example,
StackPanel
arranges items in a single
column or a row. We describe the panel types and their layout mechanisms
in Chapter 3.
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Media.Visual System.Windows.UIElement System.Windows.FrameworkElement System.Windows.Shapes.Shape
Shape
is the abstract base
class of graphical shape elements that can be added to the UI tree. The
derived types include Rectangle
,
Ellipse
, and Path
. WPF defines two sets of classes for
working with graphical shapes: those derived from Shape
and another set derived from Geometry
. We describe both sets in Chapter 13. The distinction is that Shape
-based elements derive from FrameworkElement
and are therefore part of the
UI tree, and can use data binding, handle input, raise events, employ
styles, and participate in layout. Shape
provides properties for controlling the
fill and outline of a shape, so if you want to define your own custom
shape types, you should derive from Shape
to take advantage of these.
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Media.Visual System.Windows.UIElement System.Windows.FrameworkElement System.Windows.Controls.Control
Control
is the base class for
elements that offer a particular interactive behavior. For example, a
TextBox
allows the user to enter and
edit text; a ListBox
presents a list
of items, allowing the user to scroll through and select items.
Not all visual elements derive from Control
. Elements with no intrinsic
interactive behavior derive either directly from FrameworkElement
or from one of the other
noncontrol base classes described in this appendix.
Controls are typically visible to the user as a single coherent
interactive entity in the user interface. Elements that do not fit
this mold tend not to be controls. For example, the Grid
type is extremely useful to developers
as a means of managing layout, but it is not something directly
recognizable to a user, so it is not a control. Likewise, although a
Border
element will be visible
on-screen, it has no interactive behavior and no standard appearance.
Normal users don’t recognize Border
elements in the way that they will recognize and understand a Button
, so Border
is also not a control (but Button
is).
Control
defines a Template
property. This contains a reference
to a ControlTemplate
that defines the
appearance of the control. Most controls are lookless—they have no intrinsic appearance
and rely entirely on their templates to provide their visuals. This
further emphasizes that the role of a control is to define behavior, not
appearance. Chapter 9 describes the use of
control templates.
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
ContentControl
is a
specialization of Control
. It’s the
base class for any control that can host a single piece of content,
which goes in the Content
property.
The content can be plain text or a tree of user interface elements
(e.g., a nested control, or a panel containing several child items). The
content can also be any .NET object, in which case WPF will attempt to
display it using a data template: if you set the ContentTemplate
or ContentTemplateSelector
property, WPF will use
the template you supply, but otherwise it will attempt to locate a
template automatically. If it can’t find a template, it will display the
value returned by the object’s ToString
method. We describe data templates in
Chapter 6.
Some controls derive from ContentControl
in order to offer a caption.
For example, the various button types (e.g., Button
, CheckBox
, RadioButton
; see Chapter 5
for details) typically contain either text or graphics. By deriving from
ContentControl
, these buttons are
able to host any mixture of text and graphics.
An alternative reason to derive from ContentControl
is to offer a service wrapped
around arbitrary content. For example, ScrollViewer
can host any content, providing
scroll bars for when the content is larger than the available physical
space. This wrapping scenario may seem like the same job for which
Decorator
was designed. However,
there is one critical difference: ContentControl
derives from Control
. This means it should provide some
interactive behavior (e.g., scrolling in the case of ScrollViewer
). Also, a ContentControl
can use a ControlTemplate
to define its appearance. This
makes it possible to define a custom appearance for a ScrollViewer
, whereas you cannot replace the
visuals of a Decorator
such as a
Border
. ContentControl
is therefore the base class for
wrapper-like elements that provide a specific interactive behavior
around their content, allowing the container’s visuals to be customized,
whereas Decorator
is the base class
for lower-level wrapper elements with no particular interactive
behavior, and which either have no appearance or have a fixed
appearance.
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.HeaderedContentControl
Some controls offer two placeholders for content instead of just
one. HeaderedContentControl
supports
this by adding a Header
property
alongside the ContentControl
type’s
Content
property. This is the base
class of Expander
, GroupBox
, and TabItem
, and it enables all three controls to
host arbitrary content both in the main body of the control and as the
header.
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.UserControl
The UserControl
class is
designed to provide an easy way to build a custom control using the same
techniques and tools you would use to create a window: the appearance is
defined with markup, and the behavior is defined in a code-behind file.
This provides an easy way to build a reusable chunk of user interface.
It also offers a useful tool for managing complexity: if a window has
become too complex, it may be easier to split its content into a set of
user controls, allowing developers to work on individual pieces
independently.
UserControl
is almost identical
to its base class, ContentControl
. It
adjusts the default values for a few properties. For example, a UserControl
disables keyboard focus and tab
stop navigation for itself, although not for its content—the assumption
is that a UserControl
will typically
contain focus targets such as text boxes and buttons, so the containing
control itself will usually not need to act as a distinct focus
target.
UserControl
is not strictly
necessary for building elements through markup, composition, and code
behind. You can also use this style if you derive directly from ContentControl
, or any other element that
accepts one or more children, such as Decorator
or Grid
. The main contribution of UserControl
is to signal intent: it is clear
to any developer looking at a control derived from UserControl
that it is intended to be a chunk
of UI that forms part of a window, designed to be used as is. This makes
it clear that replacing the template is unlikely to be useful—user
controls are typically not lookless, because their appearance is defined
by their hardcoded content rather than by their template.
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.ItemsControl
ItemsControl
is the base class
for all controls that present lists or trees of items, including
ListBox
, TreeView
, and Menu
. Because it is a concrete class, it can
also be used in its own right. ItemsControl
provides item presentation and
data binding support, but does not offer item selection—this lets you
present a list of items without being forced to make them selectable.
Use the Selector
base class if you
require selection.
Chapter 5 describes how to use ItemsControl
and the various list controls
derived from it. Chapter 7 describes how
to use data binding with these controls.
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.ItemsControl System.Windows.Controls.HeaderedItemsControl
HeaderedItemsControl
derives
from ItemsControl
, so it does
everything that Control
does, but it
also adds a Header
property designed
to hold a single item of content. This works in the same way as the
Content
property of ContentControl
.
Elements derive from HeaderedItemsControl
if they need to present
both a caption and a set of children. For example, both MenuItem
and TreeViewItem
derive from this class.
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.ItemsControl System.Windows.Controls.Primitives.Selector
Selector
adds item selection
functionality to its base class, ItemsControl
. The selection management is
designed to work with a single linear list of items, so although this is
the base class of ComboBox
, ListBox
, and ListView
, it is not the base class of TreeView
. TreeView
derives directly from ItemsControl
and implements its own selection
management.
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.ContentElement
Text has distinctive layout requirements that are unlike those of
the rest of the user interface. Consequently, there is a separate part
of the class hierarchy for managing textual content. ContentElement
is at the root of this
hierarchy, and it derives directly from DependencyObject
, not Visual
.
ContentElement
is the base
class of FrameworkContentElement
,
which is the base class of all the types in WPF’s text object model. The
split between ContentElement
and
FrameworkContentElement
exists for
the same reason as the split between UIElement
and FrameworkElement
: ContentElement
is part of WPF’s core API,
whereas FrameworkContentElement
is
part of WPF’s framework API. The separation of responsibilities is
similar—ContentElement
provides basic
event handling and animation support, and FrameworkContentElement
adds data binding and
layout.
As with UIElement
, you would
derive directly from ContentElement
only if you were writing your own UI framework on top of WPF’s core
services. Most WPF applications will only use types derived from
FrameworkContentElement
.
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.ContentElement System.Windows.FrameworkContentElement
FrameworkContentElement
is the
base class of all types in WPF’s text object model. Because this class
does not derive from Visual
, elements
of this type do not generate their own appearance. Instead, a tree of
content elements is merely a description of some textual content, and it
requires some element derived from Visual
to present the content. WPF provides
four such elements. TextBlock
presents simple textual content, whereas the three flow document
readers—FlowDocumentScrollViewer
,
FlowDocumentPageViewer
, and FlowDocumentReader
—present larger bodies of
text.
Chapter 14 describes the text object model and the visual elements that can present text.
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Freezable
Many types in WPF describe features of the user interface rather
than being UI elements in their own right. For example, the various
brush types described in Chapter 13 describe how a
particular UI feature should be colored; geometries describe shapes of
graphical elements. Most of these descriptive classes derive from the
common Freezable
base class. You
would not normally derive your own types from Freezable
; it is an important class in the WPF
class hierarchy because so many important types derive from it.
One of the most important characteristics of a freezable object is that you can use it in multiple places. Example D-1 contains a single description of an ellipse shape, which is shared by three UI elements.
<StackPanel Orientation="Horizontal"> <StackPanel.Resources> <EllipseGeometry x:Key="pathDescription" RadiusX="100" RadiusY="50" Center="100,50" /> </StackPanel.Resources> <Path Data="{StaticResource pathDescription}" Fill="Green" /> <Path Data="{StaticResource pathDescription}" Fill="Cyan" /> <Path Data="{StaticResource pathDescription}" Fill="Black" /> </StackPanel>
As you can see from Figure D-2, this example has
three distinct elements—the three ellipses correspond to the three
Path
elements. This highlights the
fact that Path
is more than just a
description of a shape. The identity of a Path
object is significant—each of the three
shapes visible in the UI corresponds to exactly one of the three
Shape
objects created by Example D-1. However, all three share a single
description of the shape: they all use the same EllipseGeometry
object.
An EllipseGeometry
does not
belong to any part of the visual tree. On the contrary, this one
instance is used from multiple places in the tree. All types that derive
from Freezable
, such as geometries
and brushes, can be shared in this way.
The significance of the name Freezable is
that it is possible to freeze such
an object, preventing any further changes from occurring. By default, a
newly created Freezable
will not be
frozen—it is possible to change the object.[131] For example, you could write code that modified the
RadiusX
of the EllipseGeometry
in Example D-1. This would cause all three
ellipses in Figure D-2 to
change. WPF has to do a certain amount of tracking to enable such
changes to work correctly, and this does not come for free. If you don’t
need to be able to make such changes, you can opt out of the
corresponding costs by freezing the object.
Freezing a Freezable
in code is
simple: just call the Freeze
method.
You can also freeze objects from markup. Example D-2 shows a version of Example D-1 modified to freeze the EllipseGeometry
.
<StackPanel Orientation="Horizontal" xmlns:po="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="po"> <StackPanel.Resources> <EllipseGeometry x:Key="pathDescription" po:Freeze="True" RadiusX="100" RadiusY="50" Center="100,50" /> </StackPanel.Resources> <Path Data="{StaticResource pathDescription}" Fill="Green" /> <Path Data="{StaticResource pathDescription}" Fill="Cyan" /> <Path Data="{StaticResource pathDescription}" Fill="Black" /> </StackPanel>
Freezing a Freezable
has three
effects. First, any attempt to change a frozen object will cause an
InvalidOperationException
to be
thrown. Second, WPF will no longer keep track of the relationship
between the object and the places where it is used. This reduces the
memory and CPU consumption of your application, and it may also enable
WPF to perform some internal optimizations. Third, freezing detaches the
Freezable
from its Dispatcher
, making it possible to use the
object on a different thread than the one on which it was
created.
Because freezing frees an object from thread affinity, this can
help improve the responsiveness of an application that builds complex
visualizations. If you are building a drawing, bitmap, or 3D model that
is sufficiently complex that it takes a noticeable amount of time to
create (e.g., anything more than 0.1 seconds), you should avoid doing
this on the UI thread, because it will make the application
unresponsive. Moving such work to a worker thread is the obvious
response, but if you create objects derived from DispatcherObject
on the wrong thread, they
will be associated with the wrong dispatcher, and you will get an error
when you try to use them in the UI thread. Freezing objects avoids this
problem. You can use this technique with drawings, brushes, geometries,
the various 3D model and geometry classes, and bitmaps, because the
relevant classes, which we describe in Chapter 13 and
Chapter 17, all derive from Freezable
.
Sometimes it can be useful to obtain a modifiable copy of a frozen
Freezable
. Freezable
offers a Clone
method for this purpose. It performs a
deep copy—it copies any nested objects. For example, consider a frozen
GeometryDrawing
object with a
Brush
property referring to a frozen
SolidColorBrush
. Calling Clone
would make unfrozen copies of both the
drawing and the brush.
System.Object System.Windows.Threading.DispatcherObject System.Windows.DependencyObject System.Windows.Freezable System.Windows.Media.Animation.Animatable
Many of the Freezable
types
describe aspects of an element’s appearance such as its shape or color,
which makes them candidates for animation. Most Freezable
types therefore derive from Freezable
indirectly through the Animatable
class. This class provides WPF’s
animation system with the hooks it needs to change properties over
time.
If necessary, the animation system will call Clone
to create an unfrozen copy of a value in
order to animate the value.
Example D-2 shows the public types that
derive from Animatable
.
[130] The control types in question are ContentControl
, HeaderedContentControl
, ItemsControl
, and HeaderedItemsControl
, or any type deriving
from any of those classes. In fact, ContentPresenter
is the key in all these
cases, as these controls rely on ContentPresenter
to instantiate data
templates. ContentPresenter
derives directly from FrameworkElement
.
[131] In early previews of WPF, this type was called Changeable
. Confusingly, the old name is
just as accurate a description as Freezable
, despite seemingly having almost
the opposite meaning. To clarify: these objects start life as
changeable objects, but you can freeze them, preventing further
changes.