Элемент является нулевым в DataTemplateSelector

У нас есть небольшая проблема с использованием dataTemplateSelector для выбора правильной матрицы данных для моей модели представления, основанной на значении перечисления.

Это демо, которое воспроизводит проблему.

У меня есть иерархия моделей, которые используются моими viewModels

Перечисление, определяющее типы модели:

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>

Модель Selector это:

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, но мне нужно иметь этот шаблон в отдельном файле, поскольку он будет использоваться в разных частях приложения.

Я не знаю, я могу сделать, чтобы иметь доступ к ModelViewModel в ModelSelector?

4 ответа

Да, вы можете получить доступ к вашему ViewModel через DataTemplateSelector. Ваша ошибка в том, что вы установили DataContext имущество:

DataContext="{Binding}"

Для ContentPresenter вы должны установить Content свойство вместо

Content="{Binding}"

Если вы будете делать это, объект item внутри переопределенного метода будет точно Content имущество.

Согласно статье MSDN:

Если свойство ContentTemplateSelector в ContentPresenter установлено, ContentPresenter применяет соответствующий DataTemplate к свойству Content, и отображается результирующий UIElement и его дочерние элементы, если таковые имеются.

Обратите внимание на логику ContentPresenter для отображения Content,

Ухх - после того, как я ударился головой о стену с точно такой же проблемой, я наконец понял проблему.

Вам нужно использовать ContentControl вместо ContentPanel, и, как предполагает Stukselbax, вам нужно связать контент, а не текстовый текст.

Извините, для вас уже 2 года, но надеюсь, это поможет кому-то еще!

Поздний ответ, но чтобы использовать TemplateSelector, вам нужно сначала установить содержимое, поэтому для ContentControl вы устанавливаете Content перед ContentTemplateSelector в Xaml.

То же самое для ListView с ItemsSource и ItemTemplateSelector, я думаю.

Что-то вроде этого:

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

просто для истории привязки сложны. Звонят несколько раз. До применения шаблонов и после. В моей ситуации первый вызов был до того, как мои шаблоны применялись с нулевым значением в элементе, а после с данными. Таким образом, простой ответ - проверить\отладить не нуль в элементе.

Другие вопросы по тегам