달력

1

« 2025/1 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
2016. 11. 1. 19:11

Element Tree and Serialization 프로그래밍/WPF2016. 11. 1. 19:11

Element Tree and Serialization


https://msdn.microsoft.com/ko-kr/library/ms750605(v=vs.110).aspx

https://msdn.microsoft.com/en-us/library/ms750605(v=vs.110).aspx



Trees in WPF


In many technologies, elements and components are organized in a tree structure where developers directly manipulate the object nodes in the tree to affect the rendering or behavior of an application. Windows Presentation Foundation (WPF) also uses several tree structure metaphors to define relationships between program elements. For the most part WPF developers can create an application in code or define portions of the application in XAML while thinking conceptually about the object tree metaphor, but will be calling specific API or using specific markup to do so rather than some general object tree manipulation API such as you might use in XML DOM. WPF exposes two helper classes that provide a tree metaphor view, LogicalTreeHelper and VisualTreeHelper. The terms visual tree and logical tree are also used in the WPF documentation because these same trees are useful for understanding the behavior of certain key WPF features. This topic defines what the visual tree and logical tree represent, discusses how such trees relate to an overall object tree concept, and introduces LogicalTreeHelper and VisualTreeHelpers

Trees in WPF

The most complete tree structure in WPF is the object tree. If you define an application page in XAML and then load the XAML, the tree structure is created based on the nesting relationships of the elements in the markup. If you define an application or a portion of the application in code, then the tree structure is created based on how you assign property values for properties that implement the content model for a given object. In Windows Presentation Foundation (WPF), there are two ways that the complete object tree is conceptualized and can be reported to its public API: as the logical tree and as the visual tree. The distinctions between logical tree and visual tree are not always necessarily important, but they can occasionally cause issues with certain WPF subsystems and affect choices you make in markup or code.

Even though you do not always manipulate either the logical tree or the visual tree directly, understanding the concepts of how the trees interact is useful for understanding WPF as a technology. Thinking of WPF as a tree metaphor of some kind is also crucial to understanding how property inheritance and event routing work in WPF.

System_CAPS_noteNote

Because the object tree is more of a concept than an actual API, another way to think of the concept is as an object graph. In practice, there are relationships between objects at run time where the tree metaphor will break down. Nevertheless, particularly with XAML-defined UI, the tree metaphor is relevant enough that most WPF documentation will use the term object tree when referencing this general concept.

The Logical Tree

In WPF, you add content to UI elements by setting properties of the objects that back those elements. For example, you add items to a ListBoxcontrol by manipulating its Items property. By doing this, you are placing items into the ItemCollection that is the Items property value. Similarly, to add objects to a DockPanel, you manipulate its Children property value. Here, you are adding objects to the UIElementCollection. For a code example, see Add an Element Dynamically.

In Extensible Application Markup Language (XAML), when you place list items in a ListBox or controls or other UI elements in a DockPanel, you also use the Items and Children properties, either explicitly or implicitly, as in the following example.

<DockPanel
  Name="ParentElement"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  >
  <!--implicit: <DockPanel.Children>-->
  <ListBox DockPanel.Dock="Top">
    <!--implicit: <ListBox.Items>-->
    <ListBoxItem>
      <TextBlock>Dog</TextBlock>
    </ListBoxItem>
    <ListBoxItem>
      <TextBlock>Cat</TextBlock>
    </ListBoxItem>
    <ListBoxItem>
      <TextBlock>Fish</TextBlock>
    </ListBoxItem>
  <!--implicit: </ListBox.Items>-->
  </ListBox>
  <Button Height="20" Width="100" DockPanel.Dock="Top">Buy a Pet</Button>
  <!--implicit: </DockPanel.Children>-->
</DockPanel>

