DataTemplateSelector中的Item为空

5

我有一个小问题,使用DataTemplateSelector根据枚举值选择正确的DataTemplate来呈现我的ViewModel。

下面是复现问题的演示代码。

我有一个模型层级结构,这些模型被我的ViewModel所使用。

定义模型类型的枚举如下:

public enum ModelType
{
    ModelA,
    ModelB        
}

模型基类是:

public abstract class ModelBase
{
    protected ModelBase(ModelType modelType)
    {
        ModelType = modelType;
    }

    public ModelType ModelType { get; private set; }


    public string Name { get; set; }
}

子模型类包括:

public class ModelA:ModelBase
{
    public ModelA():base(ModelType.ModelA)
    {
        Name = "ModelA";
    }


    public string PropertyModelA { get { return "PropertyModelA"; } }
}

并且。
public class ModelB : ModelBase
{
    public ModelB()
        : base(ModelType.ModelB)
    {

        Name = "ModelB";
    }
    public string PropertyModelB { get { return "PropertyModelB"; } }


}

我的MainViewModel和ModelViewModel分别是:

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()));
    }

并且

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");
            }

        }
    }

}

之后我在我的主视图中有一个列表框,使用项目模板来显示每个项目。

 <ListBox x:Name="entryList" ItemsSource="{Binding Models}"  >
            <ListBox.ItemTemplate>
                <DataTemplate>                       
                      <views:ModelView/>     
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

这个项目模板使用名为ModelView的其他视图来呈现项。 ModelView 显示常见信息,由 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>

目前模型选择器可以选择的视图为A和B:

<StackPanel>
    <TextBlock Text="{Binding Model.PropertyModelA}" />
</StackPanel>


<StackPanel>
    <TextBlock Text="{Binding Model.PropertyModelB}" />
</StackPanel>

模型选择器是:

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;
    }
}

问题在于DataTemplateSelector中的参数item为空,而另一个参数(Container)的DataContext也为空。我无法知道哪个是ModelViewModel的值以选择正确的视图。
如果将数据模板放在ListView项模板中,则该项具有ModelViewModel的值,但我需要将该模板放在单独的文件中,因为它将在应用程序的不同部分中使用。
我不知道如何在ModelSelector中访问ModelViewModel?
4个回答

15

是的,您可以通过 DataTemplateSelector 访问您的 ViewModel。您的错误在于设置了 DataContext 属性:

DataContext="{Binding}"

针对 ContentPresenter,应设置Content属性而非其他属性。
Content="{Binding}"

如果你这样做,重载方法中的item对象将会完全匹配Content属性。
根据MSDN文章
如果在ContentPresenter上设置了ContentTemplateSelector属性,ContentPresenter将把相应的DataTemplate应用于Content属性,显示出生成的UIElement及其子元素(如果有)。
注意ContentPresenter逻辑来显示Content

1
感谢@stukselbax的帮助,但我已经尝试过了,它没有起作用,我不知道为什么。我认为问题与绑定有关,并且项目模板的内容在单独的文件中。如果我将其放在同一个文件中,它就可以工作:s - welhell

6

啊,我曾经也遇到过完全相同的问题,最终找到了解决方法。

你需要使用“ContentControl”,而不是“ContentPanel”,并且正如Stukselbax建议的那样,你需要绑定内容而不是数据上下文。

很抱歉这对你来说已经晚了两年,但希望它能帮助其他人!


谢谢。我不小心使用了ContentPresenter,这正是我需要的答案! - Billy Jake O'Connor

1
晚了一些,但是要使用TemplateSelector,您需要先设置内容,因此对于ContentControl,您需要在Xaml中在ContentTemplateSelector之前设置Content。
我猜ListView与ItemsSource和ItemTemplateSelector相同。
类似这样:
<ContentControl Content="{Binding Animals}" ContentTemplateSelector="{StaticResource AnimalTemplateSelctor}" />

0

仅供历史参考 绑定很复杂。它们会多次调用。在应用模板之前和之后都会调用。在我的情况下,第一次调用是在我的模板应用之前使用 null 的 item,之后使用数据。因此,简单的答案是检查\调试 item 中是否为非空。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接