使用行和列标题动态生成2D网格

时间:2016-04-21 20:24:40

标签: wpf xaml datagrid grid reactiveui

我开始使用IReactiveList<ICoordVm>,其中:

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

我想从此列表创建一个网格,其中每个ViewModel的视图位于相应的X / Y坐标。此外,必须根据X和Y的ToString()值标记行和列。最后,我希望避免重新绘制整个网格,因为新项目会添加到我的列表中。

我正在考虑是否使用网格,数据网格或其他东西。使用第二个解决方案here中描述的数据表/数据网格似乎得到了行和标题列,但似乎我需要注入一个新的数据表并在每次添加项目时重绘屏幕。使用this解决方案中描述的GridHelper可能会给我一种避免重绘的方法,但是没有描述如何包含行和列标题。

任何人都有任何创意,如何解决这个问题?

1 个答案:

答案 0 :(得分:0)

这是我最终提出的解决方案。 This链接帮助很大;我直接盗取了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>

以下是我的viewmodel的精简版。基本技巧是当网格尺寸已知时调用Initialize方法;此方法使用以下内容填充网格:

  • Cells:ICoordViewModel类型的项目
  • 行标题:RowHeaderViewModel类型的项目
  • 列标题:ColumnHeaderViewModel类型的项目
  • Corner: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上其他位置的显示尺寸,并自动调整单元格的大小。

对任何不清楚的事情表示歉意。希望这里的代码能够为解决这个问题的其他人提供一些高级线索,同时避免过多不必要的细节。