【发布时间】:2018-01-04 09:23:43
【问题描述】:
我有一个ObservableCollection 对象,这些对象使用名称和一组 3D 坐标进行分类。我还有一个ObservableCollection 层,每个层都应该包含一个 2D 网格。
问题设置
目标是使用ItemsControl,可能是嵌套的,以下列方式显示这些对象:如果 Z 坐标确定图层,并且 X 和 Y 坐标指定每个对象在网格上的位置,那么对象应显示在TabControl 中,其中TabItem 对应于Z 坐标并承载Grid,其中Grid.Row 和Grid.Column 属性确定对象名称在TabItem 上的写入位置。
重要提示:虽然每个 3D 坐标仅使用一次,但一个“条目”对象可能有多个 3D 坐标,因此可能会在网格上出现不同时间和/或不同的TabItems。
注意:数据模型不是一成不变的;如果问题可以用另一个数据模型解决,我愿意改变它。然而,显示是客户的要求——它可能会被修改,但我需要非常好的论据。
背景资料
对象看起来像这样(BindableBase 来自 Prism 库):
public class Entry : BindableBase {
public string Name { set; get; }
private EntryCoordinates coordinates;
public EntryCoordinates Coordinates {
set { SetProperty(ref coordinates, value); }
get { return coordinates; }
}
}
public class EntryCoordinates : BindableBase {
private int x;
public int X {
set { SetProperty(ref x, value); }
get { return x; }
}
private int y;
public int Y {
set { SetProperty(ref y, value); }
get { return y; }
}
private int z;
public int Z {
set { SetProperty(ref z, value); }
get { return z; }
}
}
Entry 对象托管在“入口层”中:
public class EntryLayer : ObservableCollection<Entry> {
}
最终,我希望能够通过 UI 修改 Entry 对象(实际上更复杂),因此双向数据绑定是绝对必要的。
到目前为止的努力
使用@Rachel 出色的WPF Grid Extension,我实现了一个ItemsControl,它根据需要填充了Grid:
<ItemsControl ItemsSource="{Binding EntryLayers, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid GridExtension.RowCount="{Binding RowCount}"
GridExtension.ColumnCount="{Binding ColumnCount}"
GridExtension.StarRows="{Binding StarRows}"
GridExtension.StarColumns="{Binding StarColumns}"
IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding Coordinates.X}"/>
<Setter Property="Grid.Column" Value="{Binding Coordinates.Y}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<GridCellThumb XCoordinate="{Binding Coordinates.X}"
YCoordinate="{Binding Coordinates.Y}">
<Thumb.Template>
<ControlTemplate>
<TileControl Entry="{Binding}"/>
</ControlTemplate>
</Thumb.Template>
</GridCellThumb>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
GridCellThumb 是一个自定义控件,允许拖放(为清楚起见,此处省略)并公开坐标依赖属性:
public class GridCellThumb : Thumb {
public static readonly DependencyProperty XCoordinateProperty = DependencyProperty.Register("XCoordinate", typeof(int), typeof(GridCellThumb));
public int XCoordinate {
get { return (int)GetValue(XCoordinateProperty); }
set { SetValue(XCoordinateProperty, value); }
}
public static readonly DependencyProperty YCoordinateProperty = DependencyProperty.Register("YCoordinate", typeof(int), typeof(GridCellThumb));
public int YCoordinate {
get { return (int)GetValue(YCoordinateProperty); }
set { SetValue(YCoordinateProperty, value); }
}
}
TileControl 是一个显示Entry 名称的用户控件:
<StackPanel Orientation="Vertical">
<Label Content="{Binding Path=Name}" />
</StackPanel>
我一直在寻找如何将原始 ItemsControl 包装在 TabControl 模板中,以便正确显示条目的目标,可能在不同时间。例如,绑定到Coordinates.Z 路径有效,但会创建与条目一样多的TabItems:
<TabControl ItemsSource="{Binding Entries}">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Header"
Value="{Binding Coordinates.Z}" />
<Setter Property="TabIndex"
Value="{Binding Coordinates.Z}" />
</Style>
</TabControl.ItemContainerStyle>
<!-- the ItemsControl goes here -->
</TabControl>
我已经尝试过@greg40 (nested ItemsControl)、@d.moncada (another nested ItemsControl) 和@Sheridan (going up the visual tree) 提出的解决方案,但在将Entry 与给定的EntryLayer。
有没有人有进一步的想法要探索?正如我所说,我也愿意重新构建我的数据模型,如果这会导致更简单的解决方案。
2018-01-08 更新
我已经探索了使用Buttons 而不是TabControl 的路径,将它们的点击状态数据绑定到在网格上显示不同的信息。然而,这只是转移了问题并产生了一个新问题:不再预加载数据,这对客户的要求至关重要。
我现在正在强烈考虑向客户提出改变要求的建议,并设计一个完全不同的数据模型。为了避免再次走错路,如果社区中有人对这个问题有强烈的意见并愿意与我分享,我将不胜感激。
【问题讨论】:
-
另一个想法:
Entry和Coordinate之间存在一对多的关系。Coordinate确定图层和网格。将ItemsControl作为外部标签并将TabControl作为内部标签是否更可行?但是如何渲染TabItems 以便它们包含相应的Grids?