【问题标题】:Item is null in DataTemplateSelectorDataTemplateSelector 中的项目为空
【发布时间】:2013-01-07 13:21:37
【问题描述】:

使用 dataTemplateSelector 根据枚举值为我的视图模型选择正确的数据模板时遇到了一点问题。

这是重现问题的演示。

我有一个视图模型使用的模型层次结构

定义模型类型的枚举是:

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

        }
    }

}

之后,我的 MainView 中有一个列表框,它使用项目模板来显示每个项目。

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

这个item模板使用ModelView的其他view来渲染item。ModelView展示的是公共信息,具体的模型数据由ModelSelector选择的view展示。

<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中的参数项为null,而另一个参数(Container)的dataContext为null。我无法知道ModelViewModel的值是哪个来选择正确的视图。

如果我将数据模板放在 ListView 项目模板中,则该项目具有 ModelViewMode 的值,但我需要将该模板放在单独的文件中,因为它将用于应用程序的不同部分。

我不知道如何才能访问 ModelSelector 中的 ModelViewModel?

【问题讨论】:

    标签: c# wpf datatemplate datatemplateselector


    【解决方案1】:

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

    DataContext="{Binding}"
    

    对于ContentPresenter,您应该设置Content 属性

    Content="{Binding}"
    

    如果您这样做,则被覆盖方法内的对象item 将完全是Content 属性。

    根据msdn article

    如果 ContentPresenter 上的 ContentTemplateSelector 属性已设置,则 ContentPresenter 会将适当的 DataTemplate 应用于 Content 属性,并显示生成的 UIElement 及其子元素(如果有)。

    注意显示ContentContentPresenter逻辑。

    【讨论】:

    • 感谢@stukselbax 的帮助,但我已经尝试过了,但没有成功,我不知道为什么。我认为该问题与绑定有关,并且项目模板的内容位于单独的文件中。如果我把它放在同一个文件中它可以工作:s
    【解决方案2】:

    Urgh - 在用完全相同的问题将我的头撞到墙上之后,我终于找到了问题所在。

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

    抱歉,这对您来说太晚了 2 年,但希望它可以帮助其他人!

    【讨论】:

    • 谢谢。我不小心使用了 ContentPresenter,这就是我需要的答案!
    【解决方案3】:

    迟到的答案,但要使用 TemplateSelector,您需要先设置内容,因此对于 ContentControl,您在 Xaml 中的 ContentTemplateSelector 之前设置 Content ..

    我猜 ListView 与 ItemsSource 和 ItemTemplateSelector 相同。

    类似这样的:

    <ContentControl Content="{Binding Animals}" ContentTemplateSelector="{StaticResource AnimalTemplateSelctor}" />
    

    【讨论】:

    • 你拯救了我的一天。谢谢!
    【解决方案4】:

    只是为了历史 绑定很复杂。他们多次打电话。在应用模板之前和之后。在我的情况下,第一次调用是在我的模板在项目中应用 null 之前,在数据之后。如此简单的答案是检查\调试是否在项目中不为空。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-13
      • 2023-03-09
      • 1970-01-01
      相关资源
      最近更新 更多