WPF TabControl and DataTemplates - wpf

I've got a set of ViewModels that I'm binding to the ItemsSource property of a TabControl. Let's call those ViewModels AViewModel, BViewModel, and CViewModel. Each one of those needs to have a different ItemTemplate (for the header; because they each need to show a different icon) and a different ContentTemplate (because they have very different interaction models).
What I'd like is something like this:
Defined in Resource.xaml files somewhere:
<DataTemplate x:Key="ItemTemplate" DataType="{x:Type AViewModel}">
...
</DataTemplate>
<DataTemplate x:Key="ItemTemplate" DataType="{x:Type BViewModel}">
...
</DataTemplate>
<DataTemplate x:Key="ItemTemplate" DataType="{x:Type CViewModel}">
...
</DataTemplate>
<DataTemplate x:Key="ContentTemplate" DataType="{x:Type AViewModel}">
...
</DataTemplate>
<DataTemplate x:Key="ContentTemplate" DataType="{x:Type BViewModel}">
...
</DataTemplate>
<DataTemplate x:Key="ContentTemplate" DataType="{x:Type CViewModel}">
...
</DataTemplate>
Defined separately:
<TabControl ItemTemplate="[ Some way to select "ItemTemplate" based on the type ]"
ContentTemplate="[ Some way to select "ContentTemplate" based on the type ]"/>
Now, I know that realistically, each time I define a DataTemplate with the same key the system is just going to complain. But, is there something I can do that's similar to this that will let me put a DataTemplate into a TabControl based on a name and a DataType?

The easiest way would be to use the automatic template system, by including the DataTemplates in the resources of a ContentControl. The scope of the templates are limited to the element they reside within!
<TabControl ItemsSource="{Binding TabViewModels}">
<TabControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type AViewModel}">
...
</DataTemplate>
<DataTemplate DataType="{x:Type BViewModel}">
...
</DataTemplate>
<DataTemplate DataType="{x:Type CViewModel}">
...
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.Resources>
<DataTemplate DataType="{x:Type AViewModel}">
...
</DataTemplate>
<DataTemplate DataType="{x:Type BViewModel}">
...
</DataTemplate>
<DataTemplate DataType="{x:Type CViewModel}">
...
</DataTemplate>
</TabControl.Resources>
</TabControl>

