I had a meeting at work today where I was asked to explain a bit about Avalons
multiple trees, events, property system, resources, styles, etc... I didn't quite
get to all of it, but I thought I'd share some of my explanation here...
Pre-reading... The Avalon rearchitecture Part
1, Part
2, and Part
3.
Trees
Avalon has One Tree, right? Well, that was what I claimed in a previous
post... Of course, by now you should know that I firmly believe in the saying
that "facts obscure the truth". From the view of the Avalon rearchitecture we did
go down to one tree, however reality is never that clean.
Avalon as a "visual tree". That tree is a tree of objects that derive from MSAvalon.Windows.Media.Visual.
Visual can be conceptually thought of as a rendering surface. In fact there are several
types of visuals (2d rendering, 3d rendering, video, etc.). The base element class
- MSAvalon.Windows.UiElement - derives from Visual also. Thus, the "visual tree" is
a tree of visuals, some of which are also elements.
<LowLevelDetail BoredomFactor="High">
The "visual tree" is actually not really the display tree. The managed visual
objects are compiled (converted, rendered, whatever you want to call it) into a "composition
tree". This is built up of unmanaged "comp node" objects that are what we use for
low level rendering and animation. It is these comp nodes that can animate and render
at display refresh speed (yes, that means updating 60Hz+). No user code (or managed
code) ever makes it to the composition tree - this is critical to maintain glitch
free video and animation (not because of the GC or anything... the design of the composition
tree could be an entire post...)... Communication between the composition tree and
the visual tree is done through two one way message pipelines - one inbound, one outbound.
</LowLevelDetail>
Within this visual tree we needed to splice in things like a <Bold> or <Paragraph>
- which can't be simply represented as a single visual (imagine a bold or paragraph
that splits multiple pages). To facilitate this, we created a notion of the "logical
tree" - that is a parent pointer (and children) that are not associated always with
the visual hierarchy, but can have non-visual nodes in the tree.
Events
Events come in three flavors - preview events, bubbling events, and simple events.
Preview events (like PreviewKeyDown) travel down the tree from the root to the target
element. Imagine if you press the key "A" inside of a textbox. The window at the root
of the tree will first get the PreviewKeyDown event, followed by each element that
is a parent of the textbox, until eventually the preview event makes it to the textbox.
Bubbling events (like KeyDown) travel from the target up to the root element. Again,
imagine the textbox and your press "A". In this case the textbox gets the KeyDown
event first, followed by each of the parents of the textbox, until eventually the
event makes it all the way to the window.
Simple events (like TextChanged) only are raised on the target element. Almost all
the property changed events are simple events (also called "Direct only" events).
Events are dealt with in two stages - build route, and invoke. Build route basically
traces the path from the target element to the root of the tree. Then, during invoke,
each element in the route is invoked with the event - either starting at the begining
of the route or the end. The route will include elements from both the logical and
visual tree - thus, you can handle the TextBox PreviewKeyDown event in the Bold element
that is wrapped around it!
Properties
The Avalon property system - called the dependency property system - was originally
designed as a way for our engineers to reuse code. We found that all of our properties
wanted to share a great deal of features; inheritence, styling, change notification,
attached storage, data binding, validation, etc. We didn't want to duplicate all the
implementation of this for every property, so we had to find a way to make this work
consistently across the platform. In addition, we didn't want the consumers of these
components to have to learn a new concept, so we knew that we needed to leverage the
existing CLR property system.
Dependency properties are defined with a token - the DependencyProperty - that uniquely
identifies the property and lets the definer specify the various services that they
want associated with that property. The definer then implements a CLR accessor for
easy use. Thus, when you say "button1.Backgorund = Brushes.Red;" the dependency property
system is actually invoked, and allowed to provide the implementation of that property.
This is a much more abreviated version of the discussion, but probably long enough
to bore you all...