Wpf DataGrid:动态DataGridTemplateColumn的动态DataTemplate
我想在datagrid中显示数据,其中的数据是Wpf DataGrid:动态DataGridTemplateColumn的动态DataTemplate,wpf,silverlight,datagrid,datatemplate,datagridtemplatecolumn,Wpf,Silverlight,Datagrid,Datatemplate,Datagridtemplatecolumn,我想在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; } p
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; }
...
}
当我计划在运行时更改它时,我希望为每个候选对象设置一个DataTemplate
——用户可以选择在不同的列中显示关于候选对象的哪些信息(候选对象只是一个示例,我有不同的对象)。这意味着我还想在运行时更改列模板,尽管这可以通过一个大模板并折叠其部分来实现
我知道实现目标的两种方法(两种方法非常相似):
AutoGeneratingColumn
事件并创建候选列XamlReader
从字符串加载DataTemplate
。在此之前,我必须编辑字符串以将绑定更改为所需候选
有没有更好的方法来创建具有未知数量DataGridTemplateColumn的DataGrid?
注:此问题基于
Edit:由于我需要同时支持WPF和Silverlight,我创建了自己的
DataGrid
组件,该组件具有DependencyProperty
用于绑定列集合。当集合更改时,我会更新列。例如,我们创建2个数据模板和一个ContentControl:
<DataTemplate DataType="{x:Type viewModel:VariantA}"> <dataGrid...> </DataTemplate>
<DataTemplate DataType="{x:Type viewModel:VariantB}"> <dataGrid...> </DataTemplate>
<ContentControl Content="{Binding Path=GridModel}" />
现在,如果将GridModel属性(例如type对象)设置为Variata或VariantB,它将切换DataTemplate
Variata&B示例实现:
public class VariantA
{
public ObservableCollection<ViewModel1> DataList { get; set; }
}
public class VariantB
{
public ObservableCollection<ViewModel2> DataList { get; set; }
}
公共类变量
{
公共ObservableCollection数据列表{get;set;}
}
公共类
{
公共ObservableCollection数据列表{get;set;}
}
希望这有帮助。我不知道这是否是一种“更好”的方式,因为这仍然很难看,但我个人喜欢这样:
- 在xaml中创建模板
- 使用多绑定,将当前绑定+对列的绑定结合起来,以获得“正确”的dataContext(即:单元格而不是行)
- 在此绑定上使用转换器获取所需属性的值,如果要检索多个属性,可以选择添加参数李>
<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>
这保存了MVVM模型,我更喜欢这种方式,因为我真的不喜欢使用xaml解析器来创建“动态”数据模板,但从我的角度来看,这仍然是一种丑陋的攻击
我希望微软的人能给我们一种方法,让我们把单元格而不是行作为数据上下文,以便能够动态生成模板列
希望这有帮助
编辑:在您的情况下,转换器实际上应该简单得多(如果我没有弄错的话,您可以直接返回单元格的实例,并且您不需要任何参数),但我保留了更复杂的版本,以防万一,其他人也有类似的问题我一直在研究一个类似的问题,只发现了一些有用的模式。整个“动态列”问题在silverlight中是一个有趣的问题 昨天我在Travis Pettijohn的网站上搜索到了这个页面 之前,我一直在使用“索引转换器”模式,该模式的工作原理非常好。。。只要使用
DataGridTextColumn
。一切都可以在代码隐藏中完成,我在运行时应用样式也没有问题。但是,我现在的要求是应用一些“单元格级别”格式—更改单元格的背景等—这意味着需要DataGridTemplateColumn
对我来说,DataGridTemplateColumn
的最大问题是无法在代码中设置绑定。我知道我们可以通过解析xaml来构建它,但和其他人一样,这似乎是一个巨大的黑客攻击,无法维护到第n个
特拉维斯(上面的第一个链接)描述的模式完全不同。在“运行时”(即页面加载时),在网格中创建所需的列。这意味着迭代您的集合,并为每个项添加一列,其中包含适当的标题等。然后为RowLoaded
事件实现一个处理程序,当加载每一行时,只需将每个单元格的DataContext设置为父项的适当属性/属性索引
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];
}
}
private void MyGrid\u行已加载(对象发送方,事件参数e)
{
var grid=发送方作为数据网格;
var myItem=grid.SelectedItem作为MyClass;
foreach(int i=0;i
这使我不再需要使用索引转换器。在设置cell.DataContext
时,您可能可以使用绑定
,但对我来说,让模板直接绑定到底层对象更容易
我现在计划拥有多个模板(每个模板都可以绑定到我的单元格对象上的相同属性),并在页面加载时在它们之间切换。非常整洁的解决方案。我明白你的意思,不幸的是我需要列模板,而不是整个网格模板。虽然我不能使用它,但这是一个有趣的想法,因为Silverlight不支持
多绑定。
。arf,很抱歉,我不熟悉Silverlight,所以我不知道。我可以确认这在WPF中非常有效,我今天再次使用了它,并开始对这个方法感到非常满意,尽管它很难看。你有没有得到这个问题的解决方案或答案?没有。正如我写的那样,我最终得到了自定义DataGrid
控件。但是我正在考虑开发一个基于Grid
的自定义控件,因为DataGrid
对于我的任务来说非常繁重。这个rowload
看起来是一个非常好的解决方案。虽然我不认为这是一个错误
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();
}
}
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];
}
}