In the course of the last month or so, several people have asked why the DataContext that they set on the form level wasn’t carried down deeper into their element tree. Since I haven’t seen any documentation going into this I’d like to go over when/why the DataContext may be explicitly set in the WPF framework.
The DataContext is an inherited property defined on FrameworkElement and on FrameworkContextElement (the latter is actually an AddOwner of the former but I’ll discuss AddOwner another day). Suffice it to say that if you set the DataContext on an element, it should/will get inherited by its descendant elements. So naturally some people will assume that if they set it on their Window/Page, that all elements within that Window/Page will get that DataContext value. Afterall they are not setting it anywhere else. Well, that is the problem. While they may not be setting it, the WPF framework does in certain situations and if you look at it you can understand why. So if anything, including within the WPF framework itself, sets the DataContext on one of the descandants of that Window/Page, then all its descandants will get its locally set DataContext instead of the one set on one of its ancestors.
Ok so when will the WPF framework actually set the DataContext? There are actually two situations that I know of in which a class in the WPF framework will set the DataContext and both relate to when it automatically generates an element for you. One is in the ItemsControl – or more accurately by the ItemContainerGenerator of an ItemsControl when a container element is generated for an element in the list. The other is in the ContentPresenter when an element is generated for its Content (e.g. based on a DataTemplate). This makes perfect sense since after all the element generated for the ItemsControl and the elements within the DataTemplate need to get access to the thing that that element is supposed to represent.
The reason this may not be noticed in normal usage is because inherited properties prefer the logical tree and therefore can “skip” over the elements that have had their DataContext set further up the visual tree between it and its logical parent. So for example, if you were to put a TextBlock into a ListBox:
At runtime, a ListBoxItem would be created to contain the TextBlock within the ListBox. The DataContext of that ListBoxItem is the TextBlock (i.e. the object it represents within the listbox). However, the DataContext of the TextBlock ends up being Foo since that is the DataContext of its logical parent (i.e. the ListBox) and would show “Foo” as its Text.
If the TextBlock were not a logical child of the listbox (e.g. if it were added to the listbox via its ItemsSource) then it would inherit the DataContext of its visual parent – which ultimately would come from the ListBoxItem.
In this case, the TextBlock would have a Text value of “System.Windows.Control.TextBlock” – the ToString of the TextBlock itself since it is the DataContext of the ListBoxItem and therefore that is its DataContext.