You can remove the x:Key :) This will automatically apply the template when the given type is encountered (probably one of the most powerful and underused features of WPF, imo.
This Dr. WPF article goes over DataTemplates pretty well. The section you'll want to pay attention to is "Defining a Default Template for a Given CLR Data Type".
http://www.drwpf.com/blog/Home/tabid/36/EntryID/24/Default.aspx
If this doesn't help your situation, you might be able to do something close to what you are looking for using a Style (ItemContainerStyle) and setting the content and header based on the type using a data trigger.
The sample below hinges on your ViewModel having a property called "Type" defined pretty much like this (easily put in a base ViewModel if you have one):
public Type Type
{
get { return this.GetType(); }
}
So as long as you have that, this should allow you to do anything you want. Note I have "A Header!" in a textblock here, but that could easily be anything (icon, etc).
I've got it in here two ways... one style applies templates (if you have a significant investment in these already) and the other just uses setters to move the content to the right places.
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
xmlns:local="clr-namespace:WpfApplication1">
<Window.Resources>
<CompositeCollection x:Key="MyCollection">
<local:AViewModel Header="A Viewmodel" Content="A Content" />
<local:BViewModel Header="B ViewModel" Content="B Content" />
</CompositeCollection>
<DataTemplate x:Key="ATypeHeader" DataType="{x:Type local:AViewModel}">
<WrapPanel>
<TextBlock>A Header!</TextBlock>
<TextBlock Text="{Binding Header}" />
</WrapPanel>
</DataTemplate>
<DataTemplate x:Key="ATypeContent" DataType="{x:Type local:AViewModel}">
<StackPanel>
<TextBlock>Begin "A" Content</TextBlock>
<TextBlock Text="{Binding Content}" />
</StackPanel>
</DataTemplate>
<Style x:Key="TabItemStyle" TargetType="TabItem">
<Style.Triggers>
<!-- Template Application Approach-->
<DataTrigger Binding="{Binding Path=Type}" Value="{x:Type local:AViewModel}">
<Setter Property="HeaderTemplate" Value="{StaticResource ATypeHeader}" />
<Setter Property="ContentTemplate" Value="{StaticResource ATypeContent}" />
</DataTrigger>
<!-- Just Use Setters Approach -->
<DataTrigger Binding="{Binding Path=Type}" Value="{x:Type local:BViewModel}">
<Setter Property="Header">
<Setter.Value>
<WrapPanel>
<TextBlock Text="B Header!"></TextBlock>
<TextBlock Text="{Binding Header}" />
</WrapPanel>
</Setter.Value>
</Setter>
<Setter Property="Content" Value="{Binding Content}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<TabControl ItemContainerStyle="{StaticResource TabItemStyle}" ItemsSource="{StaticResource MyCollection}" />
</Grid>
HTH, Anderson

One way would be to use DataTemplateSelectors and have each one resolve the resource from a separate ResourceDictionary.

In this example I use DataTemplates in the resources section of my TabControl for each view model I want to display in the tab items.
In this case I map ViewModelType1 to View1 and ViewModelType2 to View2.
The view models will be set as DataContext object of the views automatically.
For displaying the tab item header, I use an ItemTemplate.
The view models I bind to are of different types, but derive from a common base class ChildViewModel that has a Title property. So I can set up a binding to pick up the title to display it in the tab item header.
In addition I display a "Close" Button in the tab item header. If you do not need that, just remove the button from the example code so you just have the header text.
The contents of the tab items are rendered with a simple ItemTemplate which displays the view in a content control with Content="{Binding}".
<UserControl ...>
<UserControl.DataContext>
<ContainerViewModel></ContainerViewModel>
</UserControl.DataContext>
<TabControl ItemsSource="{Binding ViewModels}"
SelectedItem="{Binding SelectedViewModel}">
<TabControl.Resources>
<DataTemplate DataType="{x:Type ViewModelType1}">
<View1/>
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModelType2}">
<View2/>
</DataTemplate>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate>
<DockPanel>
<TextBlock Text="{Binding Title}" />
<Button DockPanel.Dock="Right" Margin="5,0,0,0"
Visibility="{Binding RemoveButtonVisibility}"
Command="{Binding DataContext.CloseItemCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TypeOfContainingView}}}"
>
<Image Source="/Common/Images/ActiveClose.gif"></Image>
</Button>
</DockPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl Content="{Binding}"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</UserControl>
The user control which contains the tab control has a container view model of type ContainerViewModel as DataContext. Here I have a collection of all the view models displayed in the tab control. I also have a property for the currently selected view model (tab item).
This is a shortened version of my container view model (I skipped the change notification part).
public class ContainerViewModel
{
/// <summary>
/// The child view models.
/// </summary>
public ObservableCollection<ChildViewModel> ViewModels {get; set;}
/// <summary>
/// The currently selected child view model.
/// </summary>
public ChildViewModel SelectedViewModel {get; set;}
}

Josh Smith uses exactly this technique (of driving a tab control with a view model collection) in his excellent article and sample project WPF Apps With The Model-View-ViewModel Design Pattern. In this approach, because each item in the VM collection has a corresponding DataTemplate linking the View to the VM Type (by omitting the x:Key as Anderson Imes correctly notes), each tab can have a completely different UI. See the full article and source code for details.
The key parts of the XAML are:
<DataTemplate DataType="{x:Type vm:CustomerViewModel}">
<vw:CustomerView />
</DataTemplate>
<DataTemplate x:Key="WorkspacesTemplate">
<TabControl
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource ClosableTabItemTemplate}"
Margin="4"
/>
There is one downside - driving a WPF TabControl from an ItemsSource has performance issues if the UI in the tabs is big/complex and therefore slow to draw (e.g., datagrids with lots of data). For more on this issue, search SO for "WPF VirtualizingStackPanel for increased performance".

Related

