Каков наилучший способ отображения многомерных данных в WPF? Я не буду знать размер/форму данных до времени выполнения. В идеале я хотел бы использовать привязку данных, но это не является строгим требованием. Я думал о какой-то сетке, но я не знаю, как динамически привязываться к данным и определять количество строк и столбцов. Предложения и примеры, пожалуйста?
Отображение многомерных данных в WPF
Ответы (5)
Звучит так, как будто вам нужен Excel-подобный интерфейс для 2D-массивов с возможностью редактирования. Для других измерений вам придется придумать вкладки или серию выпадающих списков.
Ознакомьтесь с WPF Toolkit DataGrid. Есть возможность автоматически генерировать столбцы. Поэкспериментируйте с этим.
Однако у вас будет некоторый код для работы с другими измерениями, поскольку Datagrid может представлять только 2D-данные.
Кори
Изменить: (28.04.2010) Вот рабочее решение.
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;
}
}
}
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.
Что касается> 2D-данных, вам понадобится какая-то опция для выбора другого измерения, поскольку сетка данных может представлять только 2D-данные.
Я думаю, что лучшее решение для вас — просто использовать TreeView :) Если вы используете MVVM, это самое блестящее руководство по TreeView, которое я нашел: http://www.codeproject.com/KB/WPF/TreeViewWithViewModel.aspx
Пожалуйста, держите меня в курсе, если вам нужна дополнительная информация.
Я сделал что-то подобное, когда данные были списком или списками или массивом массивов. Не уверен, насколько вы привязаны к использованию многомерных массивов по сравнению с этим методом для данных, подобных сетке.
Вот как я связался со списком списков.
<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>
Очевидно, я вырезал вещи для краткости, но я надеюсь, что это имеет смысл.
С уважением,
Майк МакОлей
Я делал что-то очень похожее, но, скорее всего, в совершенно другой отрасли, чем та, в которой вы работаете.
Когда вы говорите размер/форма данных, я предполагаю, что вы не имеете в виду буквально круги и квадраты, а вместо этого говорите, что у вас есть матрица данных NxM, и вы не знаете, что такое N и M, пока время выполнения. Я также предполагаю, что вы хотите отображать какой-то текст в каждой ячейке матрицы NxM.
Обратите внимание, что следующий XAML был изменен из моего кода и не тестировался, но я хотел дать вам общее представление о том, как я это сделал.
<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>
Я изменил ItemsPanel ListBox, чтобы он был просто ListBox. Итак, пока у вас есть матрица NxM (она тоже может быть разреженной), вы рассматриваете ее как вектор с ячейками NxM. Каждая ячейка фактически является элементом в ListBox.
Затем вы устанавливаете DataTemplate для каждого элемента ListBox просто как TextBlock, где размер TextBlock привязан к данным, а отображаемое значение находится в элементе CellValue
ваших ячеек, который является ObservableCollection<double>
или подобным, привязанным к данным Cells
.
Единственное, что вам нужно сделать на этом этапе кода, — это выяснить, что такое N и M, и выполнить необходимые математические действия для расчета размера ListBox (может быть внутри ViewBox, чтобы сделать его масштабируемым), чтобы значения отображаются соответствующим образом. Очевидно, вам нужно будет изменить мои жестко запрограммированные значения на что-то связанное с данными.
Этот подход кажется мне самым простым для понимания, поэтому я использую его для своих целей.
Я также должен добавить, что для создания разреженной матрицы вы установите для CellValue значение 0, но вы также можете использовать DataTrigger, чтобы сделать TextBlocks невидимыми, если значение равно 0, создавая таким образом иллюзию разреженной матрицы.
Я только что нашел DataGrid в наборе инструментов WPF, который демонстрирует некоторый потенциал. По крайней мере, это позволяет выбирать отдельные ячейки. Я опубликую код, когда выясню привязку данных.
http://wpf.codeplex.com/releases/view/40535
http://windowsclient.net/wpf/wpf35/wpf-35sp1-toolkit-datagrid-feature-walkthrough.aspx