ItemsControl显示多个数据层

时间:2018-01-04 09:23:44

标签: c# wpf xaml mvvm

我有一个ObservableCollection个对象,这些对象被分类为一个名称和一组3D坐标。我还有一个ObservableCollection层,每层都应该有一个2D网格。

问题设置

目标是使用ItemsControl(可能是嵌套的)以下列方式显示这些对象:如果Z坐标确定图层,并且X和Y坐标指定每个对象在网格上的位置,则该对象应显示在TabControl中,其中TabItem对应于Z坐标,并托管Grid,其中Grid.RowGrid.Column属性确定对象的名称写在TabItem

重要提示:虽然每个3D坐标只使用一次,但一个“Entry”对象可能有多个3D坐标,因此可能会在网格和/或不同的TabItems上出现不同的时间。

注意:数据模型不是刻在石头上;如果问题可以通过其他数据模型解决,我愿意改变它。然而,显示器是客户要求 - 它可能会被修改,但我需要非常好的论据。

背景资料

对象看起来像这样(BindableBase来自Prism Library):

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

我已经探索了使用Button而不是TabControl的路径,即将数据绑定到点击状态以在网格上显示不同的信息。然而,这只是改变了问题并创建了一个新问题:数据不再预先加载,这对客户的要求至关重要。

我现在正在考虑建议改变对客户的要求,并完全设计出不同的数据模型。为了避免再次走错路线,我非常感谢社区中有人对这个问题有强烈意见,他们愿意与我分享。

1 个答案:

答案 0 :(得分:0)

在获得同行专家提供的一些非常有价值的离线建议后,我决定反对客户建议的数据模型,并为他们提供替代解决方案。现在的方法是以自上而下的方式填充 View

实质上,这意味着之前我的 Z 坐标现在是定义<%= stylesheet_link_tag .... <%= javascript_include_tag .... s的专用EntryLayer对象的索引。每个TabItem都有一个EntryLayer,指的是那些ObservableCollection<Entry>部分的Entry

这与初始模型形成对比,因为现在不再可能EntryLayer可以有多个坐标;相反,Entry本身可能会以不同的坐标多次存在。

我是如何将其出售给客户的?我告诉他们,现在Entry可能有自定义选项,可能在相同或其他Entry的表示之间有所不同,例如,通过使用用户指定的颜色和字体,同时仍然指向相同的基础信息。它为我提供了更多的实现工作,但它通过将其置于新功能的形状中优雅地解决了僵局。