Динамическое создание 2D-сетки с заголовками строк и столбцов

Я начинаю с IReactiveList<ICoordVm>, где:

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

Я хотел бы создать сетку из этого списка, где представление для каждой ViewModel расположено в соответствующей координате X/Y. Кроме того, строки и столбцы должны быть помечены, скажем, в соответствии с ToString() значениями X и Y. Наконец, я хотел бы избежать перерисовки всей сетки по мере добавления новых элементов в мой список.

Я обдумываю, использовать ли сетку, сетку данных или что-то еще. Использование datatable/datagrid, как описано во втором решении здесь, кажется, меня столбцы строк и заголовков, но похоже, что мне нужно будет вводить новую таблицу данных и перерисовывать экран каждый раз, когда добавляется элемент. Использование GridHelpers, как описано в этом решении, может дать мне способ избежать перерисовки, но нет описания того, как включать заголовки строк и столбцов.

У кого-нибудь есть творческие идеи, как решить эту проблему?


person ket    schedule 21.04.2016    source источник
comment
Если вам нужно абсолютное позиционирование, почему сетка, а не холст?   -  person Evk    schedule 21.04.2016
comment
Холст может быть другим вариантом, но на самом деле мне не нужно абсолютное позиционирование - X и Y - это просто категории, а не обязательно числа (несмотря на то, что я ссылался на координаты). Я мог бы рассчитать позиции X и Y, но я бы предпочел, чтобы элемент управления управлял позиционированием автоматически.   -  person ket    schedule 21.04.2016


Ответы (1)


Вот решение, которое я в конце концов придумал. Эта ссылка очень помогла ; Я пиратил 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>

Ниже приведена урезанная версия моей модели представления. Основная хитрость заключается в том, что метод Initialize вызывается, когда размеры сетки известны; этот метод заполняет сетку следующим:

  • Ячейки: элементы типа ICoordViewModel
  • Заголовки строк: элементы типа RowHeaderViewModel
  • Заголовки столбцов: элементы типа 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, который обеспечивает центральное расположение ширины и высоты ячейки. Это позволяет пользователю настраивать размеры отображения в другом месте пользовательского интерфейса, а размеры ячеек автоматически изменяются.

Извинения за все, что неясно. Надеемся, что код здесь обеспечит достойный баланс между предоставлением некоторых подсказок высокого уровня для других, решающих эту проблему, и избеганием слишком большого количества ненужных деталей.

person ket    schedule 29.04.2016