Почему WPF не выберет правильный DataTemplate в моем случае?

В моей модели ViewModel есть 4 класса, которые реализуют интерфейс IPage (интерфейс, который я создал). В ViewModel моего приложения есть свойство CurrentPage типа IPage.

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

Итак, когда я использую следующий код, WPF не найдет нужный DataTemplate

<UserControl ...>
    <ContentControl Content="{Binding CurrentPage, Mode=OneWay}"
                    VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
        <ContentControl.Resources>
            <DataTemplate DataType="{x:Type operation:OperationListPageVM}">
                <pages1:OperationListPageV/>
            </DataTemplate>
            <DataTemplate DataType="{x:Type operation:OperationDetailPageVM}">
                <pages1:OperationDetailPageV/>
            </DataTemplate>
            <DataTemplate DataType="{x:Type part:PartListPageVM}">
                <pages1:PartListPageV/>
            </DataTemplate>
            <DataTemplate DataType="{x:Type part:PartDetailPageVM}">
                <pages1:PartDetailPageV/>
            </DataTemplate>
            <DataTemplate DataType="{x:Type pages:SettingsPageVM}">
                <pages1:SettingsPageV/>
            </DataTemplate>
        </ContentControl.Resources>
    </ContentControl>
</UserControl>

Но если я поставлю только один DataTemplate как ContentControl.ContentTemplate, он применит его правильно.

<UserControl ...>
    <ContentControl Content="{Binding CurrentPage, Mode=OneWay}"
                    VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
        <ContentControl.ContentTemplate>
            <DataTemplate DataType="{x:Type operation:OperationListPageVM}">
                <pages1:OperationListPageV/>
            </DataTemplate>
        </ContentControl.ContentTemplate>
    </ContentControl>
</UserControl>

Если я затем верну эти DataTemplate в качестве ресурсов, дам им ключ, создайте DataTemplateSelector и добавлю

ContentTemplateSelector="{StaticResource PageTemplateSelector}"

В ControlTemplate с DataTemplateSelector следующим образом:

using System.Windows;
using System.Windows.Controls;
using MaintenanceModuleV3.ViewModel;
using MaintenanceModuleV3.ViewModel.Pages;
using MaintenanceModuleV3.ViewModel.Pages.Operation;
using MaintenanceModuleV3.ViewModel.Pages.Part;

namespace MaintenanceModuleV3.View.TemplateSelector {
    public class PageTemplateSelector : DataTemplateSelector {
        public override DataTemplate SelectTemplate(object item, DependencyObject container) {
            if (AppVM.Instance.CurrentPage != null) {
                if (AppVM.Instance.CurrentPage is OperationListPageVM) {
                    return (container as FrameworkElement).FindResource("OperationListPageTemplate") as DataTemplate;
                } else if (AppVM.Instance.CurrentPage is OperationDetailPageVM) {
                    return (container as FrameworkElement).FindResource("OperationDetailPageTemplate") as DataTemplate;
                } else if (AppVM.Instance.CurrentPage is PartListPageVM) {
                    return (container as FrameworkElement).FindResource("PartListPageTemplate") as DataTemplate;
                } else if (AppVM.Instance.CurrentPage is PartDetailPageVM) {
                    return (container as FrameworkElement).FindResource("PartDetailPageTemplate") as DataTemplate;
                } else if (AppVM.Instance.CurrentPage is SettingsPageVM) {
                    return (container as FrameworkElement).FindResource("SettingsPageTemplate") as DataTemplate;
                }
            }
            return null;
        }
    }
}

Затем выбирается правильный DataTemplate.

Почему WPF не выберет правильный DataTemplate, если в этом случае я помещу DataTemplates в ресурсы элемента?

Спасибо

NB: Использование Framework 3.5

РЕДАКТИРОВАТЬ: я заметил вторую проблему: DataContext внутри DataTemplates имеет значение null. Это почему? Почему они не принимают CurrentPage как DataContext? Это тот случай, когда я напрямую устанавливаю ContentTemplate. WPF, похоже, не любит привязки к свойствам, типизированным для интерфейса.

РЕДАКТИРОВАТЬ 2: Если я явно устанавливаю DataContext для OperationListPageV в его конструкторе и использую TemplateSelector, все работает, но почему я должен их использовать?

