带有嵌套列表的列表作为DataGridView中的数据源

时间:2019-08-22 12:17:13

标签: c# datagridview

我正在构建一个显示比赛中单圈时间的应用程序。 我有一堂课,其中包含与会人员的嵌套列表,其中包含了他们的闲暇时间。

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 | ...

2 个答案:

答案 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}}

希望这会有所帮助。