动态生成具有行和列的二维网格 headers

Dynamically generating 2D grid with row and column headers

我从 IReactiveList<ICoordVm> 开始,其中:

interface ICoordViewModel
{
     object X {get;}
     object Y {get;}
     ViewModel ViewModel {get;}
}

我想从此列表创建一个网格,其中每个 ViewModel 的视图都位于适当的 X/Y 坐标处。此外,行和列必须根据 X 和 Y 的 ToString() 值进行标记。最后,我想避免在将新项目添加到我的列表时重绘整个网格。

我正在考虑是使用网格、数据网格还是其他东西。使用第二个解决方案 here seems to get me the row and header columns, but it seems like I'll need to inject a new datatable and redraw the screen every time an item is added. Using GridHelpers as described in this 解决方案中描述的 datatable/datagrid 可能会给我一种避免重绘的方法,但没有描述如何包含行和列 headers。

有人有解决这个问题的创意吗?

这是我最终想出的解决方案。 This link 帮助很大;我直接盗版了XAML:

<DataTemplate x:Key="DataTemplateLevel2">
    <ContentPresenter Content="{Binding}"/>
</DataTemplate>

<DataTemplate x:Key="DataTemplateLevel1">
    <ItemsControl ItemsSource="{Binding}" ItemTemplate="{DynamicResource DataTemplateLevel2}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</DataTemplate>

<DataTemplate DataType="{x:Type viewModels:DisplayGridViewModel}">
    <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
        <ItemsControl ItemsSource="{Binding Lists}" ItemTemplate="{DynamicResource DataTemplateLevel1}"/>
    </ScrollViewer>
</DataTemplate>

下面是我的视图模型的 stripped-down 版本。基本技巧是当网格尺寸已知时调用 Initialize 方法;此方法使用以下内容填充网格:

  • 单元格:ICoordViewModel 类型的项目
  • 行headers:RowHeaderViewModel
  • 类型的项目
  • 列headers:ColumnHeaderViewModel
  • 类型的项目
  • 角落:CornerHeaderViewModel 类型的项目。

接下来,当新的视图模型到达时,调用Add方法找到视图模型应该放置的X/Y位置,并将其注入到占位符中;当发生这种情况时,它会显示在适当位置的网格中。 (所有视图模型都是 XAML 中的模板)。

public class DisplayGridViewModel
{
    // Fields and constructor.

    public void Initialize()
    {
        var rows = GetRows().ToList();
        var columns = GetColumns().ToList();
        _lists.Clear();
        var cornerHeader = new CornerHeaderDisplayViewModel(_displayEditor /* + other content */);
        var columnHeaders = columns.Select(c => new ColumnHeaderViewModel(c, _displayEditor));
        _lists.Add(new ReactiveList<object>(Enumerable.Repeat((object)cornerHeader, 1).Union(columnHeaders)));
        var index = 1;
        foreach (var row in rows)
        {
            _lists.Add(new ReactiveList<object>());
            _lists[index].Add(new RowHeaderViewModel(row, _displayEditor /* + other content */));
            foreach (var column in columns)
            {
                _lists[index].Add(new CellViewModel(_displayEditor /* + other content */));
            }
            index++;
        }
    }

    private IEnumerable<object> GetRows()
    {
        // custom implementation            
    }

    private IEnumerable<object> GetColumns()
    {
        // custom implementation
    }

    public void Add(ICoordChart coordChart)
    {
        var match = _lists.SelectMany(l => l).OfType<CoordViewModel>().Single(cc => IsMatch(cc, coordChart));
        match.ViewModel = coordChart.ViewModel;
    }

    private static bool IsMatch(ICoordChart cc, ICoordChart chart)
    {
        // custom implementation
    }

    private readonly IReactiveList<IReactiveList<object>> _lists = new ReactiveList<IReactiveList<object>>();

    public IReactiveList<IReactiveList<object>> Lists
    {
        get { return _lists; }
    }
}

最后还有一个 DisplayEditorViewModel,它为单元格宽度和高度提供了一个中心位置。这允许用户调整 UI 上其他地方的显示尺寸,并且单元格会自动调整大小。

对于任何不清楚的地方,我们深表歉意。希望此处的代码能够在为其他人解决此问题提供一些 high-level 线索和避免过多不必要的细节之间取得良好的平衡。