Appendix D. WPF Base Types

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.

WPF core types inheritance diagram
Figure D-1. WPF core types inheritance diagram

DispatcherObject

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.

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.

Visual

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.

Visual3D

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.

UIElement

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.

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.

Tip

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.

Decorator

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.

Tip

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.

Panel

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.

Shape

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.

Control

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.

Tip

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.

ContentControl

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.

HeaderedContentControl

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.

UserControl

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.

ItemsControl

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.

HeaderedItemsControl

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.

Selector

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.

ContentElement

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.

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.

Freezable

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.

Example D-1. Freezables and 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.

One geometry shared by three elements
Figure D-2. One geometry shared by three elements

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.

Example D-2. Freezing from XAML
<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.

Animatable

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.

Table D-1. Public types derived from Animatable

BitmapEffect

GuidelineSet

Rotation3D

Brush

ImageSource

TextEffect

Camera

Material

TextDecoration

DashStyle

MediaPlayer

Timeline

Drawing

Model3D

Transform

Geometry

PathFigure

Transform3D

Geometry3D

PathSegment

 

GradientStop

Pen

 


[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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset