我想在datagrid中显示数据,其中数据是
的集合public class Thing
{
public string Foo { get; set; }
public string Bar { get; set; }
public List<Candidate> Candidates { get; set; }
}
public class Candidate
{
public string FirstName { get; set; }
public string LastName { get; set; }
...
}
候选人名单中的候选人数量在运行时会有所不同。
所需的网格布局如下所示
Foo | Bar | Candidate 1 | Candidate 2 | ... | Candidate N
我希望每个 Candidate 都有一个DataTemplate
,因为我计划在运行时更改它 - 用户可以选择在不同列中显示候选人的哪些信息(候选人只是一个例如,我有不同的对象)。这意味着我还想在运行时更改列模板,尽管这可以通过一个大模板实现并折叠其部分。
我知道如何实现目标的两种方式(两者非常相似):
AutoGeneratingColumn
事件并创建候选人列在这两种情况下,我都需要使用DataTemplate
从字符串加载XamlReader
。在此之前,我必须编辑字符串以将绑定更改为想要的 Candidate 。
有没有更好的方法来创建具有未知数量的DataGridTemplateColumn的DataGrid?
注意:此问题基于dynamic datatemplate with valueconverter
编辑:由于我需要同时支持WPF和Silverlight,因此我创建了自己的DataGrid
组件,其中DependencyProperty
用于绑定一组列。当集合发生变化时,我会更新列。
答案 0 :(得分:2)
例如,我们创建2个DataTemplates和一个ContentControl:
<DataTemplate DataType="{x:Type viewModel:VariantA}"> <dataGrid...> </DataTemplate>
<DataTemplate DataType="{x:Type viewModel:VariantB}"> <dataGrid...> </DataTemplate>
<ContentControl Content="{Binding Path=GridModel}" />
现在,如果将GridModel属性(例如类型对象)设置为VariantA或VariantB,它将切换DataTemplate。
VariantA&amp; B示例实施:
public class VariantA
{
public ObservableCollection<ViewModel1> DataList { get; set; }
}
public class VariantB
{
public ObservableCollection<ViewModel2> DataList { get; set; }
}
希望这有帮助。
答案 1 :(得分:0)
我不知道这是否是一种“更好”的方式,因为这仍然非常难看,但我个人确实喜欢这样:
例如:(抱歉,我没有调整我的代码以适应您的项目,但您应该可以从那里自己完成)
这是我的dataTemplate:
<DataTemplate x:Key="TreeCellTemplate">
<Grid>
<TextBlock HorizontalAlignment="Left" VerticalAlignment="Center" Margin="5,0,0,0">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource RowColumnToCellConverter}" ConverterParameter="Text">
<Binding />
<Binding RelativeSource="{RelativeSource AncestorType=DataGridCell}" Path="Column" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Grid>
</DataTemplate>
这是我的转换器:
public class RowColumnToCellConverter : MarkupExtension, IMultiValueConverter
{
public RowColumnToCellConverter() { }
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
XwpfRow row = values[0] as XwpfRow;
XwpfTreeColumn column = values[1] as XwpfTreeColumn;
if (row == null || column == null) return DependencyProperty.UnsetValue;
TreeCell treeCell = (TreeCell)row[column.DataGrid.Columns.IndexOf(column)];
switch ((string)parameter)
{
case "Text": return treeCell.Text;
case "Expanded": return treeCell.Expanded;
case "ShowExpandSymbol": return treeCell.ShowExpandSymbol;
case "CurrentLevel": return new GridLength(treeCell.CurrentLevel * 14);
default:
throw new MissingMemberException("the property " + parameter.ToString() + " is not defined for the TreeCell object");
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return new RowColumnToCellConverter();
}
}
这样可以保存MVVM模型,我更喜欢这种做法,因为我真的不喜欢使用xaml解析器制作“动态”数据模板,但从我的角度来看,它仍然是一个丑陋的Hack。
我希望MS的工作人员能够为我们提供一种方法来获取单元格而不是行作为dataContexts,以便能够动态生成模板化的列...
希望这会有所帮助
编辑:在你的情况下,转换器实际应该更简单(你可以直接返回单元格的实例,如果我没有弄错,你不需要任何参数),但是我离开了更复杂的版本,以防其他人有类似的问题
答案 2 :(得分:0)
我一直在寻找类似的问题,并且只发现了一些有用的模式。整个“动态专栏”问题在silverlight中是一个有趣的问题。
昨天我在搜索期间在Travis Pettijohn的网站上找到了这个页面Silverlight DataGrid with Dynamic Columns。
以前我一直在使用Colin Eberhardt概述的'索引转换器'模式,只要您使用DataGridTextColumn
,效果非常好。一切都可以在后面的代码中完成,我在运行时应用样式没有问题。但是我现在要求应用一些“单元格级”格式 - 更改单元格的背景等 - 这意味着需要DataGridTemplateColumn
。
DataGridTemplateColumn
对我来说最大的问题是我无法在代码中设置绑定。我知道我们可以通过解析xaml来构建它,但是像其他人一样,看起来像是一个大规模的黑客攻击并且无法维护到第n个。
Travis概述的模式(上面的第一个链接)完全不同。在“运行时”(即页面加载时间),在网格中创建所需的列。这意味着遍历您的集合,并为每个项添加一个具有相应标头的列等。然后为RowLoaded
事件实现一个处理程序,并且当每行加载时,只需为每个单元格设置DataContext < / em>到父级的相应属性/属性索引。
private void MyGrid_RowLoaded(object sender, EventArgs e)
{
var grid = sender as DataGrid;
var myItem = grid.SelectedItem as MyClass;
foreach (int i = 0; i < myItem.ColumnObjects.Count; i++)
{
var column = grid.Columns[i];
var cell = column.GetCellContent(e.Row)
cell.DataContext = myItem.ColumnObjects[i];
}
}
这使我无需使用索引转换器。您可以在设置Binding
时使用cell.DataContext
,但对我而言,模板更容易直接绑定到基础对象。
我现在计划拥有多个模板(每个模板可以绑定到我的单元格对象上的相同属性),并在页面加载时切换它们。非常整洁的解决方案。