我正在尝试根据保存单元格类型的List<List<int>>
更改DataGrid
内单元格的模板(即 1=布尔值、2=整数、3=字符串、4=自定义等(。自定义类型(类型,因为它们可以大于 1(必须由ComboBox
表示。对于数字和字符串,我需要正常的TextBox
,对于布尔值,我需要CheckBox
。DataGrid
绑定到一个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
中写入,但我不能,因为AutoGenerateColumns
True
。还可以更改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
事件。我知道您不希望列的实体相同,但您仍然需要使用此事件 - 您只需要以不同的方式使用它。
您考虑Style
s 和DataTrigger
s 来动态更改单元格模板是正确的。这通常是通过在 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>
以上是使用带有DataTrigger
s 的ContentControl
动态设置ContentTemplate
的DataTemplate
。我将其定义为带有x:Key
的DataGrid
的资源。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
资源的坚持。