我有一个自定义数据结构,我需要一个代表我的数据结构的WPF组件。组件应该看起来像在图片上。组件应该是动态的,因此结构中的ColumnSets数量可以是1到x。并且每个columnSet可以具有不同的列数。
public class CustomStructure{
public List<ColumnSet> ColumnSets{get; set;}
}
public class ColumnSet{
public string Name { get; set; }
public List<Column> ColumnSets{get;}
}
public class Column{
public string Name { get; set; }
public List<int> Data{get;}
}
第一行表示ColumnSet类的Name属性。第二行是Column类的Name属性。其他行是来自Column类的数据。
Wanted WPF component design (image)
我的解决方案
我定义了两个组件。首先表示外表,它被称为FuzzyTableControl。它有X列,只有一行。 第二个表示FuzzyTableControl第一行中每列的FuzzyInnerTableControl。
FuzzyTableControl.xaml
<UserControl:Class="FuzzyTableControl">
<UserControl.DataContext>
<viewModel:FuzzyTableViewModel/>
</UserControl.DataContext>
<DataGrid AutoGenerateColumns="True" IsReadOnly="True"
CanUserAddRows="False"
CanUserDeleteRows="False"
ItemsSource="{Binding DataTable}">
// here is problem 1
<DataGrid.Columns>
<DataGridTemplateColumn Header="{Binding}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<view:FuzzyInnerTableControl/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</UserControl>
FuzzyTableViewModel.cs
public class FuzzyTableViewModel : BaseViewModel
{
public FuzzyTable Table { get; set; }
public DataTable DataTable { get; set; }
public FuzzyTableViewModel()
{
Table = FuzzyTable.Generate(3, 2);
DataTable = new DataTable();
foreach (var attribute in Table.Attributes)
{
DataTable.Columns.Add(new DataColumn(attribute.Name));
}
var row = new List<object>();
foreach (var attribute in Table.Attributes)
row.Add(attribute);
DataTable.Rows.Add(row.ToArray());
}
}
FuzzyInnerTableControl.xaml 这个很好用
<UserControl:Class="FuzzyInnerTableControl">
<UserControl.DataContext>
<viewModel:FuzzyInnerTableViewModel/>
</UserControl.DataContext>
<DataGrid ItemsSource="{Binding DataTable}"/>
</UserControl>
FuzzyInnerTableViewModel.cs
public class FuzzyInnerTableViewModel : BaseViewModel
{
public DataTable DataTable { get; }
public ColumnSetDouble ColumnSetDouble { get; set; }
public FuzzyInnerTableViewModel()
{
// test
var table = FuzzyTable.Generate(3, 2);
ColumnSetDouble = table.ClassAttribute;
//end test
DataTable = new DataTable();
foreach (var attribute in ColumnSetDouble.Columns)
DataTable.Columns.Add(new DataColumn(attribute.Name));
for (int rowId = 0; rowId < ColumnSetDouble.Columns[0].Data.Count; rowId++)
{
var row = new List<object>();
foreach (var column in ColumnSetDouble.Columns)
{
row.Add(column.Data[rowId]);
}
DataTable.Rows.Add(row.ToArray());
}
}
}
我不知道如何为FuzzyTableControl的每一列定义单元格模板。此解决方案创建NEW列,但我需要从viewModel动态加载列。
答案 0 :(得分:0)
要为所有自动生成的单元格定义模板,您可以尝试使用DataGrid.CellStyle
。
<DataGrid AutoGenerateColumns="True" IsReadOnly="True"
CanUserAddRows="False"
CanUserDeleteRows="False"
ItemsSource="{Binding DataTable}">
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<view:FuzzyInnerTableControl/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGrid.CellStyle>
</DataGrid>
如果我有更好的主意,我会编辑。
现在你问为什么<view:FuzzyInnerTableControl DataContext="{Binding}"/>
无效。所以我想你已经准备好了你的外部DataTable,所以它真的包含FuzzyInnerTableViewModel
并不是完全无关紧要的:
// when creating a column, ensure to set the DataType, so your content won't be reduced to some text
DataTable.Columns.Add(new DataColumn(attribute.Name, typeof(FuzzyInnerTableViewModel)));
// I have no idea what attribute is, but it better be a FuzzyInnerTableViewModel for your use case!
row.Add(attribute);
首先,DataGrid
会尝试自动生成相对无用的DataGridTextColumn
,需要进行更改。处理AutoGeneratingColumn
事件以更改列类型。 DataGridTemplateColumn
实际上不会为您提供单元格内容和,如果您(像我一样)尝试通过DataGridCell.Content
设置CellStyle
,它会好友不理你并在本地设置内容。因此,暂时将单元格值推送到DataGridCell.Tag
。
以下FuzzyTableControl
DataGrid_AutoGeneratingColumn
方法与xaml中的DataGrid.AutoGeneratingColumn
事件相关联。
public partial class FuzzyTableControl : UserControl
{
public FuzzyTableControl()
{
InitializeComponent();
}
private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
e.Column = new DataGridTemplateColumn
{
CellStyle = new Style(typeof(DataGridCell))
{
Setters =
{
new Setter(DataGridCell.TagProperty, new Binding(e.PropertyName)),
}
},
Header = e.Column.Header,
HeaderStringFormat = e.Column.HeaderStringFormat,
HeaderStyle = e.Column.HeaderStyle,
HeaderTemplate = e.Column.HeaderTemplate,
HeaderTemplateSelector = e.Column.HeaderTemplateSelector,
// transfer whatever properties you feel worthy in your scenario
};
}
}
现在,至少所需的数据悬在Tag
的某个地方,但猜猜是什么 - DataGrid.CellStyle
刚被一种风格所取代,主要关注的是动态绑定某些风格数据。因此,让我们将DataTemplate
移动到资源中,然后将绑定添加到某个父DataGridCell.Tag
,同时又热... {/ p>
<UserControl.Resources>
<DataTemplate x:Key="FuzzyInnerTableTemplate">
<view:FuzzyInnerTableControl
DataContext="{Binding Tag,RelativeSource={RelativeSource AncestorType=DataGridCell}}"/>
</DataTemplate>
</UserControl.Resources>
并将模板与DataGridTemplateColumn
// ...
// transfer whatever properties you feel worthy in your scenario
CellTemplate = Resources["FuzzyInnerTableTemplate"] as DataTemplate
从UserControl.DataContext
移除FuzzyInnerTableControl
,否则绑定将失败并且您仍然会获得默认值。如果您需要组合默认值和外部提供的值,请查找其他方法。
困惑?好吧,我也是,所以这里是我的测试代码的摘要,包括一些组成的行和列,因为我没有你的整个Column.Data
和Table.Attributes
的东西可用的:
用于FuzzyTable的XAML(Control,ViewModel)
<UserControl x:Class="WpfApplication2.FuzzyTableControl"
...
>
<UserControl.DataContext>
<viewModel:FuzzyTableViewModel/>
</UserControl.DataContext>
<UserControl.Resources>
<DataTemplate x:Key="FuzzyInnerTableTemplate">
<view:FuzzyInnerTableControl
DataContext="{Binding Tag,RelativeSource={RelativeSource AncestorType=DataGridCell}}"/>
</DataTemplate>
</UserControl.Resources>
<DataGrid AutoGenerateColumns="True" IsReadOnly="True"
CanUserAddRows="False"
CanUserDeleteRows="False"
ItemsSource="{Binding DataTable}"
AutoGeneratingColumn="DataGrid_AutoGeneratingColumn">
</DataGrid>
</UserControl>
FuzzyTable的代码
public partial class FuzzyTableControl : UserControl
{
public FuzzyTableControl()
{
InitializeComponent();
}
private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
e.Column = new DataGridTemplateColumn
{
CellStyle = new Style(typeof(DataGridCell))
{
Setters =
{
new Setter(DataGridCell.TagProperty, new Binding(e.PropertyName)),
}
},
Header = e.Column.Header,
HeaderStringFormat = e.Column.HeaderStringFormat,
HeaderStyle = e.Column.HeaderStyle,
HeaderTemplate = e.Column.HeaderTemplate,
HeaderTemplateSelector = e.Column.HeaderTemplateSelector,
// transfer whatever properties you feel worthy in your scenario
CellTemplate = Resources["FuzzyInnerTableTemplate"] as DataTemplate
};
}
}
public class FuzzyTableViewModel : BaseViewModel
{
public DataTable DataTable { get; set; }
public FuzzyTableViewModel()
{
DataTable = new DataTable();
DataTable.Columns.Add(new DataColumn("O", typeof(FuzzyInnerTableViewModel)));
DataTable.Columns.Add(new DataColumn("P", typeof(FuzzyInnerTableViewModel)));
var c1 = new FuzzyInnerTableViewModel();
var c2 = new FuzzyInnerTableViewModel();
c1.DataTable.Rows[1][0] = "Replace";
var row = new List<object>();
row.Add(c1);
row.Add(c2);
DataTable.Rows.Add(row.ToArray());
}
}
FuzzyInnerTable的XAML
<UserControl x:Class="WpfApplication2.FuzzyInnerTableControl"
...
Loaded="UserControl_Loaded">
<UserControl.Resources>
<viewModel:FuzzyInnerTableViewModel x:Key="defaultVM"/>
</UserControl.Resources>
<DataGrid ItemsSource="{Binding DataTable}"/>
</UserControl>
FuzzyInnerTable的代码
public partial class FuzzyInnerTableControl : UserControl
{
public FuzzyInnerTableControl()
{
InitializeComponent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
if (DataContext == null)
{
DataContext = Resources["defaultVM"];
}
}
}
public class FuzzyInnerTableViewModel : BaseViewModel
{
public DataTable DataTable { get; private set; }
public FuzzyInnerTableViewModel()
{
DataTable = new DataTable();
DataTable.Columns.Add(new DataColumn("A"));
DataTable.Columns.Add(new DataColumn("B"));
for (int rowId = 0; rowId < 3; rowId++)
{
var row = new List<object>();
row.Add(rowId);
row.Add(2 * rowId);
DataTable.Rows.Add(row.ToArray());
}
}
}