If you were to process this XAML as XML under a document object model, and if you had included the tags commented out as implicit (which would have been legal), then the resulting XML DOM tree would have included elements for <ListBox.Items> and the other implicit items. But XAML does not process that way when you read the markup and write to objects, the resulting object graph does not literally include ListBox.Items. It does however have a ListBox property named Items that contains a ItemCollection, and that ItemCollection is initialized but empty when the ListBox XAML is processed. Then, each child object element that exists as content for the ListBox is added to the ItemCollection by parser calls to ItemCollection.Add. This example of processing XAML into an object tree is so far seemingly an example where the created object tree is basically the logical tree.

However, the logical tree is not the entire object graph that exists for your application UI at run time, even with the XAML implicit syntax items factored out. The main reason for this is visuals and templates. For example, consider the Button. The logical tree reports the Button object and also its string Content. But there is more to this button in the run-time object tree. In particular, the button only appears on screen the way it does because a specific Button control template was applied. The visuals that come from an applied template (such as the template-defined Border of dark gray around the visual button) are not reported in the logical tree, even if you are looking at the logical tree during run time (such as handling an input event from the visible UI and then reading the logical tree). To find the template visuals, you would instead need to examine the visual tree.

For more information about how XAML syntax maps to the created object graph, and implicit syntax in XAML, see XAML Syntax In Detail or XAML Overview (WPF)

The Purpose of the Logical Tree

The logical tree exists so that content models can readily iterate over their possible child objects, and so that content models can be extensible. Also, the logical tree provides a framework for certain notifications, such as when all objects in the logical tree are loaded. Basically, the logical tree is an approximation of a run time object graph at the framework level, which excludes visuals, but is adequate for many querying operations against your own run time application's composition.

In addition, both static and dynamic resource references are resolved by looking upwards through the logical tree for Resources collections on the initial requesting object, and then continuing up the logical tree and checking each FrameworkElement (or FrameworkContentElement) for another Resources value that contains a ResourceDictionary, possibly containing that key. The logical tree is used for resource lookup when both the logical tree and the visual tree are present. For more information on resource dictionaries and lookup, see XAML Resources.

Composition of the Logical Tree

The logical tree is defined at the WPF framework-level, which means that the WPF base element that is most relevant for logical tree operations is either FrameworkElement or FrameworkContentElement. However, as you can see if you actually use the LogicalTreeHelper API, the logical tree sometimes contains nodes that are not either FrameworkElement or FrameworkContentElement. For instance, the logical tree reports the Textvalue of a TextBlock, which is a string.

Overriding the Logical Tree

Advanced control authors can override the logical tree by overriding several APIs that define how a general object or content model adds or removes objects within the logical tree. For an example of how to override the logical tree, see How to: Override the Logical Tree.

Property Value Inheritance

Property value inheritance operates through a hybrid tree. The actual metadata that contains the Inherits property that enables property inheritance is the WPF framework-level FrameworkPropertyMetadata class. Therefore, both the parent that holds the original value and the child object that inherits that value must both be FrameworkElement or FrameworkContentElement, and they must both be part of some logical tree. However, for existing WPF properties that support property inheritance, property value inheritance is able to perpetuate through an intervening object that is not in the logical tree. Mainly this is relevant for having template elements use any inherited property values set either on the instance that is templated, or at still higher levels of page-level composition and therefore higher in the logical tree. In order for property value inheritance to work consistently across such a boundary, the inheriting property must be registered as an attached property, and you should follow this pattern if you intend to define a custom dependency property with property inheritance behavior. The exact tree used for property inheritance cannot be entirely anticipated by a helper class utility method, even at run time. For more information, see Property Value Inheritance.

The Visual Tree

In addition to the concept of the logical tree, there is also the concept of the visual tree in WPF. The visual tree describes the structure of visual objects, as represented by the Visual base class. When you write a template for a control, you are defining or redefining the visual tree that applies for that control. The visual tree is also of interest to developers who want lower-level control over drawing for performance and optimization reasons. One exposure of the visual tree as part of conventional WPF application programming is that event routes for a routed event mostly travel along the visual tree, not the logical tree. This subtlety of routed event behavior might not be immediately apparent unless you are a control author. Routing events through the visual tree enables controls that implement composition at the visual level to handle events or create event setters.