WPF binding to both view and viewmodel´s properties

This is originally a question about Teleriks TabbedWindow control, but its really a generic.
Question. In a ItemTemplate, how to I bind to both the view and properties of the viewmodel
Below, my datasource is a list of Views (ie UserControls). I want to have the View presented in the ContentControl and some properties of the viewmodel presented in the header.
<telerik:RadTabbedWindow x:Class="Porter.Application.Views.MainWindow"
...
ItemsSource="{Binding Tabs2}">
<telerik:RadTabbedWindow.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DataContext.TabHeader}" />
</DataTemplate>
</telerik:RadTabbedWindow.ItemTemplate>
<telerik:RadTabbedWindow.ContentTemplate>
<DataTemplate>
<ContentControl Content="{Binding}" />
</DataTemplate>
</telerik:RadTabbedWindow.ContentTemplate>
</telerik:RadTabbedWindow>
UPDATED RESULT AFTER ANSWER FROM mm8
<telerik:RadTabbedWindow
ItemsSource="{Binding Tabs2}" <!--list of ViewModels (lets say ViewModelBase.cs)-->
...>
<telerik:RadTabbedWindow.Resources>
<DataTemplate DataType="{x:Type acc:SearchAccountsViewModel}">
<acc:SearchAccountsView/>
</DataTemplate>
<DataTemplate DataType="{x:Type hello:HelloWorldViewModel}">
<hello:HelloWorldView/>
</DataTemplate>
</telerik:RadTabbedWindow.Resources>
<telerik:RadTabbedWindow.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding TabHeader}" />
</DataTemplate>
</telerik:RadTabbedWindow.ItemTemplate>
<telerik:RadTabbedWindow.ContentTemplate>
<DataTemplate >
<ContentControl Content="{Binding}" />
</DataTemplate>
</telerik:RadTabbedWindow.ContentTemplate>
</telerik:RadTabbedWindow>
The Tab2 property should return an IEnumerable<T> where the type T has some public properties that you bind to in the XAML markup.
It may for example have a TabHeader property that you bind the header of the tab to in the ItemTemplate like this:
<telerik:RadTabbedWindow x:Class="Porter.Application.Views.MainWindow"
...
ItemsSource="{Binding Tabs2}">
<telerik:RadTabbedWindow.ItemTemplate>
<DataTemplate>
<TextBlock Text = "{Binding TabHeader}" />
</ DataTemplate >
</ telerik:RadTabbedWindow.ItemTemplate>
</telerik:RadTabbedWindow>
The ContentTemplate should be resolved automatically provided that you have defined a DataTemplate for the type T in scope of the RadTabbedWindow, for example in your App.xaml. It's in this template that you add your UserControl:
<DataTemplate DataType="{x:Type local:YourClass}">
<local:UserControl1 />
</DataTemplate>
You should not create a UserControl in the view model and add it to Tabs2. This breaks what the MVVM pattern is all about, i.e. separation of concerns. A view model doesn't create views.
If you don't have/want an implicit DataTemplate in App.xaml, you may of course also define the ContentTemplate inline:
<telerik:RadTabbedWindow x:Class="Porter.Application.Views.MainWindow"
...
ItemsSource="{Binding Tabs2}">
<telerik:RadTabbedWindow.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding TabHeader}" />
</DataTemplate>
</telerik:RadTabbedWindow.ItemTemplate>
<telerik:RadTabbedWindow.ContentTemplate>
<DataTemplate>
<local:UserControl1 />
</DataTemplate>
</telerik:RadTabbedWindow.ContentTemplate>
</telerik:RadTabbedWindow>
The key point is that you bind to properties of T in both templates and that T is a POCO and not a control of some kind.

WPF DataBinding issue: UserControls in DataGrid Cells not binding

