What is the best way to display multidimensional data in WPF? I won't know the size/shape of the data until runtime. Ideally I would like to use databinding but that is not a strict requirement. I was thinking some sort of grid but I don't know how to dynamically bind to the data and have it figure out the number of rows and columns. Suggestions and examples please?
I've done something similar where the data was a List or Lists, or an array of arrays. Not sure how locked in you are to using multidimensional arrays versus this method for "grid like" data.
Here's how I bound against the List of Lists.
<Resources>
<DataTemplate x:Key="GridDtInner">
<TextBlock Text="{Binding YourProperty}"/>
</DataTemplate>
<DataTemplate x:Key="GridDtOuter">
<ItemsControl ItemsSource="{Binding}" ItemTemplate="{DynamicResource GridDtInner}" />
</DataTemplate>
</Resources>
<ItemsControl ItemTemplate="{DynamicResource GridDtOuter}" ItemsSource="{Binding YourList, Mode=Default}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Obviously I've snipped things for brevity but I hope this makes sense.
Regards,
Mike McAulay
I've done something very similar, but most likely in a completely different industry than what you're working in.
When you say size / shape of data, I assume that you're not literally talking about circles vs. squares, but instead are saying that you've got an NxM matrix of data and you don't know what N and M are until runtime. I also assume that you want to display some kind of text in each cell of the NxM matrix.
Please note that the following XAML has been modified from my code and isn't tested, but I wanted to give you a basic idea of how I did it.
<ListBox Width="300" Height="300" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ItemsSource="{Binding Cells}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Width="Auto" Height="Auto" ItemWidth="{Binding CellBoundary}" ItemHeight="{Binding CellBoundary}"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Width="{Binding CellDiameter}" Height="{Binding CellDiameter}" Text="{Binding CellValue}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I changed the ListBox's ItemsPanel to just be a ListBox. So as long as you have an NxM matrix (it can be sparse, too), you treat is as a vector with NxM cells. Each cell is effectively an item in the ListBox.
Next, you set the DataTemplate for each ListBox item to just be a TextBlock, where the TextBlock's size is databound and the value displayed is in the CellValue
member of your Cells, which is an ObservableCollection<double>
or similar, databound to Cells
.
The only thing you have to do at this point in code behind is to figure out what N and M are, and do the necessary math to calculate the size of the ListBox (could be inside of a ViewBox to make it scale), so that the values are displayed accordingly. Obviously, you'll need to change my hardcoded values to something databound as well.
This approach seems the simplest for me to wrap my brain around, so that's why I've been using it for my stuff.
I should also add that in order to do a sparse matrix, you'll set CellValue to 0, but you could also use a DataTrigger to make the TextBlocks invisible if the value is 0, thus giving the illusion of a sparse matrix.
I think the best solution for you is to just use TreeView :) if you're using MVVM, this is the most brilliant tutorial regarding TreeView i have found: http://www.codeproject.com/KB/WPF/TreeViewWithViewModel.aspx
Please keep me informed if you need more info.
I just found the DataGrid in the WPF Toolkit which is showing some potential. It at least allows selection of single cells. I will post code when I figure out the databinding.
http://wpf.codeplex.com/releases/view/40535
http://windowsclient.net/wpf/wpf35/wpf-35sp1-toolkit-datagrid-feature-walkthrough.aspx
Sounds as though you want an Excel-like interface for 2D arrays with editing capability. For the other dimensions, you will have to come up with tabs or a series of comboboxes.
Check out the WPF Toolkit DataGrid. There is an option for auto generate columns. Experiment with that.
However, you will have some code behind to deal with the other dimensions since the Datagrid can only represent 2D data.
Corey
Edit: (04/28/2010) Here is a working solution.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Data;
using Microsoft.Windows.Controls;
namespace WpfApplication2
{
public partial class MainWindow : Window
{
public List<List<object>> TheData { get; set; }
public MainWindow()
{
InitializeComponent();
// Generate some random data
Random r = new Random();
TheData = new List<List<object>>
{
new List<object> { r.Next(100), r.Next(100), r.Next(100),r.Next(100) },
new List<object> { r.Next(100), r.Next(100), r.Next(100),r.Next(100) },
new List<object> { r.Next(100), r.Next(100), r.Next(100) },
new List<object> { r.Next(100), r.Next(100), r.Next(100),r.Next(100) },
new List<object> { r.Next(100), r.Next(100), r.Next(100),r.Next(100) },
new List<object> { r.Next(100), r.Next(100), r.Next(100),r.Next(100), r.Next(100) }
};
// Now bind data to the grid
// We need at least one element
if (TheData.Count > 0)
{
// Find the longest row so we create enough columns
var max = TheData.Max(c => c.Count);
for (var i = 0; i < max; i++)
{
TheGrid.Columns.Add(
new DataGridTextColumn
{
Header = string.Format("Column: {0:00}", i),
Binding = new Binding(string.Format("[{0}]", i))
}
);
}
}
TheGrid.ItemsSource = TheData;
}
}
}
The XAML...
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:toolkit="http://schemas.microsoft.com/wpf/2008/toolkit"
Title="GridTest">
<Grid x:Name="LayoutGrid">
<toolkit:DataGrid x:Name="TheGrid"
AutoGenerateColumns="False"
IsReadOnly="False"
CanUserAddRows="False"/>
</Grid>
Some thing to note about this approach, if you allow editing of data and allow jagged arrays, you will need to new up a new List on the short rows.
As far as > 2D data, you will need some sort of option to select another dimension since the datagrid can only represent 2D data.