C# 用于统一网格绑定的DataTemplateSelector?
从Ed昨天在我的问题()中所说的开始,我试图创建一个统一的网格,第一行和第一列填充为文本块,其余单元格填充为按钮。下面是一个例子 现在,我不完全确定如何绑定这一切。我几乎可以使用CompositeContainer实现它,但是我想使用来自ObjSource和ObjLabeller的数据绑定网格的行和列。。。但我不知道该怎么做 以下是我的虚拟机中的代码:C# 用于统一网格绑定的DataTemplateSelector?,c#,wpf,xaml,C#,Wpf,Xaml,从Ed昨天在我的问题()中所说的开始,我试图创建一个统一的网格,第一行和第一列填充为文本块,其余单元格填充为按钮。下面是一个例子 现在,我不完全确定如何绑定这一切。我几乎可以使用CompositeContainer实现它,但是我想使用来自ObjSource和ObjLabeller的数据绑定网格的行和列。。。但我不知道该怎么做 以下是我的虚拟机中的代码: private CompositeCollection objGridLabelCompositeCollection; pu
private CompositeCollection objGridLabelCompositeCollection;
public CompositeCollection ObjGridLabelCompositeCollection
{
get { return objGridLabelCompositeCollection; }
set { objGridLabelCompositeCollection= value;
OnPropertyChanged("ObjGridLabelCompositeCollection");
}
}
private ObservableCollection<GridLabeller> GridLabeller = new ObservableCollection<gridLabeller >();
public ObservableCollection<GridLabeller> GridLabeller
{
get { return gridLabeller; }
set
{
gridLabeller = value;
OnPropertyChanged(nameof(GridLabeller));
}
}
private ObservableCollection<ObjA> objSource = new ObservableCollection<ObjA>();
public ObservableCollection<ObjA> ObjSource
{
get { return objSource; }
set
{
objSource = value;
OnPropertyChanged(nameof(ObjSource));
}
}
private void Generate(object rowAndColumn)
{
// Cast our object to a tuple
Tuple<int, int> rowColumnForGrid = rowAndColumn as Tuple<int, int>;
// Set Row - add 1 for header
RowCount = rowColumnForGrid.Item1 + 1;
// Set Column - add 1 for header
ColumnCount = rowColumnForGrid.Item2 + 1;
ObjSource.Clear();
for (int iRow = 0; iRow < RowCount; ++iRow)
{
for (int iCol = 0; iCol < ColumnCount; ++iCol)
{
if (iRow == 0 && iCol == 0)
{
continue;
}
if (iRow == 0 || iCol == 0)
{
// Create label
GridLabeller label = new GridLabeller();
if (iRow == 0)
{
label.HeaderName = iCol.ToString();
}
if (iCol == 0)
{
label.HeaderName = GridHelpers.GetRowName(iRow);
}
label.Row = iRow;
label.Column = iCol;
GridLabeller.Add(label);
}
else
{
// Create ObjA
ObjA objee = new ObjA();
objee.Id = GridHelpers.GetRowName(iRow) + (iCol);
objee.Row = iRow;
objee.Column = iCol;
ObjSource.Add(objee);
}
}
}
ObjGridLabelCompositeCollection = new CompositeCollection();
ObjGridLabelCompositeCollection.Add(new CollectionContainer() { Collection = GridLabeller });
ObjGridLabelCompositeCollection.Add(new CollectionContainer() { Collection = ObjSource });
}
以及网格中的XAML
<ItemsControl ItemsSource="{Binding ObjGridLabelCompositeCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid DockPanel.Dock="Top" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Grid.Row="1"
Rows="{Binding RowCount}"
Columns="{Binding ColumnCount}"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding Row}"/>
<Setter Property="Grid.Column" Value="{Binding Column}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type engine:ObjA}">
<Button Content="{Binding Id}" />
</DataTemplate>
<DataTemplate DataType="{x:Type engine:GridLabeller}">
<TextBlock Text="{Binding HeaderName}"/>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
现在看来是这样的。。。第一张图片是我想要的
抱歉,没有足够的代表发布图像
不管怎样,如果有人有什么好主意,我将不胜感激!也许我需要一些复合收藏。。。我不知道…这个例子展示了如何实现一个动态的
数据网格
,其中每个单元格都是一个按钮
元素。您需要稍微修改它(数据类型)以满足您的需求
我建议使用已经有列和行标题的DataGrid
。使用DataTable
填充DataGrid
。DataTable
简化了动态列生成,并使数据模型没有冗余数据(例如列标题等)
要覆盖单元格布局,需要设置DataGrid.CellStyle
,在本例中,这将使单元格变成按钮。此修改还需要将类型为DataRowView
的列数据转换为实际显示数据(在本例中为CellDataModel
),然后将其绑定到按钮。Content
要显示行号,需要第二个转换器来生成DataGridRowHeader
的值。DataGridRowHeader
是通过设置DataGrid.RowHeaderStyle
来定义的
CellDataModel.cs
public class CellDataModel
{
public CellDataModel(object data)
{
this.Data = data;
}
public object Data { get; set; }
}
class DataRowViewToCellDataConverter : IMultiValueConverter
{
#region Implementation of IMultiValueConverter
/// <inheritdoc />
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[0] is DataRowView dataRowView
&& ((int) values[1]) is int cellIndex
&& dataRowView[cellIndex] is CellDataModel cellModel)
{
return cellModel.Data;
}
return Binding.DoNothing;
}
/// <inheritdoc />
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) =>
throw new NotSupportedException();
#endregion
}
class RowDataToRowNumberConverter : IMultiValueConverter
{
#region Implementation of IMultiValueConverter
/// <inheritdoc />
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[0] is DataGrid dataGrid && values[1] is object rowData)
{
return dataGrid.Items.IndexOf(rowData) + 1;
}
return Binding.DoNothing;
}
/// <inheritdoc />
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) =>
throw new NotSupportedException();
#endregion
}
ViewModel.cs
class ViewModel
{
public DataTable GridSource { get; set; }
public ViewModel()
{
// Create a data set with an arbitrary column and row count
this.GridSource = new DataTable();
// Create column headers with alphabetic letters from 'A' to 'G'.
// The decimal ASCII value is converted to a string.
for (var asciiCode = 65; asciiCode < 72; asciiCode++)
{
this.GridSource.Columns.Add(new DataColumn(new string((char) asciiCode, 1), typeof(CellDataModel)));
}
// Populate data table
int maxNumberOfRows = 5;
for (var rowNumber = 1; rowNumber <= maxNumberOfRows; rowNumber++)
{
DataRow newRow = this.CellTable.NewRow();
foreach (DataColumn tableColumn in this.CellTable.Columns)
{
newRow[tableColumn.ColumnName] = new CellDataModel($"Value: {rowNumber}{tableColumn.ColumnName}");
}
this.GridSource.Rows.Add(newRow);
}
}
}
类视图模型
{
公共数据表GridSource{get;set;}
公共视图模型()
{
//创建具有任意列数和行数的数据集
this.GridSource=新数据表();
//使用字母“A”到“G”创建列标题。
//十进制ASCII值将转换为字符串。
对于(变量ASCICODE=65;ASCICODE<72;ASCICODE++)
{
Add(新数据列(新字符串((char)ascicode,1),typeof(CellDataModel));
}
//填充数据表
int maxNumberOfRows=5;
对于(var rowNumber=1;rowNumber)
抛出新的NotSupportedException();
#端区
}
RowDataToRowNumberConverter.cs
public class CellDataModel
{
public CellDataModel(object data)
{
this.Data = data;
}
public object Data { get; set; }
}
class DataRowViewToCellDataConverter : IMultiValueConverter
{
#region Implementation of IMultiValueConverter
/// <inheritdoc />
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[0] is DataRowView dataRowView
&& ((int) values[1]) is int cellIndex
&& dataRowView[cellIndex] is CellDataModel cellModel)
{
return cellModel.Data;
}
return Binding.DoNothing;
}
/// <inheritdoc />
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) =>
throw new NotSupportedException();
#endregion
}
class RowDataToRowNumberConverter : IMultiValueConverter
{
#region Implementation of IMultiValueConverter
/// <inheritdoc />
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[0] is DataGrid dataGrid && values[1] is object rowData)
{
return dataGrid.Items.IndexOf(rowData) + 1;
}
return Binding.DoNothing;
}
/// <inheritdoc />
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) =>
throw new NotSupportedException();
#endregion
}
class RowDataToRownNumber转换器:IMultiValueConverter
{
#IMultiValueConverter的区域实现
///
公共对象转换(对象[]值,类型targetType,对象参数,CultureInfo区域性)
{
如果(值[0]是DataGrid DataGrid&&values[1]是对象行数据)
{
返回dataGrid.Items.IndexOf(rowData)+1;
}
不做任何事;
}
///
公共对象[]转换回(对象值、类型[]目标类型、对象参数、文化信息文化)=>
抛出新的NotSupportedException();
#端区
}
main window.xaml
<Window>
<Window.DataContext>
<ViewModel />
</Window.DataContext>
<DataGrid ItemsSource="{Binding GridSource}"
AutoGeneratingColumn="DataGrid_OnAutoGeneratingColumn"
IsReadOnly="True">
<DataGrid.Resources>
<DataRowViewToCellDataConverter x:Key="DataRowViewToCellDataConverter" />
<RowDataToRowNumberConverter x:Key="RowDataToRowNumberConverter" />
</DataGrid.Resources>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridCell">
<Button>
<Button.Content>
<MultiBinding Converter="{StaticResource DataRowViewToCellDataConverter }">
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="DataContext" />
<Binding RelativeSource="{RelativeSource TemplatedParent}"
Path="TabIndex" />
</MultiBinding>
</Button.Content>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.CellStyle>
<DataGrid.RowHeaderStyle>
<Style TargetType="{x:Type DataGridRowHeader}">
<Setter Property="Content">
<Setter.Value>
<MultiBinding Converter="{StaticResource RowDataToRowNumberConverter}">
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType=DataGrid}" />
<Binding />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</DataGrid.RowHeaderStyle>
</DataGrid>
</Window>
此示例生成以下输出:
您可以添加更多细节吗?您的目标是什么?按钮应该做什么?它们是否像第一张图中一样是空的?嗨,BionicCode。我们不需要太担心按钮的功能(它们可以选择、拖动等)。但我有代码来做这件事(将所有代码都移植到MVVM中的UserControl更好)。我已经让它工作了一半,但是我不能让ItemContainerStyle为网格行和列工作。ObjSource和ObjLabeller都有一个行和列变量。让我更新我的答案,以便您可以看到它是什么样子。列计数是常量吗?不,它是动态的。让我发布代码。行计数和列计数都由dy绑定纳米奇value@BionicCode请参阅更新-谢谢。所有内容都显示得很好,但显然没有考虑ObjSource和GridLabeller的行和列,因为它们都有一个行/列变量,所以不知道绑定到哪个行/列变量