I am trying to build up a DataGrid control for managing a property grid. I have an abstract class for my parameter base class that parameters of various types implement. This base class contains variables that are shared across each parameter (like Group, Name, IsLocked, etc.), and then each parameter manages the information that relevant to the particular type. For example, a number parameter may use a slider for the user control, which also then uses min/max values, etc.
I have set up a class which is an Observable Collection of my base parameter, and established it as a static resource for a CollectionViewSource:
<UserControl.Resources>
<local:Parameters x:Key="Parameters"/>
<CollectionViewSource x:Key="cvsParameters" Source="{StaticResource Parameters}" />
</UserControl.Resources>
Then in my DataGrid, I am using style triggers to switch between user controls for each of my parameters:
<DataGrid x:Name="DataGrid_Globals"
ItemsSource="{Binding Source={StaticResource cvsParameters}}"
AutoGenerateColumns="False"
CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Key" Binding="{Binding Key}" IsReadOnly="True"/>
<DataGridTemplateColumn Width="*" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding UIType}" Value="Text">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<controls:ucText DataContext="{Binding}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding UIType}" Value="Number">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<controls:ucNumber DataContext="{Binding}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
My user controls are set up with their data context declared in XAML:
<UserControl.DataContext>
<local:TestNumber_cls/>
</UserControl.DataContext>
<Grid HorizontalAlignment="Stretch">
<Slider
Value="{Binding NumberValue}"
Minimum="{Binding Min}"
Maximum="{Binding Max}"
/>
</Grid>
I can't figure out how to ensure that my usercontrols bind correctly. My other column works fine, with each Key value appearing, being correctly bound and updating (for this example it's just the "Key" column, but all of the fields from the base class behave just fine.) The correct UserControls are also selected based on the UIType DataTrigger, but the UserControls just instantiate from an empty constructor and don't bind.
I have also confirmed that standalone instances of my UserControls work fine and bind correctly. For, example, it works when I set the datacontext from code behind (which I don't want to do):
this.OneOffNumberUserControl.DataContext = ((Parameters)this.ParameterGrid.Resources["Parameters"])[2];
I have also tried to remove the DataContext="{Binding}" from my user controls, but this doesn't change anything.
In conjunction with Mechanic's suggestion above, which greatly simplified the XAML for my property grid, through further research I also learned that I needed to implement a DependencyProperty for each subclass-dependent usercontrol. Where I set the DataTemplate for each DataType in my property grid UserControl, I set the binding to my DependencyProperty.
<UserControl.Resources>
<local:Parameters x:Key="Parameters"/>
<CollectionViewSource x:Key="cvsParameters" Source="{StaticResource Parameters}" />
<DataTemplate DataType="{x:Type local:TestText_cls}">
<controls:ucText TextObject="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:TestNumber_cls}">
<controls:ucNumber NumberObject="{Binding}"/>
</DataTemplate>
</UserControl.Resources>
<Grid>
<DataGrid x:Name="DataGrid_Globals"
ItemsSource="{Binding Source={StaticResource cvsParameters}}"
AutoGenerateColumns="False"
CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Key" Binding="{Binding Key}" IsReadOnly="True"/>
<DataGridTemplateColumn Width="*" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
Then in the code behind my UserControls, I implement the DependencyProperty:
public static readonly DependencyProperty TextObjectProperty =
DependencyProperty.Register("TextObject",
typeof(TestText_cls),
typeof(ucText),
new PropertyMetadata(new TestText_cls()));
[Bindable(true)]
public TestText_cls TextObject
{
get { return (TestText_cls)this.GetValue(TextObjectProperty); }
set { this.SetValue(TextObjectProperty, value); }
}
public ucText()
{
InitializeComponent();
}
Finally, within each UserControl, I ensure that I have given it a name, and then set my binding to the correct variable in my DependencyProperty:
<UserControl x:Class="Testing.ucText"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Testing.Classes"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Name="TextUI">
<Grid HorizontalAlignment="Stretch">
<TextBox Text="{Binding ElementName=TextUI, Path=TextObject.Value}"/>
</Grid>
</UserControl>
I hope this helps anyone else!
If you want to put different controls for different subclasses, specify different datatemplates, and then
<!-- subtype-specific column -->
<DataGridTemplateColumn Header="">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

DataTemplate Binding depending on property type and with working property Binding

I check those articles about doing DataTemplate :
WPF DataTemplate Binding
WPF DataTemplate and Binding
WPF DataTemplate Textblock binding
and thoses about DataTemplate depending on property type :
WPF DataTemplate Binding depending on the type of a property
Dynamically display a control depending on bound property using WPF
I'm trying to display a property with different controls depending of the property value. I have this Xaml that is partialy working. I have 2 problems :
The property is displaying with the right control, but when I set the value it doesn't go back to the property. Means the "set" of My property is not call (but was before I creates the DataTemplate). I detect that the problem about setting the property is about the ="{Binding Path=.}" but I cannot find the solution to set it otherwise.
Also To be able to make it work, I had to "isolate" the Value into a single ViewModel so that the DataTemplate doesn't affect all the other control.
Can you help me find betters solutions to resolves those 2 problems?
Here is the xaml code of my View linked with MyContainerViewModel that has a "ChangingDataType" :
<UserControl >
<UserControl.Resources>
<!-- DataTemplate for strings -->
<DataTemplate DataType="{x:Type sys:String}">
<TextBox Text="{Binding Path=.}" HorizontalAlignment="Stretch"/>
</DataTemplate>
<!-- DataTemplate for bool -->
<DataTemplate DataType="{x:Type sys:Boolean}">
<CheckBox IsChecked="{Binding Path=.}" />
</DataTemplate>
<!-- DataTemplate for Int32 -->
<DataTemplate DataType="{x:Type sys:Int32}">
<dxe:TextEdit Text="{Binding Path=.}" MinWidth="50" Mask="d" MaskType="Numeric" HorizontalAlignment="Stretch"/>
<!--<Slider Maximum="100" Minimum="0" Value="{Binding Path=.}" Width="100" />-->
</DataTemplate>
<!-- DataTemplate for decimals -->
<DataTemplate DataType="{x:Type sys:Decimal}">
<!-- <TextBox Text="{Binding Path=.}" MinWidth="50" HorizontalAlignment="Stretch" />-->
<dxe:TextEdit Text="{Binding Path=.}" MinWidth="50" Mask="f" MaskType="Numeric" HorizontalAlignment="Stretch" />
</DataTemplate>
<!-- DataTemplate for DateTimes -->
<DataTemplate DataType="{x:Type sys:DateTime}">
<DataTemplate.Resources>
<DataTemplate DataType="{x:Type sys:String}">
<TextBlock Text="{Binding Path=.}"/>
</DataTemplate>
</DataTemplate.Resources>
<DatePicker SelectedDate="{Binding Path=.}" HorizontalAlignment="Stretch"/>
</DataTemplate>
</UserControl.Resources>
<ContentPresenter Content="{Binding MyChangingPropery}"/>
</UserControl>
More informations about 2 :
I wanted to have in a view a label and a property that changes depending of the object. Something like this :
<UserControl>
<UserControl.Resources>
<!-- ...DataTemplate here... -->
</UserControl.Resources>
<StackPanel>
<Label Content="Allo"/>
<ContentPresenter Content="{Binding MyChangingPropery}"/>
</StackPanel>
</UserControl>
But if I put the DataTemplate on this UserControl resources, it will also affect the Label "allo". So I had to create another view that contains the DataTemplate and MyChangingProperty so that the label Allo would not be affected. But the extra View created just for one property is kind of ugly to me, I'm sure there is a better way to isolate the DataTemplate so it can apply only to one UIControl.
<UserControl >
<StackPanel>
<Label Content="Allo"/>
<ContentPresenter Content="{Binding MyContainerViewModel}"/>
</StackPanel>
</UserControl>
Note : MyContainerViewModel here is linked with the first view described.
Thanks in advance!
One possible solution would be to use a DataTemplateSelector. You cannot bind primitive types using two way bindings because that would have to be somehow by reference via the DataTemplate which I think is not supported by WPF.
The DataTemplateSelector now selects the right DataTemplate based on the property type and searches for the right DataTemplate in the resources by name. This also solves your problem that your DataTemplates interacted with the Label.
So first you need to define a DataTemplateSelector that changes the DataTemplate based on the type of the property:
public class MyDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var fe = (FrameworkElement)container;
var prop = (item as MyViewModelType)?.MyChangingProperty;
if (prop is string)
return fe.FindResource("MyStringDT") as DataTemplate;
else if (prop is bool)
return fe.FindResource("MyBoolDT") as DataTemplate;
// More types...
return base.SelectTemplate(item, container);
}
}
Then you need to change the UserControl like this:
<UserControl>
<UserControl.Resources>
<local:MyDataTemplateSelector x:Key="MyDTSelector" />
<!-- DataTemplate for strings -->
<DataTemplate x:Key="MyStringDT">
<TextBox Text="{Binding MyChangingProperty, Mode=TwoWay}"
HorizontalAlignment="Stretch"/>
</DataTemplate>
<!-- DataTemplate for bool -->
<DataTemplate x:Key="MyBoolDT">
<CheckBox IsChecked="{Binding MyChangingProperty, Mode=TwoWay}" />
<!-- More DataTemplates... -->
</DataTemplate>
</UserControl.Resources>
<StackPanel>
<Label Content="Allo"/>
<ContentPresenter Content="{Binding MyContainerViewModel}"
ContentTemplateSelector="{StaticResource MyDTSelector}" />
</StackPanel>
</UserControl>
You can find a bit more information regarding the DataTemplateSelector here.
You can of course also set a DataType on this new DataTemplates but it isn't required because the x:Key makes them unique anyway. But if you want then it has to look like this:
<DataTemplate x:Key="MyStringDT" DataType="{x:Type local:MyViewModelType}">
In my opinion, the previously posted answer is overkill. While a DateTemplateSelector is a useful thing to know about, it seems unnecessary to me in this scenario.
But if I put the DataTemplate on this UserControl resources, it will also affect the Label "allo".
The reason it affects the Label object is that the Label object is a ContentControl, and so does the same template-matching behavior for content types as your own ContentPresenter element does. And you've set the content of the Label object to a string value. But you can put anything you want as the content for it.
The way to fix the undesired effect is to intercept that behavior by changing the content from a string object to an explicit TextBlock (the control in the template that a string object normally gets assigned). For example:
<UserControl>
<UserControl.Resources>
<!-- ...DataTemplate here... -->
</UserControl.Resources>
<StackPanel>
<Label>
<TextBlock Text="Allo"/>
</Label>
<ContentPresenter Content="{Binding MyChangingPropery}"/>
</StackPanel>
</UserControl>
In that way, you bypass the template-finding behavior (since TextBlock doesn't map to any template and can be used directly), and the content for the Label will just be the TextBlock with the text you want.
This seems like a lot simpler way to fix the issue, than either to create a whole new view or to add a DataTemplateSelector.

How to resolve content presenter not displaying View?

I've added a content presenter to my TabControl's data template, in order to display the correct view.
But when I load the application, the tabs display but they have no user control content.
I Googled the error and came across this solution, that suggested an error with the data context but the setup seems ok in my AppVM and AppView below.
The names of both VM's and Views are also correct that I'm referencing in the AppView.
Does anyone have an idea where the setup has gone wrong here?
This is the ApplicationView that holds both views:
<Window x:Class="MongoDBApp.Views.ApplicationView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:views="clr-namespace:MongoDBApp.Views"
xmlns:vm="clr-namespace:MongoDBApp.ViewModels"
Title="ApplicationView"
Width="800"
Height="500">
<Window.Resources>
<DataTemplate DataType="{x:Type vm:CustomerDetailsViewModel}">
<views:CustomerDetailsView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:CustomerOrdersViewModel}">
<views:CustomerOrdersView />
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<vm:ApplicationViewModel />
</Window.DataContext>
<TabControl ItemsSource="{Binding PageViewModels}" TabStripPlacement="Top">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding CurrentPageViewModel}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Window>
ApplicationViewModel constructor and related fields:
private ICommand _changePageCommand;
private IPageViewModel _currentPageViewModel;
private List<IPageViewModel> _pageViewModels;
private static ICustomerDataService customerDataService = new CustomerDataService(CustomerRepository.Instance);
#endregion
/// <summary>
/// Initializes a new instance of the <see cref="ApplicationViewModel"/> class.
/// </summary>
public ApplicationViewModel()
{
// Add available pages
PageViewModels.Add(new CustomerDetailsViewModel(customerDataService));
PageViewModels.Add(new CustomerOrdersViewModel());
// Set starting page
CurrentPageViewModel = PageViewModels[0];
}
The ContentTemplate property wraps the Content object.
For example, with your code you are setting the .Content property to a CustomerDetailsViewModel object, and trying to bind to the CurrentPageViewModel of that object, which doesn't exist.
What is getting rendered is :
<TabControl>
<TabItem>
<ContentPresenter Content=CustomerDetailsViewModel>
<ContentPresenter Content="{Binding CurrentPageViewModel}" />
</ContentPresenter>
</TabItem>
<TabItem>
<ContentPresenter Content=CustomerOrdersViewModel>
<ContentPresenter Content="{Binding CurrentPageViewModel}" />
</ContentPresenter>
</TabItem>
</TabControl>
Because the TabControl will auto-generate a ContentPresenter to wrap the .Content for each TabItem, you don't need this template at all.
But what it sounds like what you actually want is to bind the SelectedItem property of the TabControl
<TabControl ItemsSource="{Binding PageViewModels}"
SelectedItem="{Binding CurrentPageViewModel}"
TabStripPlacement="Top">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
We don't generally use a ContentPresenter outside of a ControlTemplate and certainly not like you are doing in your DataTemplate... the use of the ContentPresenter will make the WPF Framework search for a DataTemplate that matches the type of the Content, so in your case, you'll end up with an endless loop. Instead, you should put your associated view in the DataTemplate:
<DataTemplate DataType="{x:Type YourDataXamlPrefix:CurrentPageViewModel}">
<YourUiXamlPrefix:YourView DataContext="{Binding CurrentPageViewModel}" />
</DataTemplate>
In this way, when the Framework comes across your view model class in the collection, it will find this DataTemplate and render the relevant view.