Trees, Content Elements, and Content Hosts

Content elements (classes that derive from ContentElement) are not part of the visual tree; they do not inherit from Visual and do not have a visual representation. In order to appear in a UI at all, a ContentElement must be hosted in a content host that is both a Visual and a logical tree participant. Usually such an object is a FrameworkElement. You can conceptualize that the content host is somewhat like a "browser" for the content and chooses how to display that content within the screen region that the host controls. When the content is hosted, the content can be made a participant in certain tree processes that are normally associated with the visual tree. Generally, the FrameworkElement host class includes implementation code that adds any hosted ContentElement to the event route through subnodes of the content logical tree, even though the hosted content is not part of the true visual tree. This is necessary so that a ContentElement can source a routed event that routes to any element other than itself.

Tree Traversal

The LogicalTreeHelper class provides the GetChildrenGetParent, and FindLogicalNode methods for logical tree traversal. In most cases, you should not have to traverse the logical tree of existing controls, because these controls almost always expose their logical child elements as a dedicated collection property that supports collection access such as Add, an indexer, and so on. Tree traversal is mainly a scenario that is used by control authors who choose not to derive from intended control patterns such as ItemsControl or Panel where collection properties are already defined, and who intend to provide their own collection property support.

The visual tree also supports a helper class for visual tree traversal, VisualTreeHelper. The visual tree is not exposed as conveniently through control-specific properties, so the VisualTreeHelper class is the recommended way to traverse the visual tree if that is necessary for your programming scenario. For more information, see WPF Graphics Rendering Overview.

System_CAPS_noteNote

Sometimes it is necessary to examine the visual tree of an applied template. You should be careful when using this technique. Even if you are traversing a visual tree for a control where you define the template, consumers of your control can always change the template by setting the Template property on instances, and even the end user can influence the applied template by changing the system theme.

Routes for Routed Events as a "Tree"

As mentioned before, the route of any given routed event travels along a single and predetermined path of a tree that is a hybrid of the visual and logical tree representations. The event route can travel either in the up or down directions within the tree depending on whether it is a tunneling or bubbling routed event. The event route concept does not have a directly supporting helper class that could be used to "walk" the event route independently of raising an event that actually routes. There is a class that represents the route, EventRoute, but the methods of that class are generally for internal use only.

Resource Dictionaries and Trees

Resource dictionary lookup for all Resources defined in a page traverses basically the logical tree. Objects that are not in the logical tree can reference keyed resources, but the resource lookup sequence begins at the point where that object is connected to the logical tree. In WPF, only logical tree nodes can have a Resources property that contains a ResourceDictionary, therefore there is no benefit in traversing the visual tree looking for keyed resources from a ResourceDictionary.

However, resource lookup can also extend beyond the immediate logical tree. For application markup, the resource lookup can then continue onward to application-level resource dictionaries and then to theme support and system values that are referenced as static properties or keys. Themes themselves can also reference system values outside of the theme logical tree if the resource references are dynamic. For more information on resource dictionaries and the lookup logic, see XAML Resources.




Serialization Limitations of XamlWriter.Save


The API Save can be used to serialize the contents of a Windows Presentation Foundation (WPF) application as a Extensible Application Markup Language (XAML) file. However, there are some notable limitations in exactly what is serialized. These limitations and some general considerations are documented in this topic.

Run-Time, Not Design-Time Representation

The basic philosophy of what is serialized by a call to Save is that the result will be a representation of the object being serialized, at run-time. Many design-time properties of the original XAML file may already be optimized or lost by the time that the XAML is loaded as in-memory objects, and are not preserved when you call Save to serialize. The serialized result is an effective representation of the constructed logical tree of the application, but not necessarily of the original XAML that produced it. These issues make it extremely difficult to use the Save serialization as part of an extensive XAML design surface.

Serialization is Self-Contained

The serialized output of Save is self-contained; everything that is serialized is contained inside a XAML single page, with a single root element, and no external references other than URIs. For instance, if your page referenced resources from application resources, these will appear as if they were a component of the page being serialized.

