Freigeben über


What's an inheritance context?

I've been slacking on my blogging lately, so what better way to get back into the swing of things than to post an article I've been meaning to write for five months?  But before I tell you about inheritance contexts, I have to explain the problem it solves.  Once upon a time, property inheritance looked only at the logical tree and the visual tree -- so if an element didn't have a logical or visual parent, its properties didn't inherit values from the parent it didn't have.  Which makes sense if you think about the world in terms of code.  But if you look at the world through xaml glasses, there's a lot of places where one element looks like it has a parent when it really doesn't.  Consider the following:

    <Button>
      <Button.Background>
        <SolidColorBrush>green</SolidColorBrush>
      </Button.Background>
    </Button>

Quick, what's the parent of SolidColorBrush?  If you said Button, you would be wrong -- SolidColorBrush is not part of the visual tree (it's not a Visual).  Nor is SolidColorBrush part of the logical tree, because if you call Button.Content the answer is not the SolidColorBrush.  So SolidColorBrush in this example has no parent, so it didn't inherit property values from anyone.

At first this may seem academic -- who cares if SolidColorBrush inherits?  Actually, there's a couple reasons it matters, the DataContext property and the Loaded event.  DataContext is an inherited property that use the default data source for your {Binding} statements.  When you write:

        <SolidColorBrush Color="{Binding}"/>

Since you didn't specify a data source (and who does), it uses the DataContext property.  And if that inherits the way you expect, everything is happy.  But it's easy to write:

    <Button DataContext="whatever">
      <Button.Background>
        <SolidColorBrush Color="{Binding}"/>
      </Button.Background>
    </Button>

And be confused that your SolidColorBrush didn't inherit the DataContext.  Similarly, the Loaded event was originally tied to the logical tree, so if you put your MediaElement inside a VisualBrush, there would be a gap in the visual tree and your media would never get a Loaded event and never start play the video.

And that's why we invented inheritance context.  You can think of it as logical tree 2.0 -- inheritance context is an extra pointer, which the property engine uses when there's no logical parent or visual parent to get values from.  Inheritance context don't solve every problem in this space, but they solve a lot of them, and in the future we'll add more inheritance context pointers and solve more problems.

There's a number of places we establish inheritance context pointers, I won't try to list all of them but here are some of the more interesting ones:

Freezable inside a FrameworkElement -- our SolidColorBrush/Button sample above
FrameworkElement inside a VisualBrush
Triggers and setters

Resource dictionaries present another interesting case.  Suppose you use DynamicResource inside a resource dictionary:

  <Window.Resources>
    <SolidColorBrush Color="{DynamicResource ...}" />
  </Window.Resources>

Does that dynamic resource get evaluated where the SolidColorBrush was defined?  Or where the brush gets used?  If the latter, what happens if you use the SolidColorBrush in two different places where the DynamicResource will give two different answers?  It may sound contrived:

  <Window.Resources>
    <Color x:Key="color">red</Color>
    <SolidColorBrush x:Key="brush" Color="{DynamicResource color}" />
  </Window.Resources>

    <Button>
      <Button.Background>
        <StaticResource ResourceKey="brush"/>
      </Button.Background>
    </Button>

    <Button>
      <Button.Resources>
        <Color x:Key="color">blue</Color>
      </Button.Resources>
      <Button.Background>
        <StaticResource ResourceKey="brush"/>
      </Button.Background>
    </Button>

But it actually happens in real code.  We chose the first solution, the inheritance context for SolidColorBrush points to the resource dictionary, and not its point of use.

Inheritance context has been wonderfully useful, but we haven't put inheritance context links everywhere that's theoretically possible, mostly because of time in the day (adding inheritance context links are very difficult to implement performantly and without causing undesired behavior changes).  Probably the simplest example of where we don't have inheritance context links is across random property elements:

    <Button>
      <Button.ContextMenu>
        <ContextMenu/>
      </Button.ContextMenu>
    </Button>

ContextMenu is neither a visual nor logical child of Button, nor is it one of the inheritance context cases listed above (ContextMenu is not a Freezable).  But, with inheritance context concept at our disposal, we hope to solve that in future versions of WPF.

Comments