0

I'm starting out with an IReactiveList<ICoordVm> where:

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

I'd like to create a grid from this list where the view for each ViewModel is located at the appropriate X/Y coordinate. Additionally, the rows and columns must be labeled according to, say, the ToString() values of X and Y. Finally, I'd like to avoid redrawing the entire grid as new items are added to my list.

I'm deliberating whether to use a grid, datagrid, or something else. Using a datatable/datagrid as described in the second solution 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 solution might give me a way to avoid the redraw, but there's no description how to include the row and column headers.

Anyone have any creative ideas how to tackle this?

Community
  • 1
  • 1
ket
  • 728
  • 7
  • 22
  • If you need absolute positioning, why grid and not canvas? – Evk Apr 21 '16 at 20:31
  • Canvas may be another option, but I don't actually need absolute positioning - X and Y are just categories, not necessarily numbers(despite the fact that I made reference to coordinates). I could conceivably calculate the X and Y positions, but I'd prefer to have a control handle the positioning automagically. – ket Apr 21 '16 at 20:45

1 Answers1

0

Here's the solution I eventually came up with. This link helped tremendously; I pirated the XAML directly:

<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>

Below is a stripped-down version of my viewmodel. The basic trick is that an Initialize method is called when the grid dimensions are known; this method populates the grid with the following:

  • Cells: items of type ICoordViewModel
  • Row headers: items of type RowHeaderViewModel
  • Column headers: items of type ColumnHeaderViewModel
  • Corner: item of type CornerHeaderViewModel.

Next, as new viewmodels arrive, the Add method is called to find the X/Y location where the viewmodel should be placed, and it is injected into the placeholder; and as this happens it is displayed in the grid in the appropriate location. (All viewmodels are template in 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; }
    }
}

Finally there is a DisplayEditorViewModel which provides a central location for cell width and height. This allows the user to adjust the display dimensions elsewhere on the UI and the cells are automatically resized.

Apologies for anything that's unclear. Hopefully the code here will provide a decent balance between providing some high-level clues for others tackling this issue while avoiding too much unnecessary detail.

Community
  • 1
  • 1
ket
  • 728
  • 7
  • 22