Arquitetura WPF
This topic provides a guided tour of the Windows Presentation Foundation (WPF) class hierarchy. It covers most of the major subsystems of WPF, and describes how they interact. Ele detalha algumas das escolhas feitas pelos arquitetos de WPF.
Este tópico contém as seguintes seções.
- System.Object
- System.Threading.DispatcherObject
- System.Windows.DependencyObject
- System.Windows.Media.Visual
- System.Windows.UIElement
- System.Windows.FrameworkElement
- System.Windows.Controls.Control
- Summary
- Tópicos relacionados
System.Object
The primary WPF programming model is exposed through managed code. Early in the design phase of WPF there were a number of debates about where the line should be drawn between the managed components of the system and the unmanaged ones. O CLR fornece inúmeros recursos que tornam o desenvolvimento mais produtivo e mais robusta (incluindo o gerenciamento de memória, tratamento de erros, etc., common type system), mas eles têm um custo.
The major components of WPF are illustrated in the figure below. The red sections of the diagram (PresentationFramework, PresentationCore, and milcore) are the major code portions of WPF. Of these, only one is an unmanaged component – milcore. Milcore is written in unmanaged code in order to enable tight integration with DirectX. Todos os exibem em WPF é feito por meio de DirectX engine, permitindo a eficiência do hardware e software de processamento. WPFtambém necessário exercer um melhor controle sobre a memória e a execução. The composition engine in milcore is extremely performance sensitive, and required giving up many advantages of the CLR to gain performance.
A comunicação entre as partes gerenciadas e de WPF será abordada posteriormente neste tópico. The remainder of the managed programming model is described below.
System.Threading.DispatcherObject
A maioria dos objetos em WPF derivam de DispatcherObject, que fornece as construções básicas para lidar com a concorrência e threading. WPFbaseia-se em um sistema de mensagens implementado pelo dispatcher. This works much like the familiar Win32 message pump; in fact, the WPF dispatcher uses User32 messages for performing cross thread calls.
There are really two core concepts to understand when discussing concurrency in WPF – the dispatcher and thread affinity.
During the design phase of WPF, the goal was to move to a single thread of execution, but a non-thread "affinitized" model. Thread affinity happens when a component uses the identity of the executing thread to store some type of state. The most common form of this is to use the thread local store (TLS) to store state. Thread affinity requires that each logical thread of execution be owned by only one physical thread in the operating system, which can become memory intensive. In the end, WPF’s threading model was kept in sync with the existing User32 threading model of single threaded execution with thread affinity. The primary reason for this was interoperability – systems like OLE 2.0, the clipboard, and Internet Explorer all require single thread affinity (STA) execution.
Given that you have objects with STA threading, you need a way to communicate between threads, and validate that you are on the correct thread. Herein lies the role of the dispatcher. The dispatcher is a basic message dispatching system, with multiple prioritized queues. Examples of messages include raw input notifications (mouse moved), framework functions (layout), or user commands (execute this method). By deriving from DispatcherObject, you create a CLR object that has STA behavior, and will be given a pointer to a dispatcher at creation time.
System.Windows.DependencyObject
One of the primary architectural philosophies used in building WPF was a preference for properties over methods or events. Properties are declarative and allow you to more easily specify intent instead of action. This also supported a model driven, or data driven, system for displaying user interface content. This philosophy had the intended effect of creating more properties that you could bind to, in order to better control the behavior of an application.
Para ter mais de um sistema orientado por propriedades de um sistema de propriedade mais sofisticado do que o que o CLR fornece era necessária. A simple example of this richness is change notifications. In order to enable two way binding, you need both sides of the bind to support change notification. In order to have behavior tied to property values, you need to be notified when the property value changes. O Microsoft .NET Framework tem uma interface INotifyPropertyChange, que permite que um objeto publicar as notificações de alteração, no entanto, é opcional.
WPF provides a richer property system, derived from the DependencyObject type. The property system is truly a "dependency" property system in that it tracks dependencies between property expressions and automatically revalidates property values when dependencies change. For example, if you have a property that inherits (like FontSize), the system is automatically updated if the property changes on a parent of an element that inherits the value.
The foundation of the WPF property system is the concept of a property expression. In this first release of WPF, the property expression system is closed, and the expressions are all provided as part of the framework. Expressions are why the property system doesn’t have data binding, styling, or inheritance hard coded, but rather provided by later layers within the framework.
The property system also provides for sparse storage of property values. Because objects can have dozens (if not hundreds) of properties, and most of the values are in their default state (inherited, set by styles, etc.), not every instance of an object needs to have the full weight of every property defined on it.
Novo recurso do sistema propriedade final é a noção de anexado propriedades. WPFelementos baseiam-se no princípio de composição e a reutilização de componente. It is often the case that some containing element (like a Grid layout element) needs additional data on child elements to control its behavior (like the Row/Column information). Instead of associating all of these properties with every element, any object is allowed to provide property definitions for any other object. This is similar to the "expando" features of JavaScript.
System.Windows.Media.Visual
With a system defined, the next step is getting pixels drawn to the screen. O Visual classe fornece para a criação de uma árvore de objetos visuais, cada opcionalmente contendo instruções de desenho e os metadados sobre como processar essas instruções (corte, transformação, etc.). Visualfoi projetado para ser extremamente leve e flexível, para que a maioria dos recursos não ter nenhum public API exposição e dependem fortemente protegido de retorno de chamada funções.
Visualé realmente o ponto de entrada para o WPF sistema de composição. Visualé o ponto de conexão entre esses dois subsistemas, o gerenciado API e milcore não gerenciado.
WPF displays data by traversing the unmanaged data structures managed by the milcore. These structures, called composition nodes, represent a hierarchical display tree with rendering instructions at each node. This tree, illustrated on the right hand side of the figure below, is only accessible through a messaging protocol.
When programming WPF, you create Visual elements, and derived types, which internally communicate to the composition tree through this messaging protocol. Each Visual in WPF may create one, none, or several composition nodes.
There is a very important architectural detail to notice here – the entire tree of visuals and drawing instructions is cached. In graphics terms, WPF uses a retained rendering system. This enables the system to repaint at high refresh rates without the composition system blocking on callbacks to user code. This helps prevent the appearance of an unresponsive application.
Another important detail that isn’t really noticeable in the diagram is how the system actually performs composition.
In User32 and GDI, the system works on an immediate mode clipping system. When a component needs to be rendered, the system establishes a clipping bounds outside of which the component isn’t allowed to touch the pixels, and then the component is asked to paint pixels in that box. This system works very well in memory constrained systems because when something changes you only have to touch the affected component – no two components ever contribute to the color of a single pixel.
WPF uses a "painter's algorithm" painting model. This means that instead of clipping each component, each component is asked to render from the back to the front of the display. This allows each component to paint over the previous component's display. The advantage of this model is that you can have complex, partially transparent shapes. Com o hardware de gráficos modernos de hoje, esse modelo é relativamente rápido (que não era o caso quando User32 / GDI foram criadas).
Como mencionado anteriormente, uma filosofia de núcleo de WPF é mover para uma mais declarativa, "propriedade centralizada" modelo de programação. In the visual system, this shows up in a couple of interesting places.
First, if you think about the retained mode graphic system, this is really moving away from an imperative DrawLine/DrawLine type model, to a data oriented model – new Line()/new Line(). This move to data driven rendering allows complex operations on the drawing instructions to be expressed using properties. The types deriving from Drawing are effectively the object model for rendering.
Second, if you evaluate the animation system, you'll see that it is almost completely declarative. Instead of requiring a developer to compute the next location, or next color, you can express animations as a set of properties on an animation object. These animations can then express the intent of the developer or designer (move this button from here to there in 5 seconds), and the system can determine the most efficient way to accomplish that.
System.Windows.UIElement
UIElement defines core subsystems including Layout, Input, and Events.
Layout is a core concept in WPF. Em muitos sistemas, ou há um conjunto fixo de layout de modelos (HTML suporta três modelos de layout; fluxo absoluto e tabelas) ou nenhum modelo de layout (User32 o só oferece suporte ao posicionamento absoluto). WPFiniciado com a suposição de que os desenvolvedores e designers queriam um modelo de layout flexível e extensível, o que pode ser dirigido por valores de propriedade em vez de lógica imperativa. At the UIElement level, the basic contract for layout is introduced – a two phase model with Measure and Arrange passes.
Measure allows a component to determine how much size it would like to take. This is a separate phase from Arrange because there are many situations where a parent element will ask a child to measure several times to determine its optimal position and size. The fact that parent elements ask child elements to measure demonstrates another key philosophy of WPF – size to content. All controls in WPF support the ability to size to the natural size of their content. This makes localization much easier, and allows for dynamic layout of elements as things resize. The Arrange phase allows a parent to position and determine the final size of each child.
A lot of time is often spent talking about the output side of WPF – Visual and related objects. However there is a tremendous amount of innovation on the input side as well. Probably the most fundamental change in the input model for WPF is the consistent model by which input events are routed through the system.
Input originates as a signal on a kernel mode device driver and gets routed to the correct process and thread through an intricate process involving the Windows kernel and User32. Depois que a mensagem User32 correspondente à entrada é roteada para WPF, ele será convertido em um WPF brutos mensagem de entrada e enviados para o dispatcher. WPFpermite que os eventos de entrada brutos a ser convertido em vários eventos reais, permitindo que recursos como "mouseenter" para ser implementada em um nível baixo de um sistema com entrega garantida.
Each input event is converted to at least two events – a "preview" event and the actual event. All events in WPF have a notion of routing through the element tree. Events are said to "bubble" if they traverse from a target up the tree to the root, and are said to "tunnel" if that start at the root and traverse down to a target. Input preview events tunnel, enabling any element in the tree an opportunity to filter or take action on the event. The regular (non-preview) events then bubble from the target up to the root.
This split between the tunnel and bubble phase makes implementation of features like keyboard accelerators work in a consistent fashion in a composite world. In User32 you would implement keyboard accelerators by having a single global table containing all the accelerators you wanted to support (Ctrl+N mapping to "New"). In the dispatcher for your application you would call TranslateAccelerator which would sniff the input messages in User32 and determine if any matched a registered accelerator. In WPF this wouldn’t work because the system is fully "composable" – any element can handle and use any keyboard accelerator. Having this two phase model for input allows components to implement their own "TranslateAccelerator".
To take this one step further, UIElement also introduces the notion of CommandBindings. The WPF command system allows developers to define functionality in terms of a command end point – something that implements ICommand. Command bindings enable an element to define a mapping between an input gesture (Ctrl+N) and a command (New). Both the input gestures and command definitions are extensible, and can be wired together at usage time. This makes it trivial, for example, to allow an end user to customize the key bindings that they want to use within an application.
To this point in the topic, "core" features of WPF – features implemented in the PresentationCore assembly, have been the focus. When building WPF, a clean separation between foundational pieces (like the contract for layout with Measure and Arrange) and framework pieces (like the implementation of a specific layout like Grid) was the desired outcome. The goal was to provide an extensibility point low in the stack that would allow external developers to create their own frameworks if needed.
System.Windows.FrameworkElement
FrameworkElement can be looked at in two different ways. It introduces a set of policies and customizations on the subsystems introduced in lower layers of WPF. It also introduces a set of new subsystems.
A diretiva primária introduzida pelo FrameworkElement está em torno de layout do aplicativo. FrameworkElementbaseia o contrato de layout básico introduzido por UIElement e adiciona a noção de um layout "slot" Isso facilita para os autores de layout ter um conjunto consistente de propriedade orientada a semântica de layout. Properties like HorizontalAlignment, VerticalAlignment, MinWidth, and Margin (to name a few) give all components derived from FrameworkElement consistent behavior inside of layout containers.
FrameworkElement also provides easier API exposure to many features found in the core layers of WPF. Por exemplo, FrameworkElement fornece acesso direto a animação por meio de BeginStoryboard método. A Storyboard fornece uma maneira de criar um script várias animações em um conjunto de propriedades.
The two most critical things that FrameworkElement introduces are data binding and styles.
The data binding subsystem in WPF should be relatively familiar to anyone that has used Windows Forms or ASP.NET for creating an application user interface (UI). Em cada um desses sistemas, há uma maneira simples para expressar o que você deseja que uma ou mais propriedades de um determinado elemento a ser vinculado a uma parte dos dados. WPFtem suporte completo para a ligação de propriedade, a transformação e a ligação da lista.
One of the most interesting features of data binding in WPF is the introduction of data templates. Data templates allow you to declaratively specify how a piece of data should be visualized. Instead of creating a custom user interface that can be bound to data, you can instead turn the problem around and let the data determine the display that will be created.
Styling is really a lightweight form of data binding. Using styling you can bind a set of properties from a shared definition to one or more instances of an element. Styles get applied to an element either by explicit reference (by setting the Style property) or implicitly by associating a style with the CLR type of the element.
System.Windows.Controls.Control
Control’s most significant feature is templating. Se você pensar em sistema de composição do WPF como um sistema de processamento de modo retido, modelagem permite um controle descrever sua renderização de maneira parametrizada declarativa. A ControlTemplate is really nothing more than a script to create a set of child elements, with bindings to properties offered by the control.
Control provides a set of stock properties, Foreground, Background, Padding, to name a few, which template authors can then use to customize the display of a control. The implementation of a control provides a data model and interaction model. The interaction model defines a set of commands (like Close for a window) and bindings to input gestures (like clicking the red X in the upper corner of the window). The data model provides a set of properties to either customize the interaction model or customize the display (determined by the template).
This split between the data model (properties), interaction model (commands and events), and display model (templates) enables complete customization of a control’s look and behavior.
A common aspect of the data model of controls is the content model. If you look at a control like Button, you will see that it has a property named "Content" of type Object. In Windows Forms and ASP.NET, this property would typically be a string – however that limits the type of content you can put in a button. Content for a button can either be a simple string, a complex data object, or an entire element tree. In the case of a data object, the data template is used to construct a display.
Summary
WPF is designed to allow you to create dynamic, data driven presentation systems. Every part of the system is designed to create objects through property sets that drive behavior. Data binding is a fundamental part of the system, and is integrated at every layer.
Traditional applications create a display and then bind to some data. In WPF, everything about the control, every aspect of the display, is generated by some type of data binding. The text found inside a button is displayed by creating a composed control inside of the button and binding its display to the button’s content property.
When you begin developing WPF based applications, it should feel very familiar. You can set properties, use objects, and data bind in much the same way that you can using Windows Forms or ASP.NET. With a deeper investigation into the architecture of WPF, you'll find that the possibility exists for creating much richer applications that fundamentally treat data as the core driver of the application.