Extension References are Dereferenced

Common references to objects made by various markup extension formats, such as StaticResource or Binding, will be dereferenced by the serialization process. These were already dereferenced at the time that in-memory objects were created by the application runtime, and the Savelogic does not revisit the original XAML to restore such references to the serialized output. This potentially freezes any databound or resource obtained value to be the value last used by the run-time representation, with only limited or indirect ability to distinguish such a value from any other value set locally. Images are also serialized as object references to images as they exist in the project, rather than as original source references, losing whatever filename or URI was originally referenced. Even resources declared within the same page are seen serialized into the point where they were referenced, rather than being preserved as a key of a resource collection.

Event Handling is Not Preserved

When event handlers that are added through XAML are serialized, they are not preserved. XAML without code-behind (and also without the related x:Code mechanism) has no way of serializing runtime procedural logic. Because serialization is self-contained and limited to the logical tree, there is no facility for storing the event handlers. As a result, event handler attributes, both the attribute itself and the string value that names the handler, are removed from the output XAML.

Realistic Scenarios for Use of XAMLWriter.Save

While the limitations listed here are fairly substantial, there are still several appropriate scenarios for using Save for serialization.

  • Vector or graphical output: The output of the rendered area can be used to reproduce the same vector or graphics when reloaded.

  • Rich text and flow documents: Text and all element formatting and element containment within it is preserved in the output. This can be useful for mechanisms that approximate a clipboard functionality.

  • Preserving business object data: If you have stored data in custom elements, such as XML data, so long as your business objects follow basic XAML rules such as providing custom constructors and conversion for by-reference property values, these business objects can be perpetuated through serialization.




Initialization for Object Elements Not in an Object Tree


Some aspects of Windows Presentation Foundation (WPF) initialization are deferred to processes that typically rely on that element being connected to either the logical tree or visual tree. This topic describes the steps that may be necessary in order to initialize an element that is not connected to either tree.

Elements and the Logical Tree

When you create an instance of a Windows Presentation Foundation (WPF) class in code, you should be aware that several aspects of object initialization for a Windows Presentation Foundation (WPF) class are deliberately not a part of the code that is executed when calling the class constructor. Particularly for a control class, most of the visual representation of that control is not defined by the constructor. Instead, the visual representation is defined by the control's template. The template potentially comes from a variety of sources, but most often the template is obtained from theme styles. Templates are effectively late-binding; the necessary template is not attached to the control in question until the control is ready for layout. And the control is not ready for layout until it is attached to a logical tree that connects to a rendering surface at the root. It is that root-level element that initiates the rendering of all of its child elements as defined in the logical tree.

The visual tree also participates in this process. Elements that are part of the visual tree through the templates are also not fully instantiated until connected.

The consequences of this behavior are that certain operations that rely on the completed visual characteristics of an element require additional steps. An example is if you are attempting to get the visual characteristics of a class that was constructed but not yet attached to a tree. For instance, if you want to call Render on a RenderTargetBitmap and the visual you are passing is an element not connected to a tree, that element is not visually complete until additional initialization steps are completed.

Using BeginInit and EndInit to Initialize the Element

Various classes in WPF implement the ISupportInitialize interface. You use the BeginInit and EndInit methods of the interface to denote a region in your code that contains initialization steps (such as setting property values that affect rendering). After EndInit is called in the sequence, the layout system can process the element and start looking for an implicit style.

If the element you are setting properties on is a FrameworkElement or FrameworkContentElement derived class, then you can call the class versions of BeginInit and EndInit rather than casting to ISupportInitialize.

Sample Code

The following example is sample code for a console application that uses rendering APIs and XamlReader.Load(Stream) of a loose XAML file to illustrate the proper placement of BeginInit and EndInit around other API calls that adjust properties that affect rendering.

The example illustrates the main function only. The functions Rasterize and Save (not shown) are utility functions that take care of image processing and IO.

