我正在尝试创建一个窗口,该窗口根据传递给构建器的集合来显示未知数量的页面。
对我而言,概念似乎很简单-创建模板页面,在初始化期间创建页面集合,并通过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传递到框架的方式是问题所在。
答案 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>