Well a have a little problem using a dataTemplateSelector to choose the right datatemplate for my view model based in an enum value.
This is a demo that reproduces the issue.
I have a hierarchy of models that are used by my viewModels
The enum that define the model types is:
public enum ModelType
{
ModelA,
ModelB
}
The model base class is:
public abstract class ModelBase
{
protected ModelBase(ModelType modelType)
{
ModelType = modelType;
}
public ModelType ModelType { get; private set; }
public string Name { get; set; }
}
and the child model clases are:
public class ModelA:ModelBase
{
public ModelA():base(ModelType.ModelA)
{
Name = "ModelA";
}
public string PropertyModelA { get { return "PropertyModelA"; } }
}
and
public class ModelB : ModelBase
{
public ModelB()
: base(ModelType.ModelB)
{
Name = "ModelB";
}
public string PropertyModelB { get { return "PropertyModelB"; } }
}
My MainViewModel and the ModelViewModel respectively are:
public class MainWindowViewModel:ViewModelBase
{
public MainWindowViewModel()
{
Models = new ObservableCollection<ModelViewModel>();
LoadModels();
}
public ObservableCollection<ModelViewModel> Models { get; private set; }
private void LoadModels()
{
Models.Add(new ModelViewModel(new ModelA()));
Models.Add(new ModelViewModel(new ModelB()));
Models.Add(new ModelViewModel(new ModelB()));
}
and
public class ModelViewModel : ViewModelBase
{
private ModelBase _model;
public ModelViewModel(ModelBase model)
{
_model = model;
}
public ModelBase Model
{
get { return _model; }
set
{
if (!_model.Equals(value))
{
_model = value;
OnPropertyChanged("Model");
}
}
}
}
After that I have a List box in my MainView that use a item template to show each item.
<ListBox x:Name="entryList" ItemsSource="{Binding Models}" >
<ListBox.ItemTemplate>
<DataTemplate>
<views:ModelView/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This item template use other view called ModelView to render the item.ModelView shows the common information , the specific model data are showed by the view selected by the ModelSelector.
<UserControl.Resources>
<ResourceDictionary>
<selectors:ModelSelector x:Key="modelSelector" />
</ResourceDictionary>
</UserControl.Resources>
<StackPanel>
<TextBlock Text="{Binding Model.Name}" />
<ContentPresenter ContentTemplateSelector="{StaticResource modelSelector}" DataContext="{Binding }" />
</StackPanel>
At the moment the views that could be selected by the model selector are A and B:
<StackPanel>
<TextBlock Text="{Binding Model.PropertyModelA}" />
</StackPanel>
<StackPanel>
<TextBlock Text="{Binding Model.PropertyModelB}" />
</StackPanel>
The model Selector is:
public class ModelSelector:DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var viewModel = item as ModelViewModel;
var dataTemplate = default(DataTemplate);
if (viewModel != null)
{
switch (viewModel.Model.ModelType)
{
case NestedDataTemplateSelectorTest.Models.ModelType.ModelA:
dataTemplate = CreateDataTemplate<ModelAView>();
break;
case NestedDataTemplateSelectorTest.Models.ModelType.ModelB:
dataTemplate = CreateDataTemplate<ModelBView>();
break;
default:
dataTemplate = this.SelectTemplate(item, container);
break;
}
}
return dataTemplate;
}
private DataTemplate CreateDataTemplate<TView>()
{
var dataTemplate = new DataTemplate();
var frameworkElement = new FrameworkElementFactory(typeof(TView));
dataTemplate.VisualTree = frameworkElement;
return dataTemplate;
}
}
The problem is that the parameter item in the DataTemplateSelector is null and the other parameter(Container) have the dataContext in null.I don't have way to know which is the value of the ModelViewModel to choose the right view.
if I put the data template in the ListView Item template then the item have the value of the ModelViewMode but I need to have that template in a separate file because it will be used in diferent parts of the application.
I dont know can i do to have access to the ModelViewModel in the ModelSelector?
Yes, you can access your ViewModel throug DataTemplateSelector. Your mistake is that you set the DataContext
property:
DataContext="{Binding}"
For ContentPresenter, you should set Content
property instead
Content="{Binding}"
If you will do that, the object item
inside the overriden method will be exactly Content
property.
According to the msdn article:
If the ContentTemplateSelector property on the ContentPresenter is set, the ContentPresenter applies the appropriate DataTemplate to the Content property and the resulting UIElement and its child elements, if any, are displayed.
Pay attention to the ContentPresenter logic to display the Content
.
Urgh - after beating my head against a wall with exactly the same issue I finally figured out the problem.
You need to use a 'ContentControl' instead of a 'ContentPanel', and as Stukselbax suggests it's the content you need to bind, not the datacontext.
Sorry it's 2 years too late for you, but hopefully it will help someone else!
Late answer, but to use a TemplateSelector, you need to set content first, so for a ContentControl you set Content before ContentTemplateSelector in Xaml..
Same for ListView with ItemsSource and ItemTemplateSelector I guess.
Something like this:
<ContentControl Content="{Binding Animals}" ContentTemplateSelector="{StaticResource AnimalTemplateSelctor}" />
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With