[STAThread]
static void Main(string[] args)
{
    UIElement e;
    string file = Directory.GetCurrentDirectory() + "\\starting.xaml";
    using (Stream stream = File.Open(file, FileMode.Open))
    {
        // loading files from current directory, project settings take care of copying the file
        ParserContext pc = new ParserContext();
        pc.BaseUri = new Uri(file, UriKind.Absolute);
        e = (UIElement)XamlReader.Load(stream, pc);
    }

    Size paperSize = new Size(8.5 * 96, 11 * 96);
    e.Measure(paperSize);
    e.Arrange(new Rect(paperSize));
    e.UpdateLayout();

    /*
     *   Render effect at normal dpi, indicator is the original RED rectangle
     */
    RenderTargetBitmap image1 = Rasterize(e, paperSize.Width, paperSize.Height, 96, 96);
    Save(image1, "render1.png");

    Button b = new Button();
    b.BeginInit();
    b.Background = Brushes.Blue;
    b.Width = b.Height = 200;
    b.EndInit();
    b.Measure(paperSize);
    b.Arrange(new Rect(paperSize));
    b.UpdateLayout();

    // now render the altered version, with the element built up and initialized

    RenderTargetBitmap image2 = Rasterize(b, paperSize.Width, paperSize.Height, 96, 96);
    Save(image2, "render2.png");
}




How-to Topics


How to: Find an Element by Its Name


This example describes how to use the FindName method to find an element by its Name value.

Example

In this example, the method to find a particular element by its name is written as the event handler of a button. stackPanel is the Name of the root FrameworkElement being searched, and the example method then visually indicates the found element by casting it as TextBlock and changing one of the TextBlock visible UI properties.

void Find(object sender, RoutedEventArgs e)
{
    object wantedNode = stackPanel.FindName("dog");
    if (wantedNode is TextBlock)
    {
        // Following executed if Text element was found.
        TextBlock wantedChild = wantedNode as TextBlock;
        wantedChild.Foreground = Brushes.Blue;
    }
}




How to: Override the Logical Tree


Although it is not necessary in most cases, advanced control authors have the option to override the logical tree.

Example

This example describes how to subclass StackPanel to override the logical tree, in this case to enforce a behavior that the panel may only have and will only render a single child element. This isn't necessarily a practically desirable behavior, but is shown here as a means of illustrating the scenario for overriding an element's normal logical tree.

public class SingletonPanel : StackPanel
{
    //private UIElementCollection _children; 
    private FrameworkElement _child;

    public SingletonPanel() {

    }

    public FrameworkElement SingleChild
    {

        get { return _child;}
        set
        {
            if (value==null) {
                 RemoveLogicalChild(_child);
            } else {             
                 if (_child==null) {
                     _child = value;
                 } else {
                     // raise an exception?
                     MessageBox.Show("Needs to be a single element");
                 }
            }
        } 
    }
    public void SetSingleChild(object child)
    {
        this.AddLogicalChild(child);
    }

    public new void AddLogicalChild(object child)
    {
        _child = (FrameworkElement)child;
        if (this.Children.Count == 1)
        {
            this.RemoveLogicalChild(this.Children[0]);
            this.Children.Add((UIElement)child);
        }
        else
        {
            this.Children.Add((UIElement)child);
        }
    }

    public new void RemoveLogicalChild(object child)

    {
        _child = null;
        this.Children.Clear();
    }
    protected override IEnumerator LogicalChildren
    {
       get {
       // cheat, make a list with one member and return the enumerator
       ArrayList _list = new ArrayList();
       _list.Add(_child);
       return (IEnumerator) _list.GetEnumerator();}
    }
}

For more information on the logical tree, see Trees in WPF.














'프로그래밍 > WPF' 카테고리의 다른 글

Resources  (0) 2016.11.01
Events  (0) 2016.11.01
Base Elements (기본 요소)  (0) 2016.10.24
종속성 속성(Dependency Properties)  (0) 2016.10.24
WPF 고급 - 끌어서 놓기 (Drag and Drop)  (0) 2016.10.21
:
Posted by 지훈2