РЕДАКТИРОВАТЬ 3: Полный код ViewModel, если это помогает:

Модель просмотра приложения

public class AppVM : INotifyPropertyChanged {

    #region constructor
    private AppVM() {
        AppM.LanguageChanged += (sender, args) => {
            onPropertyChanged("CurrentLanguage");
        };

        AppM.NbDaysAvgChanged += (sender, args) => {
            onPropertyChanged("NbDaysAvg");
        };

        AppM.NbDaysMenuNotificationChanged += (sender, args) => {
            onPropertyChanged("NbDaysMenuNotification");
        };
    }
    #endregion

    #region business

    #endregion

    #region events
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void onPropertyChanged(string propertyName) {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion

    #region properties

    private static AppVM _instance;
    public static AppVM Instance {
        get { return _instance ?? (_instance = new AppVM()); }
    }


    #region notifypropertychanged
    private OperationListPageVM _currentPage = OperationListPageVM.Instance;
    public OperationListPageVM CurrentPage {
        get { return _currentPage; }
        set {
            _currentPage = value;
            onPropertyChanged("CurrentPage");
        }
    }

    public Language CurrentLanguage {
        get { return AppM.CurrentLanguage; }
    }

    public int NbDaysMenuNotification {
        get { return AppM.NbDaysMenuNotification; }
    }

    public int NbDaysAvg {
        get { return AppM.NbDaysAvg; }
    }

    public Dictionary<String, String> AvailableTranslations {
        get { return AppM.AvailableTranslations; }
    }
    #endregion
    #region commands
    #endregion
    #endregion
}

Страница, на которой я должен был отображать ViewModel

public sealed class OperationListPageVM : INotifyPropertyChanged, IPage {

    #region constructor
    private OperationListPageVM() {
        //fill the available operations with data stored in the App Model coming from the DataBase
        foreach (MaintenanceOperation operation in AppM.Operations) {
            AvailableOperations.Add(new MaintenanceOperationVM(operation)); 
        }
        foreach (MaintenanceOperationVM operation in AvailableOperations) {
            DisplayedOperations.Add(operation);
        }
    }
    #endregion

    #region business

    #endregion

    #region events
    public event PropertyChangedEventHandler PropertyChanged;

    private void onPropertyChanged(string propertyName) {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion

    #region properties

    private static OperationListPageVM _instance;

    public static OperationListPageVM Instance {
        get { return _instance ?? (_instance = new OperationListPageVM()); }
    }

    #region notifypropertychanged
    private ObservableCollection<MaintenanceOperationVM> _displayedOperations = new ObservableCollection<MaintenanceOperationVM>();
    public ObservableCollection<MaintenanceOperationVM> DisplayedOperations {
        get { return _displayedOperations; }
        set {
            _displayedOperations = value;
            onPropertyChanged("DisplayedOperations");
        }
    }

    private readonly List<MaintenanceOperationVM> _availableOperations = new List<MaintenanceOperationVM>();

    internal List<MaintenanceOperationVM> AvailableOperations {
        get { return _availableOperations; }
    }

    #endregion

    #region commands

    #endregion

    #endregion
}

Уровень просмотра страницы, которую я должен был отобразить (OperationListPageV)

<UserControl x:Class="MaintenanceModuleV3.View.Pages.OperationListPageV"
            ...>
    <touch:ScrollFriction2>
        <ListBox ItemsSource="{Binding DisplayedOperations}">
                <ListBox.ItemContainerStyle>
                    <Style TargetType="{x:Type ListBoxItem}">
                        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                    </Style>
                </ListBox.ItemContainerStyle>
            <ListBox.ItemTemplate>
                <DataTemplate DataType="{x:Type viewModel:MaintenanceOperationVM}">
                    <view:MaintenanceOperationV/>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </touch:ScrollFriction2>
</UserControl>

Если вам нужно больше кода, просто попросите его. Спасибо!


person nkoniishvt    schedule 19.11.2014    source источник
comment
Комментарии не подлежат расширенному обсуждению; этот разговор был перемещен в чат.   -  person Flexo    schedule 19.11.2014


Ответы (1)


поместите шаблоны в Ресурсы вашего ItemControl. Я не знаю, почему они сделали это таким образом, но они должны быть на один уровень выше вашего ContentControl.

person Krzysztof Skowronek    schedule 06.12.2017