0

I need to display ,in a WPF app, a 'data matrix' that has no fixed tabular structure i.e.

data row 1: aaa bb ccc
data row 2: 222 $$ ddddd eeee
...
all data is in a list of arrays.

i need to show this data and enable a user to scroll through it.
the strange data structure comes from a textual flat file, and i cannot change this.

i have read a lot, and experimented with a list-view and a grid-view, but so far i am only able to get:

1. dymanic column creation, but this is no good since i have a different schema for each row

2. rendering each row of data as (delimited/formatted) text. but this is no good since all row data is endign up in a single cell (obviously).

3. (havn't done this yet, hoping to avoid) have lots of data templates with triggers, each with a diffrent column count - say from 5 to 40 - to acomodate vast majority of row types.

my question is: how do i bind this data to a list-like / grid-like scrollable view ?

Thanks in advance

Menahem
  • 3,974
  • 1
  • 28
  • 43
  • 1
    One pass to find the maximum number of columns. Build a simple Row class that has a property cols string[maxColCount]. Build the maxColCount columns for a GridView in code bind and bind the source to cols[x]. – paparazzo Apr 18 '13 at 14:04
  • @Blam hmmm , im going to try this. thanks! – Menahem Apr 18 '13 at 14:22

2 Answers2

2

If you don't need the ability to select an item, you can use nested ItemsControls

Simply bind the first ItemsControl to your collection of arrays, and set the ItemTemplate to another ItemsControl which binds to the array of values.

Something like this:

<ScrollViewer Height="300" Width="400">
    <ItemsControl ItemsSource="{Binding ListOfArrays}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <ItemsControl ItemsSource="{Binding }">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel Orientation="Horizontal" />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>

                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding }"  Width="50" />
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</ScrollViewer>
Rachel
  • 130,264
  • 66
  • 304
  • 490
  • Can this method be adapted to produce a table (e.g. DataGrid or GridView) rather than a series of stackpanels? – Stephen Holt Apr 18 '13 at 14:14
  • @StephenHolt I don't think so, or at least not easily. You could set the `ItemsPanelTemplate` to a `Grid`, however you'd need to know how many Rows and Columns your `Grid` needs, and what `Grid.Row` and `Grid.Column` each item goes in. What features do a `DataGrid` or `GridView` have that this wouldn't? (Note that this will result in tabular data because the width of each "Cell" is hardcoded to 50 - You could probably make that dynamic using a `Grid` and `Grid.SharedSizeScope`, but it's better on performance to hardcode a width if you can) – Rachel Apr 18 '13 at 14:18
  • @Rachel thanks (+1) for the fast response, i would like to have edit capability also (even though for now i`ll settle for read only) – Menahem Apr 18 '13 at 14:19
  • @Menahem You could use `TextBoxes` instead of `TextBlocks` for editing, although your binding will have to change to `Text="{Binding Path=.}` since you can't use a two-way binding without specifying a Path. Or to duplicate a DataGrid's functionality, use a `ContentControl` with it's `ContentTemplate` set to a `TextBlock`, and change the `ContentTemplate` to a `TextBox` OnClick :) – Rachel Apr 18 '13 at 14:27
  • @Rachel struggling to grasp all this, i`m trying this now. thanks again :) – Menahem Apr 18 '13 at 14:33
  • @Rachel I guess I was thinking of the ability to resize columns, have lines between the cells, that sort of thing. I guess you could build all that into the templates for the stackpanels but seems a lot of work for something you usually get for free! – Stephen Holt Apr 18 '13 at 14:50
  • @StephenHolt I just remembered that I saw a SO answer before where someone created a DataGrid2D control that is meant to be bound to a 2D array. You can find the source code an explanation for it in [this answer](http://stackoverflow.com/a/4002409/302677) - Might be worth checking out :) – Rachel Apr 18 '13 at 15:03
  • @Rachel i have seen the 2D grid, but your solution of nested `ItemControl`s is a better fit for me. i have tried it (nested) with virtualizing stack panels and a 10Mb `List`, but it just sucks up a 100`s MB of memory and CPU and never gets to the point of showing any GUI. any ideas? – Menahem Apr 19 '13 at 08:58
  • @Menahem No sorry, I've never used the 2D grid control myself – Rachel Apr 19 '13 at 11:50
  • @Rachel OK, ended up using a `ItemssControl` nested in a `Listview` and this list`s `VirtualizingStackPanel` option. Thanks! – Menahem Apr 20 '13 at 17:40
1

One pass to find the maximum number of columns.
Build a simple Row class that has a property cols string[maxColCount].
Build the maxColCount columns for a GridView in code bind and bind the source to cols[x].

The compiler needs a property but you can bind the column to an collection name index
Syntax for building and binding a GridViewColumn

            gvc = new GridViewColumn();
            gvch = new GridViewColumnHeader();
            gvch.Content = fd.FieldDef.DispName;
            gvch.HorizontalContentAlignment = System.Windows.HorizontalAlignment.Stretch;
            if (fd.FieldDef.Sort)
            {
                gvch.Click += new RoutedEventHandler(SortClick);
                gvch.Tag = fd.FieldDef.Name;
            }

            // if (fd.ID == 0 || fd.ID == 1) gvc.Width = 60; sID, sParID
            if (!fd.AppliedDispGrid) gvc.Width = 0;
            gvc.Header = gvch;

            gvBinding = new Binding();
            gvBinding.Mode = BindingMode.OneWay;
            gvBinding.Path = new PropertyPath("DocFields[" + sDocBaseResultDocsFieldsIndex.ToString() + "].DispValueShort");

            template = new DataTemplate();
            textblock = new FrameworkElementFactory(typeof(TextBlock));
            textblock.SetValue(TextBlock.TextProperty, gvBinding);
            textblock.SetValue(TextBlock.TextTrimmingProperty, TextTrimming.WordEllipsis);

            // <Setter Property="TextTrimming" Value="WordEllipsis" />

            template.VisualTree = new FrameworkElementFactory(typeof(Grid));
            template.VisualTree.AppendChild(textblock);

            gvc.CellTemplate = template;

            gvSearchResults.Columns.Add(gvc);
paparazzo
  • 44,497
  • 23
  • 105
  • 176
  • i accepted Rachel`s answer because i ended up using it, but that doesn't mean your solution would not work. someone else might find it helpful too. – Menahem Apr 20 '13 at 17:43