如何更改动态数据网格(绑定到数据表)中的单元格模板?



我正在尝试根据保存单元格类型的List<List<int>>更改DataGrid内单元格的模板(即 1=布尔值、2=整数、3=字符串、4=自定义等(。自定义类型(类型,因为它们可以大于 1(必须由ComboBox表示。对于数字和字符串,我需要正常的TextBox,对于布尔值,我需要CheckBoxDataGrid绑定到一个DataTable,我可以在运行时调整大小和编辑该。这是一些代码:

<Grid>
<DataGrid ItemsSource="{Binding Path=DataTable}" Name="Grid" AutoGenerateColumns="True" 
CanUserResizeRows="True" CanUserDeleteRows="False"
CanUserAddRows="False" AreRowDetailsFrozen="False"
SelectionUnit="Cell" LoadingRow="Grid_LoadingRow">
<DataGrid.Style>
<Style TargetType="DataGrid">
<Setter Property="AlternatingRowBackground" Value="LightYellow"/>
</Style>
</DataGrid.Style>
</DataGrid>
</Grid>
public partial class TableEditorWindow : Window
{
public string[] DebugNames = { "Bob", "Dan", "Pierre", "Mark", "Gary" };
// Stores the values of the Table
public ds_grid Table { get; set; }
// Stores the types of each cell in the Table
public ds_grid ValueTypesTable { get; set; }
// Used as wrapper between the Table variable and the DataGrid
public DataTable DataTable { get; set; }

public TableEditorWindow()
{
InitializeComponent();
Table = new ds_grid(5, 5);
// Fills the Table with 1s
for (int i = 0; i < 5; ++i)
{
for (int j = 0; j < Table.Width; ++j)
{
Table.Set(i, j, 1d);
}
}
DataTable = new DataTable();
// Add the columns
for (int i = 0; i < 5; ++i)
{
DataTable.Columns.Add(DebugNames[i]);
}
// Add the rows
for (int i = 0; i < Table.Height; ++i)
{
DataRow _row = DataTable.NewRow();
for (int j = 0; j < Table.Width; ++j)
{
_row[j] = Table.Get(j, i);
}
DataTable.Rows.Add(_row);
}
Grid.DataContext = this;
Grid.RowHeaderWidth = 50;
Grid.ColumnWidth = 100;
}
// Gives to each row the correct name
private void Grid_LoadingRow(object sender, DataGridRowEventArgs e)
{
int _id = e.Row.GetIndex();
e.Row.Header = DebugNames[_id];
}
}

ds_grid基本上是一个List<List<object>>,周围有一些实用方法。

我看到有一些解决方案,例如使用 DataTrigger,但我认为在这种情况下,我需要在 XAML 文件中的DataGrid中写入,但我不能,因为AutoGenerateColumnsTrue。还可以更改DataTable每列的类型,但我不希望该列的每个单元格都是该类型,我希望在运行时只有一个单元格成为该类型。

也许有更好的解决方案,例如不使用DataGrid,或者不使用DataTable,或者有一种方法可以将AutoGenerateColumns设置为False并在需要时通过代码手动生成每一列。任何建议都非常感谢。

提前谢谢。

这与我原来的答案有很大不同,所以我单独提交。

我还想指出,这是对DataGrid的一种非常非常规的使用。常见的数据结构是每列都有单一的类型,否则我几乎不需要。如果你坚持这个约定,我之前的答案是有效的。话虽如此,您所要求的是可以做到的。

如果你真的想忽略常见的数据结构,在单元格级别自定义东西,你需要一个自定义DataGridColumn

public class DataTableBoundColumn : DataGridBoundColumn
{
protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
{
if (dataItem == CollectionView.NewItemPlaceholder) { return null; }
DataRowView dataRow = (dataItem as DataRowView);
if (dataRow == null) { throw new ArgumentException(); }
object cellData = dataRow[cell.Column.DisplayIndex];
var contentHost = new ContentControl() { Content = cellData };
//Do some tests on cellData to determine the type and pick a DataTemplate
//Alternatively, you could build the actual content here in code-behind, but a DataTemplate would probably be cleaner
contentHost.ContentTemplate = (DataTemplate)SomeResourceDictionary["SomeResourceKey"];
return contentHost;
}
protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
{
return GenerateElement(cell, dataItem);
}
}

以上基于本文中的示例。使用此列类型,AutoGeneratingColumn处理程序如下所示:

private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
DataTableBoundColumn col = new DataTableBoundColumn();
e.Column = col;
e.Column.Header = "Whatever you want";
}

如果要自定义自动生成的列,则必须使用DataGrid.AutoGeneratingColumn事件。我知道您不希望列的实体相同,但您仍然需要使用此事件 - 您只需要以不同的方式使用它。

您考虑Styles 和DataTriggers 来动态更改单元格模板是正确的。这通常是通过在 XAML 中声明列来完成的,但你可以通过使用DataGrid.AutoGeneratingColumn并将列声明为资源来解决此问题。

拿这样的东西:

<DataGrid>
<DataGrid.Resources>
<DataGridTemplateColumn x:Key="TemplateColumn" x:Shared="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<!--Use DataTriggers to set the content to whatever you need-->
<DataTrigger>
<!--...-->
</DataTrigger>
<DataTrigger>
<!--...-->
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Resources>
</DataGrid>

以上是使用带有DataTriggers 的ContentControl动态设置ContentTemplateDataTemplate。我将其定义为带有x:KeyDataGrid的资源。x:Shared="False"意味着每当请求资源时,WPF 都会创建此列的新实例,而不是创建一个实例并提供对该单个实例的引用,以便用户可以"共享"它。这将允许您将列的多个实例添加到DataGrid中。

您的AutoGeneratingColumn将是这样的:

private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
//Add an if statement if you only want to replace some of the columns with the dynamic template one
var DG = (DataGrid)sender;
DataGridTemplateColumn col = (DataGridTemplateColumn)DG.Resources["TemplateColumn"];
e.Column = col;
e.Column.Header = "Whatever you want";
}

这会将您的列替换为TemplateColumn资源的坚持。

最新更新