This blog post shows how to use the FindName method to gain access to a template-generated element within a ContentControl. Along the way we will see how to make FrameworkElement’s FindResource method return a reference to a typed DataTemplate (i.e. a DataTemplate in a resource dictionary with no key, but only a DataType value).
The FrameworkTemplate base class has a handy method on it called FindName. You can call FindName on a template, passing it the name of an element in the template and the element on which the template was applied. If the tree of elements generated by the template contains an element with the specified name, FindName returns you a reference to it. If it can’t find the element with the name you specified, it returns null.
Suppose that we are using a ContentControl to display a data object, and that data object has a DataTemplate applied to it. Also suppose that we need to programmatically interact with one of the elements in the data object’s expanded template. It might seem like a simple task: just call FindName on the DataTemplate which is applied to the data object in the ContentControl, passing in the element name and the ContentControl. However, if you try that approach you will quickly hit the following brick wall:
As you can see above, if you pass the ContentControl into FindName an InvalidOperationException is thrown and the message is “This operation is valid only on elements that have this template applied.” Suddenly this seemingly simple task just got tricky.
The problem here is that the ContentControl does not directly host the elements provided by the expanded DataTemplate. It uses a ContentPresenter to do the dirty work. If we want to use the FindName method we will have to pass that ContentPresenter as the second argument, instead of the ContentControl. The DataTemplate is applied to the ContentPresenter, not the ContentControl.
Putting the solution to work
The demo project associated with this blog post has a very simple set of functional requirements. When the user clicks on some text, it must rearrange itself, as seen below:
In this demo app we call on the trusty Foo class to be our data object:
Here is the relevant XAML of the demo app’s Window:
The DataTemplate declared in the Window’s resource dictionary is a “typed template,” meaning that it has no x:Key but only a DataType value. Typed templates are automatically applied to instances of the data type with which they are associated. In this case, all Foo objects in the Window will automatically be rendered by that DataTemplate.
The Window contains a ContentControl, whose Content is set to an instance of Foo. When the user clicks on the ContentControl the OnContentControlMouseDown event handling method in the code-behind will be invoked. That method is implemented like so:
The inline comments above should provide sufficient explanation for how the code works, so I won’t reiterate all that here. If you have any questions feel free to drop a comment on this post.
Download the demo app here: FindName with ContentControl (demo project) Be sure to change the file extension from .DOC to .ZIP and then decompress it.