Untergeordnete Seiten
  • Management of UIElements and resources

  Wiki Navigation

    Loading...


 Recently Updated


 Latest Releases

 MediaPortal 1.32
            Releasenews | Download
 MediaPortal 2.5
            Releasenews | Download


Table of Contents

Management of MPF elements/theme resources in the SkinEngine

This site explains deep copying of elements, reasons for and problems with that, copy optimization (IUnmodifiableResource), disposal strategy.

Unlike WPF, in MPF we have to cope with data models which have a lifetime independent from that of the screen(s) based on them. That makes it necessary to have strategies to bind screens to models and unbind them later to avoid memory leaks. If we would not remove screen bindings from their underlaying models when the screen is closed, the model instance would reference more and more dead screens during the model lifetime, which produces either memory leaks and also performance problems because the change of model properties would still trigger all bindings for all dead screens.
Furthermore, theme resources have a much longer lifetime than the elements in a screen, so we need also a good strategy to
handle disposal of UI resources. For example we must avoid disposing referenced theme resources when a screen is disposed.

In a system based on XAML like MPF, which uses potential chaotic reference networks between different UI elements and the theme, the disposal strategy must cope with the different object lifetimes.

Handling model <-> screen references

The current solution to avoid dead references from models to screens is to use weak references. We have weak event delegate implementations to achieve that. This strategy even works if the screen is unable to unbind all its bindings.

Handling different object lifetimes

There are some basic statements which can be made:
The easiest way of handling different lifetimes of objects in an object reference network like a screen is to copy everything.
That means creating deep copies (IDeepCopyable) of each referenced resource.
Lets say we have a ResourceDictionary with our theme resources and we want to load a screen. If we just copy all theme resources which are referenced from the new screen by a StaticResource or ThemeResource markup extension, we can later just dispose the complete object reference network of the screen because we simply don't have references to the theme.
But that doesn't work well because copying everything takes much, much time. To create a simple ListView, we would

  • Deep copy the ListView Style to the ListView
  • Deep copy the ControlTemplate from the Style to the Template property
  • Instanciate the Template by deep copying its contents
  • Deep copy the ItemsPanel template
  • Deep Copy the ItemTemplate
  • Deep copy the ItemContainerStyle

And then, for each item of the ListView,

  • Again deep copy the ItemTemplate
  • Deep copy the ItemTemplate's ControlTemplate to the item's Template property
  • Instanciate the item's Template by deep copying its contents
  • The same for the ItemContainerStyle

Even for a small list, that results in some hundred milli seconds of copy work. To avoid that, we use an optimization which avoids copying of special classes like Style, FrameworkTemplate (and subclasses), Timeline (and subclasses) and ElementProvider (and subclasses). But, if those types are not copied at all, they mustn't be disposed by every using instance. Instead, we assign an owner to instances of those types. That is done via the interface IUnmodifiableResource. So if an instance of IUnmodifiableResource is defined inside a special resource container like as resource inside ResourceDictionary or as Value of a Setter, that resource container takes the ownership of that resource by assigning its Owner property. When it comes to disposal, only the owner resource container disposes its resources, no other object does that.

More detailed

The problem of object ownership

MPF/XAML provides a very flexible object and object reference model. Using StaticResource/DynamicResource, objects can almost randomly be defined and referenced from anywhere else. In WPF, the form is loaded once and when finished, the whole form with the visual tree and the code behind the UI are disposed together. In MP2, it's a bit more complex as we have a different lifetime of Theme, UI and models which brings many problems when a screen should be set up and disposed.
In MediaPortal 2, resources from the theme have a much bigger lifetime than screen resources. And both theme elements and screen elements have a different lifetime than models. That makes it necessary to differentiate between those different kinds of objects when it comes to the disposal task. E.g. we must absolutely avoid that a screen disposal disposes any theme resources.

To achieve that goal, we need to establish an ownership relation of the objects to make sure that 1) all objects are disposed correctly and 2) only objects in the same ownership domain are disposed (i.e. when a screen is disposed, theme resources and referenced models must not be disposed too).

Another problem which is directly related to that ownership problem is the problem of copying an object tree/object network.
While unmodifiable objects can be reused and thus simply referenced from different ownership domains (as long as those unmodifiable objects have a defined owner which is responsible to dispose them), "customizable" objects like the template control (class Control), which can be rendered and thus contain their own layout data for example, must be "customized" to fit their current position in the visual tree. So they need to be copied from the original Template value to a customized instance. That copy must be a deep copy to make sure all children can be customized too, off course.

Off course? No! The copy is not a full deep copy. When we want to create such a copy, we typically don't want to copy the whole object network (following all object references through the whole object network). What we typically want is a copy which contains only a subset of the original objects copied, while some references should simply point to the same instances as the source objects.
An example for that is when you create a copy of a control which points to a theme resource like a style. That reference must simply be reused in the copied resource.
Sometimes, we also want to map one source reference target to a special value, for example to replace the old LogicalParent of a copied TemplateControl by a null value or by a special new parent object, but this is only a slightly modified case.

The MP2 solution for those two problems looks like this:

  • Normally, when a distant object should be referenced (for example by using StaticResource/ThemeResource/DynamicResource or Binding   markup extensions), the referenced object is copied.
  • Some classes implement the interface IUnmodifiableResource, which make their instances fix. For example styles and control templates implement that interface.
  • Instances, whose class implements IUnmodifiableResource are able to store the reference to an explicit owner object. If that owner isn't set, those instances behave the same as other objects; they are simply copied when referenced. But if their owner is assigned (which is done when they are added to a ResourceDictionary for example), they will be reused when they are referenced.
  • A referencing parent container object (P) is only allowed to dispose a referenced child object (C) if either C isn't an instance of IUnmodifiableResource or if C's owner points to P. Any other referencing objects aren't allowed to dispose C.
  • When copying an object network, each source object is mapped to its target object via the copy engine. Each object with local data implements a method DeepCopy which is called on each copied target object. That method copies all local data from the source object by requesting the object from the target network from the copy manager. That makes the copy manager able to build a complete source to target objects map, which makes it possible to copy complete network structures, i.e. instances can be referenced by multiple references.
  • When copying a source object, some objects enjoy a special treatment. For example some SlimDX objects don't implement the IDeepCopyable interface. They are copied by a special MPF copy code. Other objects like the Screen are always mapped to the original object because the copy of object structures NEVER leave the borders of the current screen.

Common problems which are avoided by that strategy:

  • Objects are disposed multiple times (avoided by explicit owner declaration)
  • Copy task escalates to the whole object network (avoided by explicitly mapping the source object's LogicalParent to null)
  • "Personalized" objects are referenced (and customized) by multiple source objects (avoided by copying each referenced, potential
      modifiable object)
  • The need to copy object networks again and again (mitigated by explicit owner declaration)

   

 

This page has no comments.