I am working on a project that builds a lot of the visual structure using code. I am attempting to leverage XAML to layout the visuals, and use my code to” pull together” the visual elements in response to user choices. In addition, I am seeking to do this by actually assigning ViewModel objects to ViewModel properties to which a ContentControl has been previously bound, with the expectation that the framework can look-up the DataTemplate for the ViewModel object and apply the visuals.
In this example VS2010 project, I am seeking to add a TabControl to a Grid using a command. When a Red border appears inside the Blue border, the command is working. In the example project, the NewTabControlCommand does not work and NewTabControlViaContentControlCommand does work. Note, for simplicity of this example, the NewTabControlViaContentControlCommand approach violates some MVVM principles as shown, but could be made to be a good MVVM citizen by use of a ViewService. I have a blog entry here from a presentation that I gave about how and when to use a ViewService.
The complexity of seeking to “feed” the WPF framework a DataType and expecting it to look-up the visuals comes from the fact that not all properties provide the same behavior.
Lets use this TabControl ItemsControl as an example:
The ItemsSource property will happily retrieve the ObservableCollection<TabItemViewModel> from the TabItemViewModels property, it will see that they are not a Visual type, and proceed to look-up the visuals in the Resource tree. It will then render the visuals, add them to the tree, and use the TabItemViewModel as the DataContext. The looked-up DataTemplate wil be used to draw the visual content of the tab use by the user to select the TabItem. Very good.
However, the ContentTemplate must receive a DataTemplate. Even if you provide a ViewModel type which could be resolved to a DataTemplate in the Resource tree, it will not work. Moreover, ContentTemplate on the TabControl does not use the TabControl’s DataContext. Rather, it too will receive the DataContext of each TabItem given by ItemSource, and therefore, any bindings in your DataTemplate needs to expect that the DataContext will be a TabItem, not a TabControl.
Also, I talk more about this in a lengthy post on StackOverflow, but I want to at least mention it here as well. It is invalid to set a DataContext directly to a ViewModel. Similar to the ContentTemplate example above, this will not lookup the visuals in the Resource tree and give you the visuals with the ViewModel as the DataContext. It would be nice if at least the <ContentControl> and <ItemsControl> offered this type behavior, but they do not.
Lastly, it is important to note that your Bindings must resolve when you initiate the control. You can change the values later, which is of course the point of setting up the Binding, but the Binding must resolve upon initiation. For example, I was seeking to point some CommandParameters at binding targets that I knew would exist (after being created by code) when the user clicked the button that triggered the Command, but which would not exist when the Button providing the Command was initiated. The CommandParameter did not detect that the binding contained a valid value at “click-time” – presumably because it failed to ever get established. I believe this is true for both looking up an element that does not exist by using the ElementName binding in XAML, as well as if you bind to a property in the DataContext that returns null upon the initial binding. Subsequently setting the property – even if you implement INotifyPropertyChange – will not establish/refresh the binding.