ItemsControl中的页面绑定

时间:2019-04-01 01:20:52

标签: c# wpf binding

我正在尝试创建一个窗口,该窗口根据传递给构建器的集合来显示未知数量的页面。

对我而言,概念似乎很简单-创建模板页面,在初始化期间创建页面集合,并通过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的情况下手动进行所有绑定时(因此,从Collection中拾取头几个实体并将它们绑定到框架),一切正常,这使我相信ItemControl将Page传递到框架的方式是问题所在。

2 个答案:

答案 0 :(得分:1)

好的,这里有几件事。

首先,常规方法是将数据和视图分开。如果您用谷歌“ MVVM”或“ MVC”,您会看到很多关于此的信息。我个人建议使用MVVM。因此,通常不会有一个页面绑定到其他页面。显示的页面(“视图”)应绑定到数据(以及绑定到表示数据的“视图模型”),而不是其他页面。

第二,页面通常使用NavigateTo显示在“框架”中。您可能有一个包含Frames的StackPanel,然后将Page加载到每个Frame中,但这似乎有些过分。仅拥有一个ListView和一个用于细节的模板会更直接。如果您有多种类型的详细信息,则可以在列表视图中使用DataTemplateSelector。

第三,最终,您可能需要更新数据,为此,您将需要一个集合,该集合将有关所选实体详细信息列表更改的通知通知视图。这是列表的视图模型。同样,每个实体(在您的情况下为EntityDetail)都应在UI线程上通知其属性(或具有一个这样做的单独的视图模型对象),但是在这里我不会做太多介绍。我再次谈论的是常规的做事方式,当然还有许多其他方式。

所以……可能是这样的:

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>

答案 1 :(得分:1)

  

...这使我相信ItemControl将Page传递到框架的方式是问题所在。

您的代码无法正常工作的原因是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>