我正在构建一个显示比赛中单圈时间的应用程序。 我有一堂课,其中包含与会人员的嵌套列表,其中包含了他们的闲暇时间。
public class ParticipantClass
{
public int ID {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
public List<TimeSpan> LapTimes {get; set;}
}
现在,我想在DataGridView中显示此参与者列表。据我所知,不可能在DataGridView中直接显示带有嵌套列表的列表。我想我需要先将列表转换为另一个“ DisplayClass”,然后再将其绑定到DataGridView。问题是,我不知道会有多少圈时间。一个参与者可能做10圈,但是另一参与者可能做20圈。因此,理想情况下,此“ DisplayClass”可以具有动态的运动时间。有什么办法可以做到这一点?
另一种方法是为displayclass设置一个固定的大小,例如容纳100个LapTime,但这似乎浪费资源(尽管用户甚至不会注意到它),但我只是想知道是否还有更好的方法。
[编辑] 所需的DataGridView输出将是这样的:
1 | John | Doe | 20.02 | 21.54 | 19.41 | 19.66 | 19.12
2 | Jane | Doe | 15.41 | 15.36 | 15.02 | 15.88 | 14.44 | 14.80 | 13.95 | 14.54
3 | ...
答案 0 :(得分:0)
您可以使用这种方法
private DataTable ConvertListToDataTable(List<ParticipantClass> participants){
DataTable table = new DataTable();
table.Columns.Add("ID");
table.Columns.Add("FirstName");
table.Columns.Add("LastName");
var maxTimeLaps = 0;
foreach (var p in participants) {
// Create the row's data.
List<Object> array = new List<Object> { p.ID, p.FirstName, p.LastName };
array.AddRange(p.LapTimes.Cast<Object>());
// Add additional columns for larger sets of TimeLaps.
if (maxTimeLaps < p.LapTimes.Count) {
for (int i = maxTimeLaps; i < p.LapTimes.Count; i++) {
table.Columns.Add(i.ToString());
}
maxTimeLaps = p.LapTimes.Count;
}
// Add new row to table.
table.Rows.Add(array.ToArray());
}
return table;
}
用于在不更改数据模型的情况下生成数据的平面视图。
答案 1 :(得分:0)
您可以使用多种方法来“平整”数据。具体来说,在发布的示例中,您需要知道DataTable.
中需要多少列,如果遇到Participant
的循环时间比任何时间都多的Participant
,则需要向表中添加新列前public class Participant {
private static int count = 1;
public int ID { get; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<TimeSpan> LapTimes { get; set; }
public Participant(string firstName, string lastName) {
FirstName = firstName;
LastName = lastName;
ID = count++;
}
}
个中的一个。您可以在列表中“一个”循环中完成此操作,如Kinimod的答案所示。
我使用的方法将遍历列表两次。一次获取所需的列数,然后再次从列表中添加行。因为我们会“知道”添加数据时不必添加额外的列,所以这将使添加行变得更加容易和复杂。
首先,我使用了一个与发布的类类似的类,但对ID进行了一些小的更改,并添加了一个简单的构造函数。可能看起来像...
Participants.
接下来,我创建了另一个名为List<Participant>
的类,该类将保存List<Participant>
对象。您可以轻松地创建一个全局Participant
并使用它,但是,我相信这是一种更好的方法,因为您可能希望在将其添加到列表之前先检查列表中重复的int
之类的东西,或在这种情况下,将成为放置我们所需的两种方法的便捷位置。 1)一种方法,该方法返回一个Paticipant
数字,该数字表示List<Parcipitant>
中所有DataTable
中“最大”膝部列表的计数; 2)一种从“展平”列表中返回public class Participants {
public List<Participant> All_Participants { get; set; }
public Participants() {
All_Participants = new List<Participant>();
}
private int TotalColumns {
get {
int tot = 0;
foreach (Participant participant in All_Participants) {
if (participant.LapTimes.Count > tot)
tot = participant.LapTimes.Count;
}
return tot;
}
}
public DataTable GetParticipantDataTable() {
DataTable dt = new DataTable();
dt.Columns.Add("ID", typeof(string));
dt.Columns.Add("FirstName", typeof(string));
dt.Columns.Add("LastName", typeof(string));
int totColumns = this.TotalColumns;
for (int i = 1; i <= totColumns; i++) {
dt.Columns.Add("Lap_" + i, typeof(TimeSpan));
}
foreach (Participant participant in All_Participants) {
DataRow dr = dt.NewRow();
dr["ID"] = participant.ID;
dr["FirstName"] = participant.FirstName;
dr["LastName"] = participant.LastName;
int curLapCol = 1;
foreach (TimeSpan span in participant.LapTimes) {
dr["Lap_" + curLapCol] = span;
curLapCol++;
}
dt.Rows.Add(dr);
}
return dt;
}
}
的方法。此方法将使用第一种方法来获取所需的列数。同样,您可以在此类中添加其他“特定”方法来添加,删除,查找或排序列表。此示例的裸露骨头可能如下所示。
Participants All_Participants;
Random rand = new Random();
DataTable GridTable;
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
All_Participants = GetParticipantsList();
GridTable = All_Participants.GetParticipantDataTable();
dataGridView1.DataSource = GridTable;
}
private Participants GetParticipantsList() {
Participants allPar = new Participants();
Participant curPar = new Participant("John", "Doe");
curPar.LapTimes = GetRandomLapTimes();
allPar.All_Participants.Add(curPar);
curPar = new Participant("Jane", "Doe");
curPar.LapTimes = GetRandomLapTimes();
allPar.All_Participants.Add(curPar);
curPar = new Participant("Speedy", "Gonzalez");
curPar.LapTimes = GetRandomLapTimes();
allPar.All_Participants.Add(curPar);
curPar = new Participant("Flash", "Gordon");
curPar.LapTimes = GetRandomLapTimes();
allPar.All_Participants.Add(curPar);
curPar = new Participant("Usain", "Bolt");
curPar.LapTimes = GetRandomLapTimes();
allPar.All_Participants.Add(curPar);
return allPar;
}
private List<TimeSpan> GetRandomLapTimes() {
List<TimeSpan> times = new List<TimeSpan>();
int numberOfLapTimes = rand.Next(1, 21);
for (int i = 0; i < numberOfLapTimes; i++) {
times.Add(new TimeSpan(0, rand.Next(31), rand.Next(60)));
}
return times;
}
这几乎是您所需要的。下面是使用上述类的示例。注意:获取样本数据时,将使用一个随机数来定义为每个参与者生成的最大圈数,该最小圈数可以是最小1圈,最大20圈。因此,每次运行代码时,您应该期望得到不同的结果。
DataGridView
上面的代码将以hh:mm:ss格式在CellFormatting
中显示单圈时间。如果只想显示“ mm:ss”,则可以连接网格TimeSpan
事件以格式化圈速时间列,如图所示。下面的代码应将“单圈时间”设置为“ mm:ss”格式。
尚不清楚您是否希望用户能够“编辑”圈速。如果确实允许用户编辑单圈时间…,应注意,即使单圈时间显示为“ mm:ss”,如果您更改单圈时间,您仍必须输入“ hh”值,因为该列是DataError
类型。例如,如果您在单圈时间单元格中输入“ 12:14”,然后按Enter键,显示屏将显示“ 14:00”。您必须输入时间的所有三个部分才能使其正确显示……即“ 00: 12:14。”
此外,如果您允许用户编辑圈速时间单元,则谨慎地连接网格TimeSpan
事件以处理用户尝试输入24小时或60分钟的情况或60秒。每个值对于DataError
对象都是无效的,并且将引发CellFormatting
异常。网格private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) {
if (e.ColumnIndex >= 0 && e.RowIndex >= 0 && !dataGridView1.Rows[e.RowIndex].IsNewRow) {
string curColName = dataGridView1.Columns[e.ColumnIndex].Name;
if (curColName.Length >= 4) {
if (dataGridView1.Columns[e.ColumnIndex].Name.Substring(0, 4).Equals("Lap_")) {
if (!String.IsNullOrEmpty(e.Value.ToString())) {
e.Value = ((TimeSpan)e.Value).ToString(@"mm\:ss");
}
}
}
}
}
事件可能如下所示。
{{1}}
希望这会有所帮助。