wpf связывает разные шаблоны данных с различными типами объектов в contentcontrol - PullRequest
0 голосов
/ 13 сентября 2018

Я новичок в WPF и MVVM. Я пытаюсь связать два разных DataTemplates с двумя разными типами объектов в одном ContentControl. Каждый вид объекта соответствует одному DataTemplate.

Два вида объектов называются Единица и Компонент соответственно. Они содержат разные свойства. Например, юнит имеет 3 свойства: Id , Имя и Производство . Компонент имеет 3 свойства Id , Тип и Материалы . Пример кода следующий:

public class Unit : INotifyPropertyChanged
{
    private int _id;
    private string _name;
    private string _manufacture;

    public int Id
    {
        get {return this._id}
        set
        {
            this._id = value;
            OnPropertyChanged("Id")
        }
    {
    public string Name
    {
        get {return this._name}
        set
        {
            this._id = value;
            OnPropertyChanged("Name")
        }
    {
    public string Manufacture
    {
        get {return this._manufacture}
        set
        {
            this._id = value;
            OnPropertyChanged("Manufacture")
        }
    {
    public event PropertyChangedEventHandler PropertyChanged;
    ...
}

Класс Component имеет аналогичную структуру.

В MainWindow у меня есть ListBox, перечисляющий имена объектов (я буду менять его на TreeView в будущем) слева, и ContentControl справа. Я хочу, чтобы при выборе имени объекта детали объекта отображались справа. Код MainWindow выглядит следующим образом:

<Windows.Resources>
    <CollectionViewSource
        Source="{Binding Source={x:Static Application.Current}, Path=UnitItems}"
        x:Key="UnitDataView">
    </CollectionViewSource>

    <CollectionViewSource
        Source="{Binding Source={x:Static Application.Current}, Path=ComponentItems}"
        x:Key="ComponentDataView">
    </CollectionViewSource>

    <CompositeCollection x:Key="AllDataView
        <CollectionContainer Collection="{Binding Source={StaticResource UnitDataView}}" />
        <CollectionContainer Collection="{Binding Source={StaticResource ComponentDataView}}" />
    </CompositeCollection>

<local: PartDataTemplateSelector x:Key="MyDataTemplateSelector"
                                 UnitTemplate="{StaticResource unitTemplate}"
                                 ComponentTemplate="{StaticResource componentTemplate}" />
</Windows.Resources>

<Grid>
    <Grid.ColumnDefinition>
        <ColumnDefinition>
        <ColumnDefinition>
    </Grid.ColumnDefinition>

    <ListBox x:Name="ComponentListView" Grid.Column="0"
             ItemsSource="{Binding Source={StaticResource AllDataView}}" />

    <TabControl Grid.Column="1"
        <TabItem Header="Basic Info">
            <ContentControl x:Name="BasicInfoContent"
                            ContentTemplateSelector="{StaticResource MyDataTemplateSelector}"
                            Content="{Binding Source={StaticResource AllDataView}}">
            </ContentControl>
        </TabItem>
    </TabControl>
</Grid>

UnitItems и ComponentItems - это два ObservableCollection<T> объекта, определенных в App.xaml.cs. И я определил некоторые DataTemplates в App.xaml. Пример кода приведен ниже:

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="..."
        </ResourceDictionary.MergedDictionaries>

    <DataTemplate DataType="{x:Type src:Unit}">
        <!-- This template is to show the name of a unit object in the ListBox -->
    </DataTemplate>
    <DataTemplate DataType="{x:Type src:Component}">
        <!-- This template is to show the name of a component object in the ListBox -->
    </DataTemplate>

    <DataTemplate x:Key="unitTemplate" DataType="{x:Type src:Unit}">
        <!-- This template is to show the details of a unit object in the ContentControl -->
    </DataTemplate>

    <DataTemplate x:Key="componentTemplate" DataType="{x:Type src:Component}">
        <!-- This template is to show the details of a component object in the ContentControl -->
    </DataTemplate>
</Application.Resources>

А мой пользовательский DataTemplateSelector выглядит так:

class MyDataTemplateSelector : DataTemplateSelector
{
    public DataTemplate UnitTemplate { get; set; }
    public DataTemplate ComponentTemplate { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        swith (item)
        {
            case Unit _:
                return UnitTemplate;
            case Component _:
                return ComponentTemplate;
        }
        return null;
    }
}

Я прочитал эту статью ContentTemplateSelector и попробовал ContentTemplateSelector, но, поскольку я использую CompositeCollection и CollectionContainer для связывания этих двух видов объектов в ContentControl, объект item в моем классе DataTemplateSelector получает тип CompositeCollection , а не тип модуля или тип компонента, поэтому верный шаблон не возвращается. Также я попробовал метод, упомянутый в этой статье Свойство DataType , который должен установить свойство DataType для каждого из DataTemplate и установить путь к «/». Может быть, я неправильно понял, но это тоже не сработало, где я думаю, что у него такая же проблема, как и у ContentTemplateSelector. Кто-нибудь может мне помочь в этой проблеме?

Я впервые задаю вопрос о переполнении стека. Я знаю, что некоторые из моих описаний и кодов тривиальны для этого вопроса, но я просто не хочу пропустить какие-либо детали, которые могут быть связаны с моей проблемой. Я прошу прощения за это. Также, если есть какие-либо проблемы с моим стилем кодирования и структурой данных, пожалуйста, не стесняйтесь указывать на это. Я очень ценю это. Спасибо за ваше чтение и помощь!

1 Ответ

0 голосов
/ 13 сентября 2018

Вам не нужен DataTemplateSelector. Просто убедитесь, что подробные шаблоны данных могут быть выбраны автоматически, не назначая им ключ.

Также кажется, что вам не нужны две коллекции для ваших объектов. Вы также можете получить и Unit и Component из общего базового класса и иметь единую коллекцию ссылок на базовый класс.

Наконец, должна быть модель представления, которая помимо коллекции объектов также имеет свойство для текущего выбранного объекта.

Возьмите этот упрощенный пример представления модели:

public class Base
{
    public int Id { get; set; }
}

public class Unit : Base
{
    public string UnitData { get; set; }
}

public class Component : Base
{
    public string ComponentData { get; set; }
}

public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public ObservableCollection<Base> Objects { get; }
        = new ObservableCollection<Base>();

    private Base selectedObject;

    public Base SelectedObject
    {
        get { return selectedObject; }
        set
        {
            selectedObject = value;
            PropertyChanged?.Invoke(this,
               new PropertyChangedEventArgs(nameof(SelectedObject)));
        }
    }
}

Экземпляр этого должен быть назначен DataContext окна:

public MainWindow()
{
    InitializeComponent();

    var vm = new ViewModel();
    vm.Objects.Add(new Unit { Id = 1, UnitData = "Unit Data" });
    vm.Objects.Add(new Component { Id = 2, ComponentData = "Component Data" });

    DataContext = vm;
}

Наконец, XAML будет выглядеть так:

<ListBox ItemsSource="{Binding Objects}"
            SelectedItem="{Binding SelectedObject}">
    <ListBox.Resources>
        <DataTemplate DataType="{x:Type local:Unit}">
            <TextBlock>
                <Run Text="Unit, Id:"/>
                <Run  Text="{Binding Id}"/>
            </TextBlock>
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:Component}">
            <TextBlock>
                <Run Text="Component, Id:"/>
                <Run  Text="{Binding Id}"/>
            </TextBlock>
        </DataTemplate>
    </ListBox.Resources>
</ListBox>
<ContentControl Grid.Column="1" Content="{Binding SelectedObject}">
    <ContentControl.Resources>
        <DataTemplate DataType="{x:Type local:Unit}">
            <StackPanel>
                <TextBlock Text="{Binding Id}"/>
                <TextBlock Text="{Binding UnitData}"/>
            </StackPanel>
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:Component}">
            <StackPanel>
                <TextBlock Text="{Binding Id}"/>
                <TextBlock Text="{Binding ComponentData}"/>
            </StackPanel>
        </DataTemplate>
    </ContentControl.Resources>
</ContentControl>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...