Связывание страницы в ItemsControl - PullRequest
0 голосов
/ 01 апреля 2019

Я пытаюсь создать окно, которое отображает неизвестное количество страниц в зависимости от коллекции, переданной разработчику.

Концепция казалась мне довольно простой - создать шаблон Page, создать коллекцию страниц во время инициализации и отобразить их через ItemControl. Итак:

Класс контекста данных:

class CollectionDetailsWindowContext
{
    public List<EntityDetailsPage> Pages { get; }

    public CollectionDetailsWindowContext(Collection _collection)
    {
        Pages = new List<EntityDetailsPage>();

        foreach (var entity in _collection.Entities)
            Pages.Add(new EntityDetailsPage(entity));
    }
}

XAML:

<ItemsControl ItemsSource="{Binding Pages}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Frame Content="{Binding .}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Если я запускаю этот код, это приводит к следующему исключению

System.InvalidOperationException: 'Page can have only Window or Frame as parent.'

Когда я пытаюсь сделать все привязки вручную без ItemControl (так, выбирая первые несколько Entites из Collection и привязывая их к фреймам), все работает нормально, что приводит меня к мысли, что проблема заключается в том, как ItemControl передает страницу в кадр.

Ответы [ 2 ]

1 голос
/ 01 апреля 2019

... что наводит меня на мысль, что проблема в том, как ItemControl передает страницу в кадр.

Причина, по которой ваш код не работает, заключается в том, что ItemTemplate не применяется к UIElements, например Pages.

Вы можете исправить это, создав собственный пользовательский ItemsControl и переопределив метод IsItemItsOwnContainerOverride:

public class FrameItemsControl : ItemsControl
{
    protected override bool IsItemItsOwnContainerOverride(object item)
    {
        return false;
    }
}

Реализация по умолчанию возвращает (item is UIElement).

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

<local:FrameItemsControl ItemsSource="{Binding Pages}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Frame Content="{Binding .}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</local:FrameItemsControl>
1 голос
/ 01 апреля 2019

ОК, так, пара вещей здесь.

Во-первых, традиционный подход заключается в разделении данных и представления.Если вы гуглите «MVVM» или «MVC», вы увидите тонну об этом.Лично я рекомендую MVVM.Таким образом, у вас обычно не было бы страницы, которая связывается с другими страницами.Отображаемая страница («представление») должна привязываться к данным (ну, к «модели представления», представляющей данные), а не к другим страницам.

Во-вторых, страницы обычно отображаются в кадрах с помощью NavigateTo.Вы можете иметь StackPanel, содержащую фреймы, а затем загружать страницу в каждый фрейм, но это выглядит как избыточное убийство.Было бы более прямым просто иметь ListView и шаблон для деталей.Если у вас есть несколько типов детализации, вы можете использовать DataTemplateSelector в представлении списка.

В-третьих, в конечном итоге вы, вероятно, захотите обновить данные, для чего вам понадобится коллекция, которая уведомляет представление о представлении.изменения в выбранном списке деталей объекта.Это модель представления списка.Также каждая сущность (EntityDetail в вашем случае) должна уведомлять о своих свойствах в потоке пользовательского интерфейса (или иметь отдельный объект модели представления, который делает это), но я не буду вдаваться в подробности здесь.Опять же, я говорю об обычном способе ведения дел, конечно, есть и много других способов.

Итак ... что-то вроде этого возможно:

partial class DetailsPage {
    public ObservableCollection<EntityDetail> EntityDetails {get;}

    public DetailsPage( ObservableCollection<EntityDetail> entityDetails) {
        this.EntityDetails = entityDetails;
    } 
}

<Page
    x:Class="DetailsPage"
    …
    >
    <Page.Resources>
        <ResourceDictionary>
            <DataTemplate x:Name="EntityDetailDataTemplate" x:DataType="local:EntityDetail">
                <TextBlock Text={x:Bind Name, Mode=OneWay}/> (assuming Name is a property in EntityDetail)
            </DataTemplate>
        </ResourceDictionary>
    </Page.Resources>
    <ListView
        ItemsSource={x:Bind EntityDetails, Mode=OneWay}
        ItemTemplate={StaticResource EntityDetailDataTemplate}
        …
        >
    </ListView>
</Page>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...