动态生成具有行和列的二维网格 headers
Dynamically generating 2D grid with row and column headers
我从 IReactiveList<ICoordVm>
开始,其中:
interface ICoordViewModel
{
object X {get;}
object Y {get;}
ViewModel ViewModel {get;}
}
我想从此列表创建一个网格,其中每个 ViewModel 的视图都位于适当的 X/Y 坐标处。此外,行和列必须根据 X 和 Y 的 ToString()
值进行标记。最后,我想避免在将新项目添加到我的列表时重绘整个网格。
我正在考虑是使用网格、数据网格还是其他东西。使用第二个解决方案 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 解决方案中描述的 datatable/datagrid 可能会给我一种避免重绘的方法,但没有描述如何包含行和列 headers。
有人有解决这个问题的创意吗?
这是我最终想出的解决方案。 This link 帮助很大;我直接盗版了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>
下面是我的视图模型的 stripped-down 版本。基本技巧是当网格尺寸已知时调用 Initialize 方法;此方法使用以下内容填充网格:
- 单元格:ICoordViewModel 类型的项目
- 行headers:RowHeaderViewModel
类型的项目
- 列headers: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,它为单元格宽度和高度提供了一个中心位置。这允许用户调整 UI 上其他地方的显示尺寸,并且单元格会自动调整大小。
对于任何不清楚的地方,我们深表歉意。希望此处的代码能够在为其他人解决此问题提供一些 high-level 线索和避免过多不必要的细节之间取得良好的平衡。
我从 IReactiveList<ICoordVm>
开始,其中:
interface ICoordViewModel
{
object X {get;}
object Y {get;}
ViewModel ViewModel {get;}
}
我想从此列表创建一个网格,其中每个 ViewModel 的视图都位于适当的 X/Y 坐标处。此外,行和列必须根据 X 和 Y 的 ToString()
值进行标记。最后,我想避免在将新项目添加到我的列表时重绘整个网格。
我正在考虑是使用网格、数据网格还是其他东西。使用第二个解决方案 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 解决方案中描述的 datatable/datagrid 可能会给我一种避免重绘的方法,但没有描述如何包含行和列 headers。
有人有解决这个问题的创意吗?
这是我最终想出的解决方案。 This link 帮助很大;我直接盗版了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>
下面是我的视图模型的 stripped-down 版本。基本技巧是当网格尺寸已知时调用 Initialize 方法;此方法使用以下内容填充网格:
- 单元格:ICoordViewModel 类型的项目
- 行headers:RowHeaderViewModel 类型的项目
- 列headers: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,它为单元格宽度和高度提供了一个中心位置。这允许用户调整 UI 上其他地方的显示尺寸,并且单元格会自动调整大小。
对于任何不清楚的地方,我们深表歉意。希望此处的代码能够在为其他人解决此问题提供一些 high-level 线索和避免过多不必要的细节之间取得良好的平衡。