How to properly bind a ListBoxItem in WPF?

I have a listbox and I want to iterate over a collection of Bars in my Foo-object.
<ListBox DataContext="{Binding Path=Foo.Bars}" >
<ListBox.Items>
<ListBoxItem>
<ContentControl DataContext="{Binding Path=.}" />
</ListBoxItem>
</ListBox.Items>
</ListBox>
This is the datatemplate I want to use.
<DataTemplate DataType="{x:Type Bar}">
<Label Content="hello stackoverflow" />
</DataTemplate>
If I snoop (--> examine by using the tool Snoop) my application, I notice that the entire collection of Bars is bound to the ContentControl, in stead of just 1.
How can I properly bind so the iteration over the collection goes fine?
You can just set the DataTemplate, and WPF does all the work. Set the ItemsSource to a list of Bar items, and then define a DataTemplate for Bar items.
<ListBox ItemsSource="{Binding Path=Foo.Bars}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type Bar}">
<Label Content="hello stackoverflow" />
</DataTemplate>
</ListBox.Resources>
</ListBox>
You could also set the ItemsTemplate directly by using <ListBox.ItemTemplate> instead of <ListBox.Resources>
See Data Binding Overview at MSDN.
First add your namespace to the Window element (Intellisense) :
xmlns:local="clr-namespace:yourenamespace"
Then the following XAML ( in Window.Resources is a clean way to do it ) :
<Window.Resources>
<ObjectDataProvider x:Key="DataProvider" ObjectType="{x:Type local:Foo}"/>
<DataTemplate x:Key="Template" >
<TextBlock Text="{Binding Bar}"/>
</DataTemplate>
</Window.Resources>
Place the Listbox :
<ListBox DataContext="{Binding Source={StaticResource DataProvider}}" ItemsSource="{Binding Bars}" ItemTemplate="DynamicResource Template" />
But, it depends on your code-behind object, you have to set a constructor to initialise public properties within your object which are ObservableCollection<> preferably (There is some restriction rules with object instance in XAML).

Resources