I didn't find it offensive - I just got the impression that you had slightly misjudged where I was coming from. Sorry if my reply came across as aggressive!
For (1), that's fine for some contexts, but doesn't really help in the context of the problem I'm trying to solve. It's not the presence of the template the I'm checking for. (I have another test that does that. So the suggestion is helpful in a broader sense - that test works in exactly the way you suggest. But it doesn't solve the problem I'm dealing with here.) I'm specifically testing for how the view responds to user input. The relationship between the control and its template is an important part of this, because the template has an impact on how the control receives user input - events arrive in the template and bubble up into the control, so if you don't have both parts present, you've not got a representative test of user input handling. For some tests you can separate these two aspects, but for other tests, the combination of control and template is important.
I have a simple test that works just fine, where I fire the event directly into the ContentControl itself. That works, but my main problem with that is that it's not representative of how events are really raised. So while it does a basic plumbing sanity check, it's not a very good test in that it's not a very accurate reflection of how it'll be used in practice.
I would prefer to have a test that is as much like real user input as possible. And in reality, mouse input will initially arrive at the elements in the visual tree formed by the control's template, and will bubble up to the ContentControl itself.
Besides wanting my test to reflect as accurately the way in which the class will be used in reality, I have a couple of other reasons for wanting to check this Control+ContentTemplate scenario. One is to make sure that mouse coordinate handling works correctly. (It's fairly easy to envisage a bug where the view reports mouse positions relative to the wrong UI element.) Another is to make sure that the view correctly conforms to the protocol by which the viewmodel expects view elements to be identified. (Without going into too much detail, the viewmodel needs to be able to work out the correspondance between incoming events and objects in the model. It'd be fairly easy to introduce a bug where the view sends what the viewmodel expects when the view is handling events that were fired directly at the controls it created, but not when the events came in via children of that control generated by the template.)
On (2), I'm doing this already in some tests. I use the LoadContent method to instantiate the template when I want to test aspects that are purely part of the template. But the issue that led to my original post here is that I need to test the interaction between the template and is containing control. So I really do need to force the template to be instantiated in situ. (Either that, or I need to create a test environment which completely and accurately simulates everything that WPF would do when instantiating the template for real. That seems hard, particularly since I don't think there's enough documentation to do it correctly, and I'm not sure it'd be that valuable. While it would remove the need to solve the problem I've posed, it seems to replace it with a set of much harder problems; I think the hack I've shown above is probably preferable, and certainly easier.)
On (3), that's not how templates work. The ContentTemplate is of type DataTemplate. You can't inject an instance of the template class into the view just by setting the ContentTemplate property. (And likewise with control templates, you can't inject an instance of the control's template class into the view by setting the Template property.) All you're doing there is telling it which template you'd like it to instantiate when the time comes to instantiate it. You still need to do something to get the control to use that Template or ContentTemplate property. (That's the very thing I'm asking how to do!)
Also, there isn't actually a specific template class for one thing - a content template is an instance of the DataTemplate class. So although WPF templates feel a bit like classes - a reusable prototype that gets instantiated in each place it is used - the implementation is actually very different. A template is an object, not a class. It's an instance of some type derived from FrameworkTemplate. (DataTemplate for data templates, ControlTemplate for control templates, and ItemsPanelTemplate for the template for an ItemsControl's item container panel.) Its VisualTree property contains a FrameworkElementFactory, which contains instruction on how to build an instance of the template.
So it's basically a factory object. And there isn't a distinct type associated with any particular template. If I have three data templates in my project, they all have the same type (DataTemplate). So that's three distinct templates by only one type. So there is no distinct "template class" corresponding to a particular template.
And while you can use LoadContent to instantiate a template (i.e. build a visual tree as described by the template's VisualTree property), this is not actually the same thing as using the template in context. If you get WPF to apply the template for you, the behaviour is typically more complex and subtle than simply calling LoadContent.
For example, if you provide a DataTemplate for a TreeView, WPF will not just instantiate your template. It will also wrap it in a TreeViewItem container for you. And it will set the DataContext for you. And then there's the matter of triggers - I don't even know how those are wired up internally. And it probably does other things I'm not aware of.
So if you try to instantiate the thing manually in a test where the relationship between the template instance and the templated control matters to you, you've got the problem of working out how to duplicate all of WPF's behaviour in that context. Which is why I'd much rather just let WPF instantiate the thing for me in the context in which it's meant to be used: that way I know that I'll be getting all the normal behaviour WPF offers because WPF is doing the work for me.
So that's why I'm trying to work out what the easiest way of getting WPF to instantiate the thing for me in situ is. |